From 672f3d40be83d9e380fd7be4b674d5e8d5fa36de Mon Sep 17 00:00:00 2001 From: HuabingZhao Date: Tue, 25 Jul 2017 15:18:33 +0800 Subject: Divide the MSB source codes into two repos Change-Id: Ie76d545b214a8ce5191f215350a623e1529983d9 Issue-id: MSB-5 Signed-off-by: HuabingZhao --- .gitattributes | 1 + .gitignore | 10 + .gitreview | 4 - License.txt | 473 -- README.md | 19 +- .../apiroute-service/dependency-reduced-pom.xml | 121 + apiroute/apiroute-service/pom.xml | 174 + .../java/org/onap/msb/apiroute/ApiRouteApp.java | 131 + .../org/onap/msb/apiroute/ApiRouteAppConfig.java | 90 + .../org/onap/msb/apiroute/SyncDataManager.java | 135 + .../org/onap/msb/apiroute/api/ApiRouteInfo.java | 107 + .../msb/apiroute/api/CustomDateSerializer.java | 39 + .../org/onap/msb/apiroute/api/CustomRouteInfo.java | 24 + .../org/onap/msb/apiroute/api/DiscoverInfo.java | 55 + .../org/onap/msb/apiroute/api/IuiRouteInfo.java | 25 + .../msb/apiroute/api/MicroServiceFullInfo.java | 195 + .../main/java/org/onap/msb/apiroute/api/Node.java | 98 + .../onap/msb/apiroute/api/PublishFullAddress.java | 48 + .../java/org/onap/msb/apiroute/api/RouteInfo.java | 179 + .../org/onap/msb/apiroute/api/RouteServer.java | 83 + .../ExtendedInternalServerErrorException.java | 28 + .../api/exception/ExtendedNotFoundException.java | 28 + .../exception/UnprocessableEntityException.java | 18 + .../msb/apiroute/health/ApiRouteHealthCheck.java | 65 + .../msb/apiroute/health/ConsulLinkHealthCheck.java | 132 + .../msb/apiroute/health/OpenRestyHealthCheck.java | 39 + .../onap/msb/apiroute/health/RedisHealthCheck.java | 160 + .../msb/apiroute/resources/ApiRouteResource.java | 214 + .../apiroute/resources/CustomRouteResource.java | 164 + .../msb/apiroute/resources/IuiRouteResource.java | 163 + .../apiroute/resources/MicroServiceResource.java | 229 + .../apiroute/wrapper/ApiRouteServiceWrapper.java | 273 + .../wrapper/CustomRouteServiceWrapper.java | 217 + .../apiroute/wrapper/InitRouteServiceWrapper.java | 416 ++ .../apiroute/wrapper/IuiRouteServiceWrapper.java | 221 + .../msb/apiroute/wrapper/MicroServiceWrapper.java | 398 ++ .../wrapper/consulextend/CatalogClient.java | 87 + .../msb/apiroute/wrapper/consulextend/Consul.java | 97 + .../wrapper/consulextend/HealthClient.java | 112 + .../consulextend/async/ConsulResponseCallback.java | 33 + .../consulextend/async/ConsulResponseHeader.java | 27 + .../consulextend/async/OriginalConsulResponse.java | 27 + .../wrapper/consulextend/cache/ConsulCache.java | 301 + .../consulextend/cache/ServiceHealthCache.java | 65 + .../consulextend/cache/ServicesCatalogCache.java | 39 + ...heckServiceDataEmptyAndAutoStopWatchFilter.java | 98 + .../expose/CheckTagAndAutoStopWatchFilter.java | 96 + .../consulextend/expose/ConsulIndexFilter.java | 48 + .../expose/ServiceModifyIndexFilter.java | 121 + .../expose/WatchCatalogServicesTask.java | 90 + .../expose/WatchServiceHealthTask.java | 128 + .../wrapper/consulextend/expose/WatchTask.java | 87 + .../consulextend/expose/WriteBufferHandler.java | 36 + .../wrapper/consulextend/model/health/Service.java | 39 + .../consulextend/model/health/ServiceHealth.java | 30 + .../apiroute/wrapper/consulextend/util/Http.java | 281 + .../msb/apiroute/wrapper/dao/DAOConstants.java | 6 + .../onap/msb/apiroute/wrapper/dao/DAOFactory.java | 18 + .../apiroute/wrapper/dao/RedisAccessWrapper.java | 133 + .../msb/apiroute/wrapper/dao/route/IRouteDAO.java | 18 + .../apiroute/wrapper/dao/route/RouteDAOImpl.java | 63 + .../apiroute/wrapper/dao/route/bean/Metadata.java | 48 + .../msb/apiroute/wrapper/dao/route/bean/Node.java | 32 + .../apiroute/wrapper/dao/route/bean/RouteInfo.java | 79 + .../msb/apiroute/wrapper/dao/route/bean/Spec.java | 52 + .../apiroute/wrapper/dao/service/IServiceDAO.java | 17 + .../wrapper/dao/service/ServiceDAOImpl.java | 59 + .../wrapper/dao/service/bean/Metadata.java | 50 + .../apiroute/wrapper/dao/service/bean/Node.java | 32 + .../wrapper/dao/service/bean/ServiceInfo.java | 76 + .../apiroute/wrapper/dao/service/bean/Spec.java | 44 + .../onap/msb/apiroute/wrapper/queue/BaseQueue.java | 36 + .../msb/apiroute/wrapper/queue/QueueManager.java | 65 + .../apiroute/wrapper/queue/ServiceConsumer.java | 167 + .../msb/apiroute/wrapper/queue/ServiceData.java | 53 + .../apiroute/wrapper/queue/ServiceListCache.java | 35 + .../wrapper/queue/ServiceListConsumer.java | 209 + .../apiroute/wrapper/queue/ServiceListQueue.java | 59 + .../msb/apiroute/wrapper/queue/ServiceQueue.java | 45 + .../apiroute/wrapper/service/ApiRouteService.java | 159 + .../wrapper/service/CustomRouteService.java | 152 + .../apiroute/wrapper/service/IuiRouteService.java | 151 + .../wrapper/service/MicroServiceFullService.java | 220 + .../IMicroServiceChangeListener.java | 37 + .../MicroServiceChangeListener.java | 779 +++ .../wrapper/serviceListener/RouteNotify.java | 77 + .../onap/msb/apiroute/wrapper/util/CommonUtil.java | 76 + .../onap/msb/apiroute/wrapper/util/ConfigUtil.java | 446 ++ .../onap/msb/apiroute/wrapper/util/FileUtil.java | 78 + .../msb/apiroute/wrapper/util/HttpClientUtil.java | 117 + .../msb/apiroute/wrapper/util/HttpGetResult.java | 19 + .../onap/msb/apiroute/wrapper/util/Jackson.java | 28 + .../msb/apiroute/wrapper/util/JacksonJsonUtil.java | 110 + .../onap/msb/apiroute/wrapper/util/JedisUtil.java | 208 + .../apiroute/wrapper/util/MicroServiceUtil.java | 78 + .../msb/apiroute/wrapper/util/RegExpTestUtil.java | 115 + .../onap/msb/apiroute/wrapper/util/RouteUtil.java | 345 ++ .../msb/apiroute/wrapper/util/ServiceFilter.java | 515 ++ .../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 | 120 + .../iframeResizer.contentWindow.min.js | 10 + .../api-doc/js/iframeResizer/iframeResizer.min.js | 9 + .../src/main/resources/api-doc/js/tools.js | 1054 ++++ .../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-route/css/animate.min.css | 6 + .../src/main/resources/iui-route/css/base.css | 54 + .../src/main/resources/iui-route/css/chart.css | 16 + .../src/main/resources/iui-route/css/newRoute.css | 340 ++ .../src/main/resources/iui-route/css/route.css | 641 +++ .../src/main/resources/iui-route/default.html | 177 + .../iui-route/i18n/loadi18nApp_iui-route_view.js | 43 + .../i18n/msb-iui-route-i18n-en-US.properties | 166 + .../i18n/msb-iui-route-i18n-zh-CN.properties | 160 + .../resources/iui-route/img/checkbox-checked.png | Bin 0 -> 3053 bytes .../main/resources/iui-route/img/checkbox-init.png | Bin 0 -> 2833 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 -> 4762 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 | 1028 ++++ .../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/echarts/echarts.common.min.js | 29 + .../resources/iui-route/js/echarts/macarons.js | 540 ++ .../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 | 1945 +++++++ .../src/main/resources/iui-route/js/routeFunc.js | 645 +++ .../src/main/resources/iui-route/js/routeUtil.js | 359 ++ .../src/main/resources/iui-route/js/statusUtil.js | 428 ++ .../org/onap/msb/apiroute/SyncDataManagerTest.java | 56 + .../apiroute/health/ApiRouteHealthCheckTest.java | 69 + .../apiroute/health/ConsulLinkHealthCheckTest.java | 103 + .../apiroute/health/OpenRestyHealthCheckTest.java | 77 + .../msb/apiroute/health/RedisHealthCheckTest.java | 77 + .../wrapper/ApiRouteServiceWrapperTest.java | 252 + .../wrapper/CustomRouteServiceWrapperTest.java | 208 + .../wrapper/InitRouteServiceWrapperTest.java | 241 + .../wrapper/IuiRouteServiceWrapperTest.java | 212 + .../apiroute/wrapper/MicroServiceWrapperTest.java | 294 + .../apiroute/wrapper/consulextend/ConsulTest.java | 149 + .../consulextend/cache/ServiceHealthCacheTest.java | 24 + .../cache/ServicesCatalogCacheTest.java | 15 + ...ServiceDataEmptyAndAutoStopWatchFilterTest.java | 53 + .../expose/CheckTagAndAutoStopWatchFilterTest.java | 70 + .../consulextend/expose/ConsulIndexFilterTest.java | 42 + .../expose/ServiceModifyIndexFilterTest.java | 114 + .../expose/WatchCatalogServicesTaskTest.java | 138 + .../expose/WatchServiceHealthTaskTest.java | 156 + .../expose/WriteBufferHandlerTest.java | 46 + .../model/health/ServiceHealthTest.java | 54 + .../consulextend/model/health/ServiceTest.java | 32 + .../wrapper/consulextend/util/HttpTest.java | 80 + .../apiroute/wrapper/queue/QueueManagerTest.java | 178 + .../wrapper/service/ApiRouteServiceTest.java | 251 + .../wrapper/service/CustomRouteServiceTest.java | 241 + .../wrapper/service/IuiRouteServiceTest.java | 221 + .../service/MicroServiceFullServiceTest.java | 413 ++ .../MicroServiceChangeListenerTest.java | 666 +++ .../msb/apiroute/wrapper/util/CommonUtilTest.java | 61 + .../msb/apiroute/wrapper/util/ConfigUtilTest.java | 214 + .../apiroute/wrapper/util/HttpClientUtilTest.java | 29 + .../apiroute/wrapper/util/JacksonJsonUtilTest.java | 94 + .../msb/apiroute/wrapper/util/JedisUtilTest.java | 20 + .../wrapper/util/MicroServiceUtilTest.java | 45 + .../apiroute/wrapper/util/RegExpTestUtilTest.java | 96 + .../msb/apiroute/wrapper/util/RouteUtilTest.java | 373 ++ .../apiroute/wrapper/util/ServiceFilterTest.java | 210 + .../initApiGatewayConfig/initApiGatewayConfig.json | 3 + .../initRouteLabels/initRouteLabelsMatches.json | 12 + .../initRouteLabelsMatches.json.sample | 12 + .../test/resources/ext/initRouteLabels/readme.txt | 17 + .../resources/ext/initRouteWay/initRouteWay.json | 3 + .../src/test/resources/ext/initRouteWay/readme.txt | 5 + .../src/test/resources/ext/initServices/msb.json | 44 + .../src/test/resources/ext/initServices/readme.txt | 63 + .../resources/ext/initSwaggerJson/api-doc1.json | 1 + .../resources/ext/initSwaggerJson/api-doc2.json | 1 + .../ext/initUrlRootPath/initUrlRootPath.json | 4 + .../test/resources/ext/redisConf/redis.properties | 18 + .../wrapper/consulextend/util/serviceslist.json | 1 + apiroute/apiroute-standalone/pom.xml | 125 + .../assembly/resources/apiroute/conf/apiroute.yml | 47 + .../initApiGatewayConfig/initApiGatewayConfig.json | 3 + .../initRouteLabels/initRouteLabelsMatches.json | 7 + .../initRouteLabelsMatches.json.sample | 12 + .../apiroute/ext/initRouteLabels/readme.txt | 17 + .../apiroute/ext/initRouteWay/initRouteWay.json | 3 + .../resources/apiroute/ext/initRouteWay/readme.txt | 5 + .../resources/apiroute/ext/initServices/msb.json | 31 + .../resources/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 + .../apiroute/ext/redisConf/redis.properties | 18 + .../src/assembly/resources/apiroute/run.sh | 70 + .../src/assembly/resources/apiroute/setenv.sh | 10 + .../src/assembly/resources/apiroute/stop.sh | 59 + apiroute/pom.xml | 309 ++ build4docker.sh | 55 + ci/build_docker_image.sh | 36 + distributions/msb-apigateway/pom.xml | 191 + .../src/assembly/resources/shutdown.sh | 41 + .../src/assembly/resources/startup.sh | 44 + .../src/assembly/resources/startup4docker.sh | 36 + .../msb-apigateway/src/main/docker/Dockerfile | 6 + distributions/pom.xml | 27 + msb-core/apiroute/apiroute-service/pom.xml | 165 - .../src/main/java/org/openo/msb/ApiRouteApp.java | 549 -- .../main/java/org/openo/msb/ApiRouteAppConfig.java | 126 - .../main/java/org/openo/msb/ConsulClientApp.java | 451 -- .../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 | 50 - .../org/openo/msb/resources/ApiRouteResource.java | 233 - .../openo/msb/resources/CustomRouteResource.java | 152 - .../org/openo/msb/resources/IuiRouteResource.java | 154 - .../org/openo/msb/resources/MetricsResource.java | 48 - .../openo/msb/resources/MicroServiceResource.java | 246 - .../openo/msb/resources/ServiceAccessResource.java | 64 - .../openo/msb/wrapper/ApiRouteServiceWrapper.java | 565 -- .../msb/wrapper/CustomRouteServiceWrapper.java | 415 -- .../openo/msb/wrapper/IuiRouteServiceWrapper.java | 392 -- .../openo/msb/wrapper/MetricsServiceWrapper.java | 91 - .../org/openo/msb/wrapper/MicroServiceWrapper.java | 515 -- .../openo/msb/wrapper/ServiceAccessWrapper.java | 171 - .../openo/msb/wrapper/consul/CatalogClient.java | 300 - .../java/org/openo/msb/wrapper/consul/Consul.java | 314 -- .../openo/msb/wrapper/consul/ConsulException.java | 57 - .../org/openo/msb/wrapper/consul/HealthClient.java | 262 - .../consul/async/ConsulResponseCallback.java | 57 - .../msb/wrapper/consul/cache/CatalogCache.java | 69 - .../msb/wrapper/consul/cache/ConsulCache.java | 245 - .../msb/wrapper/consul/cache/ConsulCache4Map.java | 257 - .../msb/wrapper/consul/cache/HealthCache.java | 69 - .../msb/wrapper/consul/cache/ServiceCache.java | 49 - .../msb/wrapper/consul/model/ConsulResponse.java | 95 - .../wrapper/consul/model/catalog/CatalogNode.java | 56 - .../consul/model/catalog/CatalogService.java | 67 - .../consul/model/catalog/ImmutableCatalogNode.java | 305 - .../model/catalog/ImmutableCatalogService.java | 625 --- .../wrapper/consul/model/catalog/ServiceInfo.java | 62 - .../wrapper/consul/model/health/ImmutableNode.java | 265 - .../consul/model/health/ImmutableService.java | 477 -- .../msb/wrapper/consul/model/health/Node.java | 50 - .../msb/wrapper/consul/model/health/Service.java | 63 - .../wrapper/consul/model/health/ServiceHealth.java | 88 - .../msb/wrapper/consul/option/CatalogOptions.java | 54 - .../msb/wrapper/consul/option/ConsistencyMode.java | 36 - .../consul/option/ImmutableCatalogOptions.java | 250 - .../consul/option/ImmutableQueryOptions.java | 530 -- .../openo/msb/wrapper/consul/option/Options.java | 44 - .../msb/wrapper/consul/option/ParamAdder.java | 38 - .../msb/wrapper/consul/option/QueryOptions.java | 115 - .../consul/util/Base64EncodingDeserializer.java | 60 - .../openo/msb/wrapper/consul/util/ClientUtil.java | 261 - .../org/openo/msb/wrapper/consul/util/Jackson.java | 49 - .../consul/util/ObjectMapperContextResolver.java | 50 - .../wrapper/consul/util/SecondsDeserializer.java | 58 - .../msb/wrapper/consul/util/SecondsSerializer.java | 49 - .../consul/util/UnsignedLongDeserializer.java | 52 - .../IMicroServiceChangeListener.java | 31 - .../MicroServiceChangeListener.java | 291 - .../java/org/openo/msb/wrapper/util/FileUtil.java | 65 - .../openo/msb/wrapper/util/JacksonJsonUtil.java | 119 - .../java/org/openo/msb/wrapper/util/JedisUtil.java | 220 - .../org/openo/msb/wrapper/util/MetricsUtil.java | 23 - .../org/openo/msb/wrapper/util/MicroServiceDB.java | 428 -- .../openo/msb/wrapper/util/MicroServiceUtil.java | 95 - .../org/openo/msb/wrapper/util/RegExpTestUtil.java | 88 - .../java/org/openo/msb/wrapper/util/RouteUtil.java | 134 - .../main/resources/api-doc/META-INF/MANIFEST.MF | 3 - .../src/main/resources/api-doc/WEB-INF/web.xml | 44 - .../src/main/resources/api-doc/css/reset.css | 143 - .../src/main/resources/api-doc/css/screen.css | 1274 ----- .../src/main/resources/api-doc/css/typography.css | 44 - .../api-doc/fonts/droid-sans-v6-latin-700.eot | Bin 22924 -> 0 bytes .../api-doc/fonts/droid-sans-v6-latin-700.svg | 411 -- .../api-doc/fonts/droid-sans-v6-latin-700.ttf | Bin 40516 -> 0 bytes .../api-doc/fonts/droid-sans-v6-latin-700.woff | Bin 25992 -> 0 bytes .../api-doc/fonts/droid-sans-v6-latin-700.woff2 | Bin 11480 -> 0 bytes .../api-doc/fonts/droid-sans-v6-latin-regular.eot | Bin 22008 -> 0 bytes .../api-doc/fonts/droid-sans-v6-latin-regular.svg | 403 -- .../api-doc/fonts/droid-sans-v6-latin-regular.ttf | Bin 39072 -> 0 bytes .../api-doc/fonts/droid-sans-v6-latin-regular.woff | Bin 24868 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.woff2 | Bin 11304 -> 0 bytes .../resources/api-doc/images/explorer_icons.png | Bin 5763 -> 0 bytes .../main/resources/api-doc/images/logo_small.png | Bin 770 -> 0 bytes .../resources/api-doc/images/pet_store_api.png | Bin 824 -> 0 bytes .../src/main/resources/api-doc/images/throbber.gif | Bin 9257 -> 0 bytes .../main/resources/api-doc/images/wordnik_api.png | Bin 980 -> 0 bytes .../src/main/resources/api-doc/index.html | 121 - .../iframeResizer.contentWindow.min.js | 9 - .../api-doc/js/iframeResizer/iframeResizer.min.js | 8 - .../src/main/resources/api-doc/lib/backbone-min.js | 33 - .../main/resources/api-doc/lib/handlebars-2.0.0.js | 29 - .../resources/api-doc/lib/highlight.7.3.pack.js | 2 - .../main/resources/api-doc/lib/jquery-1.8.0.min.js | 2 - .../resources/api-doc/lib/jquery.ba-bbq.min.js | 10 - .../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 | 1284 ----- .../src/main/resources/api-doc/lib/shred.bundle.js | 2766 ---------- .../main/resources/api-doc/lib/shred/content.js | 210 - .../main/resources/api-doc/lib/swagger-client.js | 3316 ----------- .../main/resources/api-doc/lib/swagger-oauth.js | 280 - .../main/resources/api-doc/lib/underscore-min.js | 7 - .../src/main/resources/api-doc/o2c.html | 40 - .../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 | 24 - .../main/resources/iui-metrics/css/animate.min.css | 6 - .../src/main/resources/iui-metrics/css/metrics.css | 159 - .../i18n/loadi18nApp_iui-metrics_view.js | 46 - .../i18n/msb-iui-metrics-i18n-en-US.properties | 85 - .../i18n/msb-iui-metrics-i18n-zh-CN.properties | 81 - .../iui-metrics/img/loading-spinner-grey.gif | Bin 5203 -> 0 bytes .../resources/iui-metrics/img/netnumenLogo.png | Bin 5639 -> 0 bytes .../main/resources/iui-metrics/img/throbber.gif | Bin 9257 -> 0 bytes .../src/main/resources/iui-metrics/index.html | 259 - .../src/main/resources/iui-metrics/js/avalon.js | 5820 -------------------- .../iui-metrics/js/bootstrap/css/bootstrap-dt.css | 5798 ------------------- .../iui-metrics/js/bootstrap/css/bootstrap.min.css | 1 - .../iui-metrics/js/bootstrap/js/bootstrap.js | 1945 ------- .../iui-metrics/js/bootstrap/js/bootstrap.min.js | 6 - .../js/bootstrap/js/bootstrap2-typeahead.min.js | 20 - .../js/dataTables/dataTables.bootstrap.css | 309 -- .../js/dataTables/dataTables.bootstrap.min.js | 8 - .../js/dataTables/jquery.dataTables.min.js | 160 - .../iui-metrics/js/echarts/echarts-all.js | 36 - .../js/fontAwesome/css/font-awesome.css | 2026 ------- .../js/fontAwesome/css/font-awesome.css.map | 7 - .../js/fontAwesome/css/font-awesome.min.css | 1 - .../js/fontAwesome/fonts/FontAwesome.otf | Bin 106260 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.eot | Bin 68875 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.svg | 640 --- .../js/fontAwesome/fonts/fontawesome-webfont.ttf | Bin 138204 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff | Bin 81284 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff2 | Bin 64464 -> 0 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 5639 -> 0 bytes .../js/jquery.i18n/jquery.i18n.properties-1.0.9.js | 480 -- .../iui-metrics/js/jquery/jquery-1.10.2.min.js | 4 - .../main/resources/iui-metrics/js/metricsChart.js | 420 -- .../resources/iui-metrics/js/metricsController.js | 230 - .../main/resources/iui-metrics/js/metricsUtil.js | 49 - .../main/resources/iui-route/css/animate.min.css | 6 - .../src/main/resources/iui-route/css/newRoute.css | 273 - .../src/main/resources/iui-route/css/route.css | 646 --- .../src/main/resources/iui-route/default.html | 144 - .../iui-route/i18n/loadi18nApp_iui-route_view.js | 46 - .../i18n/msb-iui-route-i18n-en-US.properties | 168 - .../i18n/msb-iui-route-i18n-zh-CN.properties | 163 - .../resources/iui-route/img/checkbox-checked.png | Bin 3053 -> 0 bytes .../main/resources/iui-route/img/checkbox-init.png | Bin 2833 -> 0 bytes .../resources/iui-route/img/conductor-logo.png | Bin 4907 -> 0 bytes .../main/resources/iui-route/img/details_close.png | Bin 3300 -> 0 bytes .../main/resources/iui-route/img/details_open.png | Bin 3304 -> 0 bytes .../src/main/resources/iui-route/img/down.png | Bin 2938 -> 0 bytes .../iui-route/img/loading-spinner-grey.gif | Bin 5203 -> 0 bytes .../src/main/resources/iui-route/img/logo.png | Bin 4907 -> 0 bytes .../main/resources/iui-route/img/netnumenLogo.png | Bin 5639 -> 0 bytes .../main/resources/iui-route/img/openo_logo_16.png | Bin 988 -> 0 bytes .../iui-route/img/sidebar-toggler-grey.jpg | Bin 14801 -> 0 bytes .../src/main/resources/iui-route/img/throbber.gif | Bin 9257 -> 0 bytes .../src/main/resources/iui-route/img/up.png | Bin 2926 -> 0 bytes .../main/resources/iui-route/img/zte_logo_16.gif | Bin 583 -> 0 bytes .../src/main/resources/iui-route/index.html | 1040 ---- .../src/main/resources/iui-route/js/avalon.js | 5820 -------------------- .../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 | 5798 ------------------- .../iui-route/js/bootstrap/css/bootstrap.min.css | 1 - .../iui-route/js/bootstrap/js/bootstrap.js | 1945 ------- .../iui-route/js/bootstrap/js/bootstrap.min.js | 7 - .../js/bootstrap/js/bootstrap2-typeahead.min.js | 20 - .../js/dataTables/dataTables.bootstrap.css | 309 -- .../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 | 1 - .../iui-route/js/fontAwesome/fonts/FontAwesome.otf | Bin 106260 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.eot | Bin 68875 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.svg | 640 --- .../js/fontAwesome/fonts/fontawesome-webfont.ttf | Bin 138204 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff | Bin 81284 -> 0 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff2 | Bin 64464 -> 0 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 | 938 ---- .../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 | 34 - .../localization/messages_ar.min.js | 4 - .../jquery-validation/localization/messages_bg.js | 51 - .../localization/messages_bg.min.js | 4 - .../jquery-validation/localization/messages_ca.js | 51 - .../localization/messages_ca.min.js | 4 - .../jquery-validation/localization/messages_cs.js | 51 - .../localization/messages_cs.min.js | 4 - .../jquery-validation/localization/messages_da.js | 48 - .../localization/messages_da.min.js | 4 - .../jquery-validation/localization/messages_de.js | 48 - .../localization/messages_de.min.js | 4 - .../jquery-validation/localization/messages_el.js | 51 - .../localization/messages_el.min.js | 4 - .../jquery-validation/localization/messages_es.js | 54 - .../localization/messages_es.min.js | 4 - .../localization/messages_es_AR.js | 55 - .../localization/messages_es_AR.min.js | 4 - .../jquery-validation/localization/messages_et.js | 49 - .../localization/messages_et.min.js | 4 - .../jquery-validation/localization/messages_eu.js | 51 - .../localization/messages_eu.min.js | 4 - .../jquery-validation/localization/messages_fa.js | 54 - .../localization/messages_fa.min.js | 4 - .../jquery-validation/localization/messages_fi.js | 49 - .../localization/messages_fi.min.js | 4 - .../jquery-validation/localization/messages_fr.js | 77 - .../localization/messages_fr.min.js | 4 - .../jquery-validation/localization/messages_gl.js | 56 - .../localization/messages_gl.min.js | 4 - .../jquery-validation/localization/messages_he.js | 51 - .../localization/messages_he.min.js | 4 - .../jquery-validation/localization/messages_hr.js | 51 - .../localization/messages_hr.min.js | 4 - .../jquery-validation/localization/messages_hu.js | 50 - .../localization/messages_hu.min.js | 4 - .../jquery-validation/localization/messages_id.js | 50 - .../localization/messages_id.min.js | 4 - .../jquery-validation/localization/messages_is.js | 49 - .../localization/messages_is.min.js | 4 - .../jquery-validation/localization/messages_it.js | 54 - .../localization/messages_it.min.js | 4 - .../jquery-validation/localization/messages_ja.js | 51 - .../localization/messages_ja.min.js | 4 - .../jquery-validation/localization/messages_ka.js | 51 - .../localization/messages_ka.min.js | 4 - .../jquery-validation/localization/messages_kk.js | 51 - .../localization/messages_kk.min.js | 4 - .../jquery-validation/localization/messages_ko.js | 51 - .../localization/messages_ko.min.js | 4 - .../jquery-validation/localization/messages_lt.js | 51 - .../localization/messages_lt.min.js | 4 - .../jquery-validation/localization/messages_lv.js | 51 - .../localization/messages_lv.min.js | 4 - .../jquery-validation/localization/messages_my.js | 51 - .../localization/messages_my.min.js | 4 - .../jquery-validation/localization/messages_nl.js | 61 - .../localization/messages_nl.min.js | 4 - .../jquery-validation/localization/messages_no.js | 51 - .../localization/messages_no.min.js | 4 - .../jquery-validation/localization/messages_pl.js | 51 - .../localization/messages_pl.min.js | 4 - .../localization/messages_pt_BR.js | 55 - .../localization/messages_pt_BR.min.js | 4 - .../localization/messages_pt_PT.js | 55 - .../localization/messages_pt_PT.min.js | 4 - .../jquery-validation/localization/messages_ro.js | 51 - .../localization/messages_ro.min.js | 4 - .../jquery-validation/localization/messages_ru.js | 51 - .../localization/messages_ru.min.js | 4 - .../jquery-validation/localization/messages_si.js | 51 - .../localization/messages_si.min.js | 4 - .../jquery-validation/localization/messages_sk.js | 48 - .../localization/messages_sk.min.js | 4 - .../jquery-validation/localization/messages_sl.js | 51 - .../localization/messages_sl.min.js | 4 - .../jquery-validation/localization/messages_sr.js | 51 - .../localization/messages_sr.min.js | 4 - .../localization/messages_sr_lat.js | 51 - .../localization/messages_sr_lat.min.js | 4 - .../jquery-validation/localization/messages_sv.js | 49 - .../localization/messages_sv.min.js | 4 - .../jquery-validation/localization/messages_th.js | 51 - .../localization/messages_th.min.js | 4 - .../jquery-validation/localization/messages_tj.js | 51 - .../localization/messages_tj.min.js | 4 - .../jquery-validation/localization/messages_tr.js | 51 - .../localization/messages_tr.min.js | 4 - .../jquery-validation/localization/messages_uk.js | 51 - .../localization/messages_uk.min.js | 4 - .../jquery-validation/localization/messages_vi.js | 51 - .../localization/messages_vi.min.js | 4 - .../jquery-validation/localization/messages_zh.js | 51 - .../localization/messages_zh.min.js | 4 - .../localization/messages_zh_TW.js | 52 - .../localization/messages_zh_TW.min.js | 4 - .../jquery-validation/localization/methods_de.js | 40 - .../localization/methods_de.min.js | 4 - .../localization/methods_es_CL.js | 40 - .../localization/methods_es_CL.min.js | 4 - .../jquery-validation/localization/methods_fi.js | 40 - .../localization/methods_fi.min.js | 4 - .../jquery-validation/localization/methods_nl.js | 37 - .../localization/methods_nl.min.js | 4 - .../jquery-validation/localization/methods_pt.js | 37 - .../localization/methods_pt.min.js | 4 - .../js/jquery.i18n/jquery.i18n.properties-1.0.9.js | 480 -- .../iui-route/js/jquery/jquery-1.10.2.min.js | 4 - .../main/resources/iui-route/js/routeController.js | 1826 ------ .../src/main/resources/iui-route/js/routeFunc.js | 649 --- .../src/main/resources/iui-route/js/routeUtil.js | 283 - .../src/main/resources/swagger.properties | 51 - .../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 - msb-core/apiroute/apiroute-standalone/pom.xml | 128 - .../assembly/resource/apiroute/apirouteService.exe | Bin 59392 -> 0 bytes .../assembly/resource/apiroute/apirouteService.xml | 33 - .../assembly/resource/apiroute/conf/apiroute.yml | 71 - .../resource/apiroute/ext/initServices/msb.json | 53 - .../resource/apiroute/ext/initServices/readme.txt | 82 - .../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 | 33 - .../resource/apiroute/find_kill_process.bat | 27 - .../src/assembly/resource/apiroute/run.bat | 59 - .../src/assembly/resource/apiroute/run.sh | 46 - .../src/assembly/resource/apiroute/stop.bat | 38 - .../src/assembly/resource/apiroute/stop.sh | 47 - msb-core/apiroute/pom.xml | 39 - msb-core/distributions/pom.xml | 43 - msb-core/distributions/standalone/pom.xml | 250 - .../src/assembly/resource/servicesInstall.bat | 38 - .../src/assembly/resource/servicesRestart.bat | 38 - .../src/assembly/resource/servicesStatus.bat | 38 - .../src/assembly/resource/servicesUninstall.bat | 41 - .../standalone/src/assembly/resource/startup.bat | 46 - .../standalone/src/assembly/resource/startup.sh | 54 - .../standalone/src/assembly/resource/stop.bat | 44 - .../standalone/src/assembly/resource/stop.sh | 49 - msb-core/eag-openresty-ext/pom.xml | 119 - .../resources/openresty/nginx/luaext/msbconf.lua | 26 - .../openresty/nginx/sites-enabled/openomsb.conf | 181 - msb-core/openresty-ext/pom.xml | 165 - .../resources/openresty/lualib/resty/http.lua | 850 --- .../openresty/lualib/resty/http_headers.lua | 62 - .../resources/openresty/nginx/conf/nginx.conf | 72 - .../resources/openresty/nginx/logs/placeholder.txt | 0 .../openresty/nginx/luaext/customrouter.lua | 187 - .../openresty/nginx/luaext/execute_auth.lua | 25 - .../resources/openresty/nginx/luaext/msbconf.lua | 26 - .../openresty/nginx/luaext/openoadminrouter.lua | 110 - .../openresty/nginx/luaext/openoapijsonrouter.lua | 110 - .../openresty/nginx/luaext/openoapirouter.lua | 117 - .../openresty/nginx/luaext/openouirouter.lua | 115 - .../openresty/nginx/luaext/plugins/auth.lua | 171 - .../nginx/luaext/plugins/driver_manager.lua | 127 - .../openresty/nginx/luaext/setnocacheflag.lua | 29 - .../openresty/nginx/sites-enabled/openomsb.conf | 181 - .../resources/openresty/nginx/temp/placeholder.txt | 0 .../src/assembly/resources/openresty/reload.sh | 29 - .../src/assembly/resources/openresty/run.bat | 55 - .../src/assembly/resources/openresty/run.sh | 49 - .../src/assembly/resources/openresty/stop.bat | 54 - .../src/assembly/resources/openresty/stop.sh | 59 - msb-core/pom.xml | 47 - msb-core/redis-ext/pom.xml | 160 - .../assembly/resources/linux/redis/BGREWRITEAOF.sh | 28 - .../assembly/resources/linux/redis/conf/redis.conf | 1023 ---- .../src/assembly/resources/linux/redis/run.sh | 40 - .../src/assembly/resources/linux/redis/stop.sh | 30 - msb-parent/README.md | 17 - msb-parent/msbparent-lite/pom.xml | 240 - msb-parent/msbparent/pom.xml | 311 -- msb-parent/pom.xml | 56 - openresty-ext/pom.xml | 184 + .../resources/openresty/nginx/conf/mime.types | 94 + .../resources/openresty/nginx/conf/nginx.conf | 72 + .../resources/openresty/nginx/html/cert/ca.crt | 22 + .../resources/openresty/nginx/logs/placeholder.txt | 0 .../openresty/nginx/luaext/conf/msbinit.lua | 58 + .../openresty/nginx/luaext/conf/svcconf.lua | 34 + .../resources/openresty/nginx/luaext/dao/dao.lua | 123 + .../openresty/nginx/luaext/dao/db_access.lua | 85 + .../openresty/nginx/luaext/dao/redis_db.lua | 199 + .../openresty/nginx/luaext/lib/tools/db_cache.lua | 54 + .../openresty/nginx/luaext/lib/tools/rr_cache.lua | 40 + .../nginx/luaext/lib/utils/error_handler.lua | 48 + .../openresty/nginx/luaext/lib/utils/log_util.lua | 28 + .../openresty/nginx/luaext/lib/utils/str_util.lua | 30 + .../openresty/nginx/luaext/lib/utils/svc_util.lua | 96 + .../nginx/luaext/lib/utils/table_util.lua | 30 + .../nginx/luaext/loadbalance/balancer.lua | 50 + .../nginx/luaext/loadbalance/baseupstream.lua | 66 + .../nginx/luaext/loadbalance/peerwatcher.lua | 107 + .../nginx/luaext/loadbalance/policy/roundrobin.lua | 68 + .../openresty/nginx/luaext/log/logger.lua | 24 + .../resources/openresty/nginx/luaext/msb.lua | 90 + .../openresty/nginx/luaext/plugins/base_plugin.lua | 35 + .../nginx/luaext/plugins/config_custom.lua | 27 + .../nginx/luaext/plugins/config_default.lua | 27 + .../plugins/redirect-transformer/handler.lua | 42 + .../nginx/luaext/plugins/sampleplugin/handler.lua | 50 + .../nginx/luaext/rewrite/commonrewrite.lua | 138 + .../nginx/luaext/rewrite/customrewrite.lua | 203 + .../openresty/nginx/luaext/vendor/classic.lua | 68 + .../openresty/nginx/luaext/vendor/shcache.lua | 440 ++ .../msb-enabled/location-default/msblocations.conf | 218 + .../nginx/msb-enabled/location-ext-mount/README.md | 4 + .../nginx/msb-enabled/location-ext/README.md | 11 + .../nginx/msb-enabled/location-ext/cert.conf | 19 + .../msb-enabled/location-ext/defaultpage.conf | 9 + .../nginx/msb-enabled/location-ext/homepage.conf | 20 + .../resources/openresty/nginx/msb-enabled/msb.conf | 67 + .../openresty/nginx/msb-enabled/msbhttps.conf | 22 + .../openresty/nginx/sites-enabled-mount/README.md | 4 + .../openresty/nginx/sites-enabled/README.md | 15 + .../openresty/nginx/sites-enabled/addPortTemplate | 21 + .../openresty/nginx/sites-enabled/sampleplugin | 41 + .../resources/openresty/nginx/ssl/cert/cert.crt | 21 + .../resources/openresty/nginx/ssl/cert/cert.key | 27 + .../openresty/nginx/ssl/dh-pubkey/dhparams.pem | 8 + .../openresty/nginx/stream-enabled/placeholder.txt | 0 .../resources/openresty/nginx/temp/placeholder.txt | 0 .../src/assembly/resources/openresty/reload.sh | 26 + .../src/assembly/resources/openresty/run.sh | 47 + .../src/assembly/resources/openresty/run4docker.sh | 39 + .../src/assembly/resources/openresty/stop.sh | 55 + pom.xml | 196 +- redis-ext/pom.xml | 171 + .../assembly/resources/linux/redis/BGREWRITEAOF.sh | 28 + .../assembly/resources/linux/redis/conf/redis.conf | 1023 ++++ .../resources/linux/redis/logs/placeholder.txt | 0 .../src/assembly/resources/linux/redis/run.sh | 46 + .../assembly/resources/linux/redis/run4docker.sh | 46 + .../src/assembly/resources/linux/redis/stop.sh | 33 + 900 files changed, 67990 insertions(+), 85420 deletions(-) create mode 100644 .gitignore delete mode 100644 .gitreview delete mode 100644 License.txt create mode 100644 apiroute/apiroute-service/dependency-reduced-pom.xml create mode 100644 apiroute/apiroute-service/pom.xml create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteApp.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteAppConfig.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/SyncDataManager.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/ApiRouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomDateSerializer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomRouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/DiscoverInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/IuiRouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/MicroServiceFullInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/Node.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/PublishFullAddress.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteServer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedInternalServerErrorException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedNotFoundException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/UnprocessableEntityException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ApiRouteHealthCheck.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ConsulLinkHealthCheck.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/OpenRestyHealthCheck.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/RedisHealthCheck.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/ApiRouteResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/CustomRouteResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/IuiRouteResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/MicroServiceResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/ApiRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/CustomRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/InitRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/IuiRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/MicroServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/CatalogClient.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/Consul.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/HealthClient.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseCallback.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseHeader.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/OriginalConsulResponse.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ConsulCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServiceHealthCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServicesCatalogCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckServiceDataEmptyAndAutoStopWatchFilter.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckTagAndAutoStopWatchFilter.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ConsulIndexFilter.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ServiceModifyIndexFilter.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchCatalogServicesTask.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchServiceHealthTask.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchTask.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WriteBufferHandler.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/Service.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/ServiceHealth.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/util/Http.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOConstants.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOFactory.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/RedisAccessWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/IRouteDAO.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/RouteDAOImpl.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Metadata.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Node.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/RouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Spec.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/IServiceDAO.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/ServiceDAOImpl.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Metadata.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Node.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/ServiceInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Spec.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/BaseQueue.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/QueueManager.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceConsumer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceData.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListConsumer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListQueue.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceQueue.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/ApiRouteService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/CustomRouteService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/IuiRouteService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/MicroServiceFullService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/IMicroServiceChangeListener.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/MicroServiceChangeListener.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/RouteNotify.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/CommonUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ConfigUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/FileUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpClientUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpGetResult.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/Jackson.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JacksonJsonUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JedisUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/MicroServiceUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RegExpTestUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RouteUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ServiceFilter.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/js/tools.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-route/css/animate.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/css/base.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/css/chart.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/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/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/echarts/echarts.common.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/echarts/macarons.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/main/resources/iui-route/js/statusUtil.js create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/SyncDataManagerTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/health/ApiRouteHealthCheckTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/health/ConsulLinkHealthCheckTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/health/OpenRestyHealthCheckTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/health/RedisHealthCheckTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/ApiRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/CustomRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/InitRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/IuiRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/MicroServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/ConsulTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServiceHealthCacheTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServicesCatalogCacheTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckServiceDataEmptyAndAutoStopWatchFilterTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckTagAndAutoStopWatchFilterTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ConsulIndexFilterTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ServiceModifyIndexFilterTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchCatalogServicesTaskTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchServiceHealthTaskTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WriteBufferHandlerTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/ServiceHealthTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/ServiceTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/consulextend/util/HttpTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/queue/QueueManagerTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/service/ApiRouteServiceTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/service/CustomRouteServiceTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/service/IuiRouteServiceTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/service/MicroServiceFullServiceTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/serviceListener/MicroServiceChangeListenerTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/CommonUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/ConfigUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/HttpClientUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/JacksonJsonUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/JedisUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/MicroServiceUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/RegExpTestUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/RouteUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/onap/msb/apiroute/wrapper/util/ServiceFilterTest.java create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initApiGatewayConfig/initApiGatewayConfig.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initRouteLabels/initRouteLabelsMatches.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initRouteLabels/initRouteLabelsMatches.json.sample create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initRouteLabels/readme.txt create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initRouteWay/initRouteWay.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initRouteWay/readme.txt create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initServices/msb.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initServices/readme.txt create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initSwaggerJson/api-doc1.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initSwaggerJson/api-doc2.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/initUrlRootPath/initUrlRootPath.json create mode 100644 apiroute/apiroute-service/src/test/resources/ext/redisConf/redis.properties create mode 100644 apiroute/apiroute-service/src/test/resources/org/onap/msb/apiroute/wrapper/consulextend/util/serviceslist.json create mode 100644 apiroute/apiroute-standalone/pom.xml create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/conf/apiroute.yml create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initApiGatewayConfig/initApiGatewayConfig.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initRouteLabels/initRouteLabelsMatches.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initRouteLabels/initRouteLabelsMatches.json.sample create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initRouteLabels/readme.txt create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initRouteWay/initRouteWay.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initRouteWay/readme.txt create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initServices/msb.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initServices/readme.txt create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initSwaggerJson/api-doc1.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initSwaggerJson/api-doc2.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/initUrlRootPath/initUrlRootPath.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/ext/redisConf/redis.properties create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/run.sh create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/setenv.sh create mode 100644 apiroute/apiroute-standalone/src/assembly/resources/apiroute/stop.sh create mode 100644 apiroute/pom.xml create mode 100644 build4docker.sh create mode 100644 ci/build_docker_image.sh create mode 100644 distributions/msb-apigateway/pom.xml create mode 100644 distributions/msb-apigateway/src/assembly/resources/shutdown.sh create mode 100644 distributions/msb-apigateway/src/assembly/resources/startup.sh create mode 100644 distributions/msb-apigateway/src/assembly/resources/startup4docker.sh create mode 100644 distributions/msb-apigateway/src/main/docker/Dockerfile create mode 100644 distributions/pom.xml delete mode 100644 msb-core/apiroute/apiroute-service/pom.xml delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteApp.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteAppConfig.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/ConsulClientApp.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ApiRouteInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ConsulInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomDateSerializer.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomRouteInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/DiscoverInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/IuiRouteInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MetricsInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceFullInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Node.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/NodeInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/RouteServer.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Service.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ServiceAccessInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedInternalServerErrorException.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotFoundException.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotSupportedException.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/health/ApiRouteHealthCheck.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ApiRouteResource.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/CustomRouteResource.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/IuiRouteResource.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MetricsResource.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MicroServiceResource.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ServiceAccessResource.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ApiRouteServiceWrapper.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/CustomRouteServiceWrapper.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/IuiRouteServiceWrapper.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MetricsServiceWrapper.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MicroServiceWrapper.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ServiceAccessWrapper.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/CatalogClient.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/Consul.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/ConsulException.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/HealthClient.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/async/ConsulResponseCallback.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/CatalogCache.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache4Map.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/HealthCache.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ServiceCache.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/ConsulResponse.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogNode.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogService.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogNode.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogService.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ServiceInfo.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableNode.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableService.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Node.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Service.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ServiceHealth.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/CatalogOptions.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ConsistencyMode.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableCatalogOptions.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableQueryOptions.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/Options.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ParamAdder.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/QueryOptions.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Base64EncodingDeserializer.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Jackson.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ObjectMapperContextResolver.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsDeserializer.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsSerializer.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/UnsignedLongDeserializer.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/IMicroServiceChangeListener.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/MicroServiceChangeListener.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/FileUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JacksonJsonUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JedisUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MetricsUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceDB.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RegExpTestUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RouteUtil.java delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/META-INF/MANIFEST.MF delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/WEB-INF/web.xml delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/css/reset.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/css/screen.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/css/typography.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.eot delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.svg delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.ttf delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff2 delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.eot delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.svg delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.ttf delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff2 delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/images/explorer_icons.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/images/logo_small.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/images/pet_store_api.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/images/throbber.gif delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/images/wordnik_api.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/index.html delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.contentWindow.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/backbone-min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/handlebars-2.0.0.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/highlight.7.3.pack.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery-1.8.0.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery.ba-bbq.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery.slideto.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery.wiggle.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/marked.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/shred.bundle.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/shred/content.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/swagger-client.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/swagger-oauth.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/lib/underscore-min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/o2c.html delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/swagger-ui.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/api-doc/swagger-ui.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/banner.txt delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/css/animate.min.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/css/metrics.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/i18n/loadi18nApp_iui-metrics_view.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/i18n/msb-iui-metrics-i18n-en-US.properties delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/i18n/msb-iui-metrics-i18n-zh-CN.properties delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/img/loading-spinner-grey.gif delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/img/netnumenLogo.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/img/throbber.gif delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/index.html delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/avalon.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/css/bootstrap-dt.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/css/bootstrap.min.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/js/bootstrap.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/js/bootstrap.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/js/bootstrap2-typeahead.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/dataTables/dataTables.bootstrap.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/dataTables/dataTables.bootstrap.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/dataTables/jquery.dataTables.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/echarts/echarts-all.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/css/font-awesome.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/css/font-awesome.css.map delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/css/font-awesome.min.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/FontAwesome.otf delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.eot delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.svg delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.ttf delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.woff delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.woff2 delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/animated.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/bordered-pulled.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/core.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/fixed-width.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/font-awesome.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/icons.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/larger.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/list.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/mixins.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/path.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/rotated-flipped.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/stacked.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/variables.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_animated.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_bordered-pulled.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_core.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_fixed-width.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_icons.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_larger.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_list.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_mixins.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_path.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_rotated-flipped.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_stacked.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_variables.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/font-awesome.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/iframeResizer/iframeResizer.contentWindow.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/iframeResizer/iframeResizer.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/images/sort_both.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/jquery.i18n/jquery.i18n.properties-1.0.9.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/jquery/jquery-1.10.2.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/metricsChart.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/metricsController.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-metrics/js/metricsUtil.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/css/animate.min.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/css/newRoute.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/css/route.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/default.html delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/i18n/loadi18nApp_iui-route_view.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/i18n/msb-iui-route-i18n-en-US.properties delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/i18n/msb-iui-route-i18n-zh-CN.properties delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/checkbox-checked.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/checkbox-init.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/conductor-logo.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/details_close.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/details_open.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/down.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/loading-spinner-grey.gif delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/logo.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/netnumenLogo.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/openo_logo_16.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/sidebar-toggler-grey.jpg delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/throbber.gif delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/up.png delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/img/zte_logo_16.gif delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/index.html delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/avalon.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootbox/bootbox.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap-growl.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap-dt.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap.min.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap2-typeahead.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/dataTables/dataTables.bootstrap.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/dataTables/dataTables.bootstrap.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/dataTables/jquery.dataTables.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/css/font-awesome.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/css/font-awesome.css.map delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/css/font-awesome.min.css delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/FontAwesome.otf delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.eot delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.svg delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.ttf delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.woff delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.woff2 delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/animated.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/bordered-pulled.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/core.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/fixed-width.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/font-awesome.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/icons.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/larger.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/list.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/mixins.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/path.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/rotated-flipped.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/stacked.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/variables.less delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_animated.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_bordered-pulled.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_core.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_fixed-width.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_icons.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_larger.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_list.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_mixins.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_path.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_rotated-flipped.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_stacked.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_variables.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/font-awesome.scss delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/iframeResizer/iframeResizer.contentWindow.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/iframeResizer/iframeResizer.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/additional-methods.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/additional-methods.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/jquery.validate.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/jquery.validate.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ar.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ar.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_bg.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_bg.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ca.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ca.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_cs.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_cs.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_da.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_da.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_de.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_de.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_el.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_el.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es_AR.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es_AR.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_et.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_et.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_eu.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_eu.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fa.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fa.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fi.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fi.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fr.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fr.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_gl.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_gl.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_he.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_he.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hr.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hr.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hu.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hu.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_id.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_id.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_is.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_is.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_it.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_it.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ja.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ja.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ka.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ka.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_kk.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_kk.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ko.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ko.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lt.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lt.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lv.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lv.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_my.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_my.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_nl.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_nl.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_no.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_no.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pl.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pl.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_BR.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_BR.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_PT.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_PT.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ro.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ro.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ru.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ru.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_si.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_si.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sk.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sk.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sl.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sl.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr_lat.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr_lat.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sv.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sv.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_th.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_th.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tj.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tj.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tr.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tr.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_uk.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_uk.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_vi.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_vi.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh_TW.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh_TW.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_de.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_de.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_es_CL.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_es_CL.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_fi.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_fi.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_nl.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_nl.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_pt.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_pt.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery.i18n/jquery.i18n.properties-1.0.9.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/jquery/jquery-1.10.2.min.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/routeController.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/routeFunc.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/iui-route/js/routeUtil.js delete mode 100644 msb-core/apiroute/apiroute-service/src/main/resources/swagger.properties delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/ApiRouteServiceWrapperTest.java delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/CustomRouteServiceWrapperTest.java delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/IuiRouteServiceWrapperTest.java delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/MicroServiceWrapperTest.java delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/util/JacksonJsonUtilTest.java delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/util/RegExpTestUtilTest.java delete mode 100644 msb-core/apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/util/RouteUtilTest.java delete mode 100644 msb-core/apiroute/apiroute-standalone/pom.xml delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/apirouteService.exe delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/apirouteService.xml delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/conf/apiroute.yml delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initServices/msb.json delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initServices/readme.txt delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initSwaggerJson/api-doc1.json delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initSwaggerJson/api-doc2.json delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initUrlRootPath/initUrlRootPath.json delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initVisualRange/initVisualRangeMatches.json delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initVisualRange/readme.txt delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/find_kill_process.bat delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/run.bat delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/run.sh delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/stop.bat delete mode 100644 msb-core/apiroute/apiroute-standalone/src/assembly/resource/apiroute/stop.sh delete mode 100644 msb-core/apiroute/pom.xml delete mode 100644 msb-core/distributions/pom.xml delete mode 100644 msb-core/distributions/standalone/pom.xml delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/servicesInstall.bat delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/servicesRestart.bat delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/servicesStatus.bat delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/servicesUninstall.bat delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/startup.bat delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/startup.sh delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/stop.bat delete mode 100644 msb-core/distributions/standalone/src/assembly/resource/stop.sh delete mode 100644 msb-core/eag-openresty-ext/pom.xml delete mode 100644 msb-core/eag-openresty-ext/src/assembly/resources/openresty/nginx/luaext/msbconf.lua delete mode 100644 msb-core/eag-openresty-ext/src/assembly/resources/openresty/nginx/sites-enabled/openomsb.conf delete mode 100644 msb-core/openresty-ext/pom.xml delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/lualib/resty/http_headers.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/conf/nginx.conf delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/logs/placeholder.txt delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/customrouter.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/execute_auth.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/msbconf.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/openoadminrouter.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/openoapijsonrouter.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/openoapirouter.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/openouirouter.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/auth.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/driver_manager.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/setnocacheflag.lua delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/sites-enabled/openomsb.conf delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/nginx/temp/placeholder.txt delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/reload.sh delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/run.bat delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/run.sh delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/stop.bat delete mode 100644 msb-core/openresty-ext/src/assembly/resources/openresty/stop.sh delete mode 100644 msb-core/pom.xml delete mode 100644 msb-core/redis-ext/pom.xml delete mode 100644 msb-core/redis-ext/src/assembly/resources/linux/redis/BGREWRITEAOF.sh delete mode 100644 msb-core/redis-ext/src/assembly/resources/linux/redis/conf/redis.conf delete mode 100644 msb-core/redis-ext/src/assembly/resources/linux/redis/run.sh delete mode 100644 msb-core/redis-ext/src/assembly/resources/linux/redis/stop.sh delete mode 100644 msb-parent/README.md delete mode 100644 msb-parent/msbparent-lite/pom.xml delete mode 100644 msb-parent/msbparent/pom.xml delete mode 100644 msb-parent/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/html/cert/ca.crt create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/logs/placeholder.txt 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/tools/rr_cache.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/lib/utils/error_handler.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/str_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/baseupstream.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/loadbalance/peerwatcher.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/msb.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/base_plugin.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/config_custom.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/config_default.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/redirect-transformer/handler.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/plugins/sampleplugin/handler.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/classic.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-default/msblocations.conf 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/location-ext/cert.conf create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/location-ext/defaultpage.conf create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/location-ext/homepage.conf create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/msb.conf create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/msbhttps.conf 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/sites-enabled/addPortTemplate create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/sites-enabled/sampleplugin create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/ssl/cert/cert.crt create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/ssl/cert/cert.key create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/ssl/dh-pubkey/dhparams.pem create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/stream-enabled/placeholder.txt create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/temp/placeholder.txt create mode 100644 openresty-ext/src/assembly/resources/openresty/reload.sh 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.sh create mode 100644 redis-ext/pom.xml create mode 100644 redis-ext/src/assembly/resources/linux/redis/BGREWRITEAOF.sh create mode 100644 redis-ext/src/assembly/resources/linux/redis/conf/redis.conf create mode 100644 redis-ext/src/assembly/resources/linux/redis/logs/placeholder.txt create mode 100644 redis-ext/src/assembly/resources/linux/redis/run.sh create mode 100644 redis-ext/src/assembly/resources/linux/redis/run4docker.sh create mode 100644 redis-ext/src/assembly/resources/linux/redis/stop.sh diff --git a/.gitattributes b/.gitattributes index 13e92ef..bb0dee2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.java text eol=lf *.xml text eol=lf +*.sh text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6355b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.project +.classpath +.settings/ +target/ +.vagrant +/archives/ +*.jar +*.zip +*.tar +*.gz diff --git a/.gitreview b/.gitreview deleted file mode 100644 index bb6d75f..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=gerrit.open-o.org -port=29418 -project=common-services-microservice-bus.git diff --git a/License.txt b/License.txt deleted file mode 100644 index dd08487..0000000 --- a/License.txt +++ /dev/null @@ -1,473 +0,0 @@ -THIS LICENSE FILE CONTAINS THE LICENSE APPLICABLE DEPENDING ON THE TYPE OF CONTRIBUTIONS. - -APACHE LICENSE 2 IS APPLICABLE FOR SOURCE CODE, CREATIVE COMMONS ATTRIBUTION 4.0 INTERNATIONAL FOR DOCUMENTATION - -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -Attribution 4.0 International - -https://creativecommons.org/licenses/by/4.0/legalcode - ---------------------------------------------------------------------------------------- - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - ---------------------------------------------------------------------------------------- - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - ---------------------------------------------------------------------------------------- - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the "Licensor." The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md index dc836cc..6218077 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,8 @@ -##Microservice Bus -Within the OPEN-O architecture, there are a lot of microservices, e.g., Catalog, Res Mgr., LCM Mgr., Drivers. These microservices are distributed on multiple hosts, that make the communicate between them complex because the consumers need to know the addresses of all the service providers. Besides, some services may have multiple instances, which make the consumer even harder to locate the service provider. Microservice bus provides a service registration/ discovery and routing mechanism to simply the communications between services. The consumers only need to talk with microservice bus without any address information of individual service providers. -##Runtime Requirements -* Java 7 - -##Run - -1. Install 1.7 or higer version of JDK -1. export JAVA_HOME= "$JAVA_INSTALLATION_PATH" , please replace $JAVA_INSTALLATION_PATH with the JDK installation directory on your host -1. Compile form source or download the latest snapshot from https://nexus.open-o.org/content/repositories/snapshots/org/openo/common-services/microservice-bus/msb-core-standalone/1.0.0-SNAPSHOT/ -1. tar -xzf msb-standalone-1.0.0-SNAPSHOT-linux64.tar.gz -1. sudo -E ./startup.sh - -The default port is 80. +msb-apigateway +=============== +Service API gateway provides client request routing, client request load balancing, transformation, authentication & authorization for service request with plugin of auth service provider,service request logging,service request rate-limiting,service monitoring,request result cache,solve cross-domain issue for web application and other functionalities with the pluggable architecture capability. ## License The Microservice Bus is released under version 2.0 of the [Apache License][]. [Apache License]: http://www.apache.org/licenses/LICENSE-2.0 - - \ No newline at end of file diff --git a/apiroute/apiroute-service/dependency-reduced-pom.xml b/apiroute/apiroute-service/dependency-reduced-pom.xml new file mode 100644 index 0000000..63e1139 --- /dev/null +++ b/apiroute/apiroute-service/dependency-reduced-pom.xml @@ -0,0 +1,121 @@ + + + + apiroute-parent + org.onap.msb.apigateway.apiroute + 0.0.1-SNAPSHOT + + 4.0.0 + org.onap.msb.apigateway.apiroute + apiroute-service + onap/msb/apigateway/apiroute/apiroute-service + 0.0.1-SNAPSHOT + + + + src/main/java + + **/*.properties + + + + src/main/resources + + + + + maven-jar-plugin + 2.4 + + + + true + + + + + + maven-shade-plugin + 2.3 + + + package + + shade + + + + + + org.onap.msb.apiroute.ApiRouteApp + + + + + + + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + junit + junit + 4.11 + test + + + hamcrest-core + org.hamcrest + + + + + org.powermock + powermock-module-junit4 + 1.5.5 + test + + + powermock-module-junit4-common + org.powermock + + + + + org.powermock + powermock-api-mockito + 1.5.5 + test + + + mockito-all + org.mockito + + + powermock-api-support + org.powermock + + + + + com.fiftyonred + mock-jedis + 0.4.0 + test + + + + diff --git a/apiroute/apiroute-service/pom.xml b/apiroute/apiroute-service/pom.xml new file mode 100644 index 0000000..2a3c3e0 --- /dev/null +++ b/apiroute/apiroute-service/pom.xml @@ -0,0 +1,174 @@ + + + + + org.onap.msb.apigateway.apiroute + apiroute-parent + 0.0.1-SNAPSHOT + + 4.0.0 + org.onap.msb.apigateway.apiroute + apiroute-service + onap/msb/apigateway/apiroute/apiroute-service + jar + 0.0.1-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 + + + + junit + junit + test + + + org.powermock + powermock-module-junit4 + test + + + + org.powermock + powermock-api-mockito + test + + + com.orbitz.consul + consul-client + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-annotations + + + + org.apache.httpcomponents + httpasyncclient + + + + org.immutables + value + compile + + + + com.fiftyonred + mock-jedis + + + + commons-io + commons-io + + + + + + + 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.onap.msb.apiroute.ApiRouteApp + + + + + + + + + + src/main/java + + **/*.properties + + + + src/main/resources + + + + diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteApp.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteApp.java new file mode 100644 index 0000000..5c3096f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteApp.java @@ -0,0 +1,131 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute; +import io.dropwizard.Application; +import io.dropwizard.assets.AssetsBundle; +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 org.onap.msb.apiroute.health.ApiRouteHealthCheck; +import org.onap.msb.apiroute.resources.ApiRouteResource; +import org.onap.msb.apiroute.resources.CustomRouteResource; +import org.onap.msb.apiroute.resources.IuiRouteResource; +import org.onap.msb.apiroute.resources.MicroServiceResource; +import org.onap.msb.apiroute.wrapper.InitRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +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); + + InitRouteServiceWrapper.getInstance().initFilterConfig(); + + InitRouteServiceWrapper.getInstance().initDataSynchro(); + + InitRouteServiceWrapper.getInstance().initHealthCheck(); + } + + + @Override + public String getName() { + return " MicroService Bus "; + } + + @Override + public void initialize(Bootstrap bootstrap) { + super.initialize(bootstrap); + + } + + @Override + public void run(ApiRouteAppConfig configuration, Environment environment) throws Exception { + + + ConfigUtil.getInstance().initRootPath(); + + + String iuiRootPath=ConfigUtil.getInstance().getIUI_ROOT_PATH(); + + // new AssetsBundle("/iui-metrics", "/"+iuiRootPath+"/microservices/metrics","index.html", "iui-metrics").run(environment); + + new AssetsBundle("/iui-route", "/"+iuiRootPath+"/microservices", "index.html","iui-microservices").run(environment); + + new AssetsBundle("/api-doc", "/"+iuiRootPath+"/microservices/api-doc","index.html", "api-doc").run(environment); + + new AssetsBundle("/ext", "/"+iuiRootPath+"/microservices/ext","index.html", "ext").run(environment); + + + + + final ApiRouteHealthCheck healthCheck =new ApiRouteHealthCheck(); + environment.healthChecks().register("consulCheck", healthCheck); + + environment.jersey().register(new ApiRouteResource()); + environment.jersey().register(new IuiRouteResource()); + environment.jersey().register(new CustomRouteResource()); + environment.jersey().register(new MicroServiceResource()); + + // initSwaggerConfig(environment, configuration); + + ConfigUtil.getInstance().initConsulIp(); + ConfigUtil.getInstance().initDiscoverInfo(configuration); + // InitRouteServiceWrapper.getInstance().initMetricsConfig(configuration); + + + } + + + + + + + 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("ApiRoute RESTful API"); + config.setVersion("1.0.0"); + config.setResourcePackage("org.onap.msb.apiroute.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.warn("getApplicationContextPath: " + basePath); + config.setBasePath(basePath); + config.setScan(true); + } + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteAppConfig.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteAppConfig.java new file mode 100644 index 0000000..4c998e0 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/ApiRouteAppConfig.java @@ -0,0 +1,90 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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. + */ +/** +* 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.onap.msb.apiroute; + +import io.dropwizard.Configuration; + +import javax.validation.Valid; + +import org.hibernate.validator.constraints.NotEmpty; +import org.onap.msb.apiroute.api.DiscoverInfo; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ApiRouteAppConfig extends Configuration { + @NotEmpty + private String defaultWorkspace = "apiroute-works"; + + @NotEmpty + private String defaultName = "Stranger"; + + + @Valid + private DiscoverInfo discoverInfo; + + + @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/onap/msb/apiroute/SyncDataManager.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/SyncDataManager.java new file mode 100644 index 0000000..865778e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/SyncDataManager.java @@ -0,0 +1,135 @@ +package org.onap.msb.apiroute; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.http.HttpEntity; +import org.onap.msb.apiroute.wrapper.consulextend.Consul; +import org.onap.msb.apiroute.wrapper.consulextend.expose.CheckServiceDataEmptyAndAutoStopWatchFilter; +import org.onap.msb.apiroute.wrapper.consulextend.expose.CheckTagAndAutoStopWatchFilter; +import org.onap.msb.apiroute.wrapper.consulextend.expose.ServiceModifyIndexFilter; +import org.onap.msb.apiroute.wrapper.consulextend.expose.WatchCatalogServicesTask; +import org.onap.msb.apiroute.wrapper.consulextend.expose.WatchServiceHealthTask; +import org.onap.msb.apiroute.wrapper.consulextend.expose.WriteBufferHandler; +import org.onap.msb.apiroute.wrapper.queue.ServiceConsumer; +import org.onap.msb.apiroute.wrapper.queue.ServiceData; +import org.onap.msb.apiroute.wrapper.queue.ServiceListConsumer; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SyncDataManager { + private static Consul consul; + private static WatchCatalogServicesTask serviceListWatchTask; + private final static Map serviceWatchTaskMap = new ConcurrentHashMap(); + + private static final Logger LOGGER = LoggerFactory + .getLogger(SyncDataManager.class); + + private SyncDataManager() { + } + + public static void initSyncTask(final String ip, final int port) { + consul = Consul.builder().withHostAndPort(ip, port).build(); + startWatchServiceList(); + startQueueConsumer(); + } + + public static void startWatchServiceList() { + + LOGGER.info("===========start to WatchServiceList============"); + + // create service list watch task + serviceListWatchTask = new WatchCatalogServicesTask( + consul.catalogClient(), RouteUtil.WATCH_SECOND); + + // first,write data to serviceListQueue buffer. + // second,async thread will read data from serviceListQueue buffer. + serviceListWatchTask.addHandler(new WriteBufferHandler( + ServiceData.DataType.service_list)); + + // start watch + serviceListWatchTask.startWatch(); + } + + public static void startQueueConsumer() { + LOGGER.info("===========start to QueueConsumer Thread============"); + + // start ServiceListConsumer + new Thread(new ServiceListConsumer(), "ServiceListConsumerThread") + .start(); + + // start Service Consumer + int serviceQueneNum = RouteUtil.SERVICE_DATA_QUEUE_NUM; + for (int i = 0; i < serviceQueneNum; i++) { + new Thread(new ServiceConsumer(i), "ServiceConsumerThread" + i) + .start(); + } + + } + + public static void startWatchService(final String serviceName) { + + LOGGER.info("===========start to Watch Service[" + serviceName + + "]============"); + // create service watch task + WatchServiceHealthTask serviceWatchTask = new WatchServiceHealthTask( + consul.healthClient(), serviceName, RouteUtil.WATCH_SECOND); + + // 1.service Data Empty filter + serviceWatchTask + .addFilter(new CheckServiceDataEmptyAndAutoStopWatchFilter( + serviceName)); + + // 2.service change filter + serviceWatchTask.addFilter(new ServiceModifyIndexFilter()); + + // 3.apigateway tag filter:check tag and auto stop watch + serviceWatchTask.addFilter(new CheckTagAndAutoStopWatchFilter( + serviceName)); + + // start watch + serviceWatchTask.startWatch(); + + // save + serviceWatchTaskMap.put(serviceName, serviceWatchTask); + } + + public static void stopWatchServiceList() { + if (serviceListWatchTask != null) { + serviceListWatchTask.removeAllFilter(); + serviceListWatchTask.removeAllHandler(); + serviceListWatchTask.stopWatch(); + } + } + + public static void stopWatchService(String serviceName) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("stop " + serviceName + " service watch!"); + } + + WatchServiceHealthTask watchTask = serviceWatchTaskMap.get(serviceName); + if (watchTask != null) { + watchTask.removeAllFilter(); + watchTask.removeAllHandler(); + watchTask.stopWatch(); + } + serviceWatchTaskMap.remove(serviceName); + } + + public static boolean resetIndex(String serviceName) { + + WatchServiceHealthTask watchTask = serviceWatchTaskMap.get(serviceName); + + if (watchTask != null) { + return watchTask.resetIndex(); + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("reset modify index.did not find:" + serviceName); + } + + return false; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/ApiRouteInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/ApiRouteInfo.java new file mode 100644 index 0000000..1465000 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/ApiRouteInfo.java @@ -0,0 +1,107 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Objects; + + +public class ApiRouteInfo extends RouteInfo { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(example = "v1", required = true) + private String version; + + + 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=""; + + + + 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 getApiJsonType() { + return apiJsonType; + } + public void setApiJsonType(String apiJsonType) { + this.apiJsonType = apiJsonType; + } + public String getMetricsUrl() { + return metricsUrl; + } + public void setMetricsUrl(String metricsUrl) { + this.metricsUrl = metricsUrl; + } + + + + + @Override + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ApiRouteInfo that = (ApiRouteInfo) o; + return Objects.equals(isEnable_ssl(), that.isEnable_ssl()) && + Objects.equals(getServiceName(), that.getServiceName()) && + Objects.equals(version, that.version) && + Objects.equals(getUrl(), that.getUrl()) && + Objects.equals(apiJson, that.apiJson) && + Objects.equals(apiJsonType, that.apiJsonType) && + Objects.equals(metricsUrl, that.metricsUrl) && + Objects.equals(getControl(), that.getControl()) && + Objects.equals(getStatus(), that.getStatus()) && + Objects.equals(getVisualRange(), that.getVisualRange()) && + Objects.equals(getUseOwnUpstream(), that.getUseOwnUpstream()) && + Arrays.equals(getServers(), that.getServers()) && + Objects.equals(getHost(), that.getHost()) && + Objects.equals(getNamespace(), that.getNamespace()) && + Objects.equals(getPublish_port(), that.getPublish_port()) && + Objects.equals(getConsulServiceName(), that.getConsulServiceName()) && + Objects.equals(getPublishProtocol(), that.getPublishProtocol()); + } + + @Override + public int hashCode() { + return Objects.hash(getServiceName(), version, getUrl(), apiJson, apiJsonType, metricsUrl, getControl(), getStatus(), getVisualRange(), getServers(), getHost(), getNamespace(), getPublish_port(), isEnable_ssl(), getConsulServiceName(), getPublishProtocol()); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomDateSerializer.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomDateSerializer.java new file mode 100644 index 0000000..709a995 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomDateSerializer.java @@ -0,0 +1,39 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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/onap/msb/apiroute/api/CustomRouteInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomRouteInfo.java new file mode 100644 index 0000000..788ce00 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/CustomRouteInfo.java @@ -0,0 +1,24 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.api; + + +public class CustomRouteInfo extends RouteInfo{ + + private static final long serialVersionUID = 1L; + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/DiscoverInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/DiscoverInfo.java new file mode 100644 index 0000000..4a4708b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/DiscoverInfo.java @@ -0,0 +1,55 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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; + } + + @Override + public String toString() { + // TODO Auto-generated method stub + return this.ip+":"+this.port; + } + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/IuiRouteInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/IuiRouteInfo.java new file mode 100644 index 0000000..b3f3cf6 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/IuiRouteInfo.java @@ -0,0 +1,25 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.api; + + + +public class IuiRouteInfo extends RouteInfo{ + + private static final long serialVersionUID = 1L; + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/MicroServiceFullInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/MicroServiceFullInfo.java new file mode 100644 index 0000000..ca0b235 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/MicroServiceFullInfo.java @@ -0,0 +1,195 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class MicroServiceFullInfo implements Serializable { + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(required = true) + private String serviceName; + + @ApiModelProperty(example = "v1") + private String version=""; + + @ApiModelProperty(value = "Target Service URL,start with /",example = "/api/serviceName/v1", required = true) + private String url=""; + + @ApiModelProperty(value = "Service Protocol", allowableValues = "REST,UI, MQ, FTP,SNMP,TCP,UDP", example = "REST",required = true) + private String protocol = ""; + + @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=""; + + private String namespace=""; + + private String host=""; + + private String path=""; + + private String publish_port=""; + + @ApiModelProperty(value = "enable ssl", allowableValues = "true,false", example = "false") + private boolean enable_ssl=false; //true:https:å¼€å¯SSL加密, false:http + + private String custom; //PORTALå议标志 + + private Set nodes; + + @ApiModelProperty(value = "Service Status", allowableValues = "0,1", example = "1") + private String status = "1"; //0:disable 1:enable + + + + + 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; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + + public String getHost() { + return host; + } + public void setHost(String host) { + this.host = host; + } + public String getPath() { + return path; + } + public void setPath(String path) { + this.path = path; + } + + + + 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; + } + public String getPublish_port() { + return publish_port; + } + public void setPublish_port(String publish_port) { + this.publish_port = publish_port; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MicroServiceFullInfo that = (MicroServiceFullInfo) o; + return Objects.equals(serviceName, that.serviceName) && + Objects.equals(version, that.version) && + Objects.equals(url, that.url) && + Objects.equals(protocol, that.protocol) && + Objects.equals(visualRange, that.visualRange) && + Objects.equals(lb_policy, that.lb_policy) && + Objects.equals(namespace, that.namespace) && + Objects.equals(host, that.host) && + Objects.equals(path, that.path) && + Objects.equals(publish_port, that.publish_port) && + Objects.equals(enable_ssl, that.enable_ssl) && + Objects.equals(nodes, that.nodes) && + Objects.equals(status, that.status); + } + + @Override + public int hashCode() { + return Objects.hash(serviceName, version, url, protocol, visualRange, lb_policy, namespace, host, path, publish_port, enable_ssl, nodes, status); + } + public boolean isEnable_ssl() { + return enable_ssl; + } + public void setEnable_ssl(boolean enable_ssl) { + this.enable_ssl = enable_ssl; + } + public String getCustom() { + return custom; + } + public void setCustom(String custom) { + this.custom = custom; + } + + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/Node.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/Node.java new file mode 100644 index 0000000..c515820 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/Node.java @@ -0,0 +1,98 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Objects; + +public class Node implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(required = true) + private String ip; + + @ApiModelProperty(required = true) + private String port; + + private String status="passing"; //实例å¥åº·æ£€æŸ¥çŠ¶æ€ + + private int ttl=-1; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + 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; + } + + public Node(String ip,String port){ + this.ip = ip; + this.port = port; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Node node = (Node) o; + return Objects.equals(ttl, node.ttl) && + Objects.equals(ip, node.ip) && + Objects.equals(port, node.port) && + Objects.equals(status, node.status); + } + + @Override + public int hashCode() { + return Objects.hash(ip, port, status, ttl); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/PublishFullAddress.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/PublishFullAddress.java new file mode 100644 index 0000000..3f62897 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/PublishFullAddress.java @@ -0,0 +1,48 @@ +package org.onap.msb.apiroute.api; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class PublishFullAddress implements Serializable { + private static final long serialVersionUID = 1L; + + + private String ip; + + private String port; + + + private String publish_protocol; + + public String getPublish_protocol() { + return publish_protocol; + } + + public void setPublish_protocol(String publish_protocol) { + this.publish_protocol = publish_protocol; + } + + + 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 PublishFullAddress(){ + + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteInfo.java new file mode 100644 index 0000000..5e36b9d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteInfo.java @@ -0,0 +1,179 @@ +package org.onap.msb.apiroute.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class RouteInfo implements Serializable,Cloneable { + 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"; //lb policy + + @ApiModelProperty(required = true) + private RouteServer servers[]; + + private String host=""; + + private String namespace=""; + + private String publish_port=""; + + private boolean enable_ssl=false; //true:https:å¼€å¯SSL加密, false:http + + private String consulServiceName=""; + + private String publishProtocol="http"; + + + + public String getPublish_port() { + return publish_port; + } + public void setPublish_port(String publish_port) { + this.publish_port = publish_port; + } + + + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + +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.clone(); + } + + public void setServers(RouteServer[] servers) { + this.servers = servers.clone(); + } + + 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; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + public String getConsulServiceName() { + return consulServiceName; + } + public void setConsulServiceName(String consulServiceName) { + this.consulServiceName = consulServiceName; + } + + @Override + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } + public String getPublishProtocol() { + return publishProtocol; + } + public void setPublishProtocol(String publishProtocol) { + this.publishProtocol = publishProtocol; + } + public boolean isEnable_ssl() { + return enable_ssl; + } + public void setEnable_ssl(boolean enable_ssl) { + this.enable_ssl = enable_ssl; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RouteInfo that = (RouteInfo) o; + return Objects.equals(enable_ssl, that.enable_ssl) && + Objects.equals(serviceName, that.serviceName) && + Objects.equals(url, that.url) && + Objects.equals(control, that.control) && + Objects.equals(status, that.status) && + Objects.equals(visualRange, that.visualRange) && + Objects.equals(useOwnUpstream, that.useOwnUpstream) && + Arrays.equals(servers, that.servers) && + Objects.equals(host, that.host) && + Objects.equals(namespace, that.namespace) && + Objects.equals(publish_port, that.publish_port) && + Objects.equals(consulServiceName, that.consulServiceName) && + Objects.equals(publishProtocol, that.publishProtocol); + } + + @Override + public int hashCode() { + return Objects.hash(serviceName, url, control, status, visualRange, useOwnUpstream, servers, host, namespace, publish_port, enable_ssl, consulServiceName, publishProtocol); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteServer.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteServer.java new file mode 100644 index 0000000..310e623 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/RouteServer.java @@ -0,0 +1,83 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Objects; + + +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; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RouteServer that = (RouteServer) o; + return Objects.equals(weight, that.weight) && + Objects.equals(ip, that.ip) && + Objects.equals(port, that.port); + } + + @Override + public int hashCode() { + return Objects.hash(ip, port, weight); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedInternalServerErrorException.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedInternalServerErrorException.java new file mode 100644 index 0000000..de0d47a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedInternalServerErrorException.java @@ -0,0 +1,28 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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/onap/msb/apiroute/api/exception/ExtendedNotFoundException.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedNotFoundException.java new file mode 100644 index 0000000..34439d9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/ExtendedNotFoundException.java @@ -0,0 +1,28 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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/onap/msb/apiroute/api/exception/UnprocessableEntityException.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/UnprocessableEntityException.java new file mode 100644 index 0000000..28cfd17 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/api/exception/UnprocessableEntityException.java @@ -0,0 +1,18 @@ +package org.onap.msb.apiroute.api.exception; + +import javax.ws.rs.ClientErrorException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.http.HttpStatus; + +public class UnprocessableEntityException extends ClientErrorException{ + private static final long serialVersionUID = -8266622745725405656L; + + public UnprocessableEntityException(final String message) { + super(Response.status(HttpStatus.SC_UNPROCESSABLE_ENTITY).entity(message).type(MediaType.TEXT_PLAIN).build()); + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ApiRouteHealthCheck.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ApiRouteHealthCheck.java new file mode 100644 index 0000000..82b5f21 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ApiRouteHealthCheck.java @@ -0,0 +1,65 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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. + */ +/** +* 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.onap.msb.apiroute.health; + +import org.onap.msb.apiroute.api.DiscoverInfo; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +import org.onap.msb.apiroute.wrapper.util.HttpClientUtil; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; + +import com.codahale.metrics.health.HealthCheck; + +public class ApiRouteHealthCheck extends HealthCheck { + + + public ApiRouteHealthCheck() { + } + + @Override + protected Result check() throws Exception { + DiscoverInfo discoverInfo=ConfigUtil.getInstance().getDiscoverInfo(); + + String checkUrl = + (new StringBuilder().append("http://").append(discoverInfo.toString()) + .append(RouteUtil.MSB_CHECK_URL)).toString(); + + int resultStatus = HttpClientUtil.httpGetStatus(checkUrl); + + if(resultStatus==200){ + return Result.healthy(); + } + else{ + return Result.unhealthy("check consul fail:[status]"+resultStatus); + } + + + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ConsulLinkHealthCheck.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ConsulLinkHealthCheck.java new file mode 100644 index 0000000..916d2f8 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/ConsulLinkHealthCheck.java @@ -0,0 +1,132 @@ +package org.onap.msb.apiroute.health; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.ApiRouteApp; +import org.onap.msb.apiroute.wrapper.InitRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +import org.onap.msb.apiroute.wrapper.util.HttpClientUtil; +import org.onap.msb.apiroute.wrapper.util.HttpGetResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.codahale.metrics.health.HealthCheck; + +public class ConsulLinkHealthCheck extends HealthCheck implements Runnable { + + private static final Logger LOGGER = LoggerFactory + .getLogger(ConsulLinkHealthCheck.class); + + private final static String CONSUL_IP_ENV = ConfigUtil.getInstance().getConsul_ip(); + + private static int failedLoopCheckNum = 12; + private static int failedTimer = 5 * 1000; + + private static int normalTimer = 20 * 1000; + private static Result result = Result.healthy(); + + private String CHECK_IP = "127.0.0.1"; + private String CHECK_PORT = "8500"; + private String CHECK_URL = "http://" + CHECK_IP + ":" + CHECK_PORT + + "/v1/status/leader"; + + public static Result getResult() { + return result; + } + + @Override + protected Result check() { + // TODO Auto-generated method stub + + if (!StringUtils.isBlank(CONSUL_IP_ENV)) { + CHECK_IP = CONSUL_IP_ENV; + CHECK_URL = "http://" + CHECK_IP + ":" + CHECK_PORT + + "/v1/status/leader"; + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("check consul URL:" + CHECK_URL); + } + + try { + + HttpGetResult result = HttpClientUtil + .httpGetStatusAndBody(CHECK_URL); + + //response format:"127.0.0.1:8300" + if (result.getStatusCode() == 200 && result.getBody() != null + && result.getBody().contains(":8300")) { + return Result.healthy(); + } else { + return Result.unhealthy("check consul link " + CHECK_URL + + " fail:" + result.getStatusCode()+":"+result.getBody()); + } + + } catch (Exception e) { + LOGGER.warn( + "ConsulLinkHealthCheck:" + CHECK_URL + " execption", e); + return Result.unhealthy("check consul link " + CHECK_URL + + " exception:{}"); + } + + } + + return Result.healthy(); + } + + @Override + public void run() { + // TODO Auto-generated method stub + while (true) { + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("consul link check starttime:" + + System.currentTimeMillis()); + } + + result = checkWithPolicy(); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("consul link check result:" + result.isHealthy() + + " message:" + result.getMessage()); + + LOGGER.debug("consul link check endtime:" + + System.currentTimeMillis()); + } + + try { + Thread.sleep(normalTimer); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + LOGGER.warn("loop check consul,thread sleep excepiton", e); + } + } + } + + private Result checkWithPolicy() { + int failedNum = 0; + Result temp = Result.healthy(); + + do { + // check again + temp = check(); + + // healthy break; + if (temp.isHealthy()) { + break; + } + + // unhealthy go on + failedNum++; + + try { + Thread.sleep(failedTimer); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + LOGGER.warn("loop check consul,thread sleep excepiton", e); + } + + } while (failedNum <= failedLoopCheckNum); + + return temp; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/OpenRestyHealthCheck.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/OpenRestyHealthCheck.java new file mode 100644 index 0000000..fb0379f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/OpenRestyHealthCheck.java @@ -0,0 +1,39 @@ +package org.onap.msb.apiroute.health; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.wrapper.util.HttpClientUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.codahale.metrics.health.HealthCheck; + +public class OpenRestyHealthCheck extends HealthCheck { + private static final Logger LOGGER = LoggerFactory + .getLogger(OpenRestyHealthCheck.class); + private String CHECK_IP="127.0.0.1"; + private String CHECK_PORT="80"; + private String CHECK_URL = "http://"+CHECK_IP+":"+CHECK_PORT+"/api/microservices/v1/apiRoute/discoverInfo"; + + @Override + protected Result check() throws Exception { + // TODO Auto-generated method stub + + if(!StringUtils.isBlank(System.getenv("HTTP_OVERWRITE_PORT"))) + { + CHECK_PORT=System.getenv("HTTP_OVERWRITE_PORT"); + CHECK_URL = "http://"+CHECK_IP+":"+CHECK_PORT+"/api/microservices/v1/apiRoute/discoverInfo"; + LOGGER.info("check openresty URL:"+CHECK_URL); + } + + int resultStatus = HttpClientUtil.httpGetStatus(CHECK_URL); + + if (resultStatus == 200) { + return Result.healthy(); + } else { + return Result + .unhealthy("check openresty fail:" + resultStatus); + } + + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/RedisHealthCheck.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/RedisHealthCheck.java new file mode 100644 index 0000000..7cef3d0 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/health/RedisHealthCheck.java @@ -0,0 +1,160 @@ +package org.onap.msb.apiroute.health; + +import java.text.SimpleDateFormat; + +import org.onap.msb.apiroute.wrapper.util.JedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + +import com.codahale.metrics.health.HealthCheck; + +public class RedisHealthCheck extends HealthCheck implements Runnable { + private static final Logger LOGGER = LoggerFactory + .getLogger(RedisHealthCheck.class); + + public static boolean writeCheckFlag = true; + private static Result result = Result.healthy(); + + private static int failedLoopCheckNum = 12; + private static int failedTimer = 5 * 1000; + + private static int normalTimer = 20 * 1000; + + public static Result getResult() { + return result; + } + + @Override + protected Result check() { + + // check write + if (writeCheckFlag) { + Result writeCheckResult = checkWrite(); + if (writeCheckResult.isHealthy()) { + writeCheckFlag = false; + } + + // write failed + if (!writeCheckResult.isHealthy()) { + return writeCheckResult; + } + } + + // check read + Result readCheckResult = checkRead(); + + // read failed + if (!readCheckResult.isHealthy()) { + return readCheckResult; + } + + return Result.healthy(); + } + + private Result checkRead() { + Jedis jedisHandle = null; + + Result healthRst = Result.healthy(); + try { + + jedisHandle = JedisUtil.borrowJedisInstance(); + jedisHandle.get("healthchek:checktime"); + + } catch (Exception e) { + LOGGER.warn("RedisHealthCheck exception", e); + healthRst = Result.unhealthy(e); + } finally { + JedisUtil.returnJedisInstance(jedisHandle); + } + + return healthRst; + } + + private Result checkWrite() { + Jedis jedisHandle = null; + + Result healthRst = Result.healthy(); + try { + + long currentTime = System.currentTimeMillis(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String date = sdf.format(currentTime); + + jedisHandle = JedisUtil.borrowJedisInstance(); + String statusCode = jedisHandle.set("healthchek:checktime", date); + + if (statusCode != null && statusCode.equals("OK")) { + healthRst = Result.healthy("check redis:" + statusCode); + } else { + healthRst = Result.unhealthy("check redis:" + statusCode); + } + + } catch (Exception e) { + LOGGER.warn("RedisHealthCheck exception", e); + healthRst = Result.unhealthy(e); + } finally { + JedisUtil.returnJedisInstance(jedisHandle); + } + + return healthRst; + } + + @Override + public void run() { + // TODO Auto-generated method stub + while (true) { + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("redis check starttime:" + + System.currentTimeMillis()); + } + + result = checkWithPolicy(); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("redis check result:" + result.isHealthy() + + " message:" + result.getMessage()); + + LOGGER.debug("redis check endtime:" + + System.currentTimeMillis()); + } + + try { + Thread.sleep(normalTimer); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + LOGGER.warn("loop check redis,thread sleep excepiton", e); + } + } + } + + private Result checkWithPolicy() { + int failedNum = 0; + Result temp = Result.healthy(); + + do { + // check again + temp = check(); + + // healthy break; + if (temp.isHealthy()) { + break; + } + + // unhealthy go on + failedNum++; + + try { + Thread.sleep(failedTimer); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + LOGGER.warn("loop check redis,thread sleep excepiton", e); + } + + } while (failedNum <= failedLoopCheckNum); + + return temp; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/ApiRouteResource.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/ApiRouteResource.java new file mode 100644 index 0000000..e969e7a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/ApiRouteResource.java @@ -0,0 +1,214 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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 java.util.List; + +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.Response.ResponseBuilder; +import javax.ws.rs.core.UriInfo; + +import org.apache.http.HttpStatus; +import org.onap.msb.apiroute.api.ApiRouteInfo; +import org.onap.msb.apiroute.api.DiscoverInfo; +import org.onap.msb.apiroute.wrapper.ApiRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.CustomRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.IuiRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.util.CommonUtil; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +import org.onap.msb.apiroute.wrapper.util.JacksonJsonUtil; + +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 List getApiRoutes(@ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + return ApiRouteServiceWrapper.getInstance().getAllApiRouteInstances(routeWay); + } + + @POST + @Path("/") + @ApiOperation(value = "add one ApiRoute ", code = HttpStatus.SC_CREATED,response = ApiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + ApiRouteInfo new_apiRouteInfo = ApiRouteServiceWrapper.getInstance().saveApiRouteInstance4Rest(apiRouteInfo,routeWay); + 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_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "ApiRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "ApiRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + return ApiRouteServiceWrapper.getInstance().getApiRouteInstance(serviceName,version,host,publish_port,routeWay); + + } + + @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_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + ApiRouteInfo new_apiRouteInfo = ApiRouteServiceWrapper.getInstance().saveApiRouteInstance4Rest(apiRouteInfo,routeWay); + 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, + @ApiParam(value = "ApiRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "ApiRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + ApiRouteServiceWrapper.getInstance().deleteApiRoute(serviceName, version,host,publish_port,routeWay); + } + + + @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() { + + return ApiRouteServiceWrapper.getInstance().getAllApiDocs(); + } + + + @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 ConfigUtil.getInstance().getDiscoverInfo(); + } + + @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_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "ApiRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "ApiRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + ApiRouteInfo new_apiRouteInfo = ApiRouteServiceWrapper.getInstance().updateApiRouteStatus(serviceName,version,host,publish_port,status,routeWay); + 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(@ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) throws Exception { + + ResponseBuilder response = Response.ok( ApiRouteServiceWrapper.getInstance().getAllrouteByJson(routeWay)); + return response.header("Content-Disposition", "attachment; filename=\"RouteService.json\"").build(); + + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/CustomRouteResource.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/CustomRouteResource.java new file mode 100644 index 0000000..92b3408 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/CustomRouteResource.java @@ -0,0 +1,164 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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 java.util.List; + +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.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.onap.msb.apiroute.api.CustomRouteInfo; +import org.onap.msb.apiroute.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 List getCustomRoutes(@ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + return CustomRouteServiceWrapper.getInstance().getAllCustomRouteInstances(routeWay); + } + + @POST + @Path("/instance") + @ApiOperation(value = "add one CustomRoute ", code = HttpStatus.SC_CREATED,response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + CustomRouteInfo new_customRouteInfo = CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance4Rest(customRouteInfo,routeWay); + 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_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "CustomRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "CustomRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + + return CustomRouteServiceWrapper.getInstance().getCustomRouteInstance(serviceName,host,publish_port,routeWay); + + } + + @PUT + @Path("/instance") + @ApiOperation(value = "update one CustomRoute by serviceName", code = HttpStatus.SC_CREATED,response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay + ) { + + CustomRouteInfo new_customRouteInfo= CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance4Rest(customRoute,routeWay); + + 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, + @ApiParam(value = "CustomRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "CustomRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + CustomRouteServiceWrapper.getInstance().deleteCustomRoute(serviceName,host,publish_port,routeWay); + + } + + @PUT + @Path("/status") + @ApiOperation(value = "update one CustomRoute status by serviceName ",code = HttpStatus.SC_CREATED, response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNPROCESSABLE_ENTITY, 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 host", required = false) @QueryParam("host") String host, + @ApiParam(value = "CustomRoute status,1:abled 0:disabled", required = true) @QueryParam("status") String status, + @ApiParam(value = "CustomRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + CustomRouteInfo new_customRouteInfo = CustomRouteServiceWrapper.getInstance().updateCustomRouteStatus(serviceName,host,publish_port,status,routeWay); + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(new_customRouteInfo).build(); + + + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/IuiRouteResource.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/IuiRouteResource.java new file mode 100644 index 0000000..fea6645 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/IuiRouteResource.java @@ -0,0 +1,163 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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 java.util.List; + +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.http.HttpStatus; +import org.onap.msb.apiroute.api.IuiRouteInfo; +import org.onap.msb.apiroute.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 List getIuiRoutes(@ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + return IuiRouteServiceWrapper.getInstance().getAllIuiRouteInstances(routeWay); + } + + @POST + @Path("/") + @ApiOperation(value = "add one iuiRoute ", code = HttpStatus.SC_CREATED,response = IuiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + IuiRouteInfo new_iuiRouteInfo = IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance4Rest(iuiRouteInfo,routeWay); + 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_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "iuiRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "iuiRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + return IuiRouteServiceWrapper.getInstance().getIuiRouteInstance(serviceName,host,publish_port,routeWay); + + } + + @PUT + @Path("/{serviceName}") + @ApiOperation(value = "update one iuiRoute by serviceName", code = HttpStatus.SC_CREATED,response = IuiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNPROCESSABLE_ENTITY, 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, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + IuiRouteInfo new_iuiRouteInfo = IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance4Rest(iuiRouteInfo,routeWay); + 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, + @ApiParam(value = "iuiRoute host", required = false) @QueryParam("host") String host, + @ApiParam(value = "iuiRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(serviceName,host,publish_port,routeWay); + + } + + @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_UNPROCESSABLE_ENTITY, 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 host", required = false) @QueryParam("host") String host, + @ApiParam(value = "iuiRoute status,1:abled 0:disabled", required = true) @PathParam("status") String status, + @ApiParam(value = "iuiRoute Publish port", required = false) @QueryParam("publish_port") @DefaultValue("")String publish_port, + @ApiParam(value = "Route Way", required = false) @QueryParam("routeWay") @DefaultValue("ip")String routeWay) { + + IuiRouteInfo new_iuiRouteInfo = IuiRouteServiceWrapper.getInstance().updateIuiRouteStatus(serviceName,host,publish_port,status,routeWay); + 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/onap/msb/apiroute/resources/MicroServiceResource.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/MicroServiceResource.java new file mode 100644 index 0000000..5beb209 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/resources/MicroServiceResource.java @@ -0,0 +1,229 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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 java.util.List; + +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.http.HttpStatus; +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.api.exception.ExtendedInternalServerErrorException; +import org.onap.msb.apiroute.health.ConsulLinkHealthCheck; +import org.onap.msb.apiroute.health.RedisHealthCheck; +import org.onap.msb.apiroute.wrapper.MicroServiceWrapper; +import org.onap.msb.apiroute.wrapper.util.MicroServiceUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.codahale.metrics.annotation.Timed; +import com.codahale.metrics.health.HealthCheck.Result; + +@Path("/services") +// @Api(tags = {"MSB-Service Resource"}) +@Produces(MediaType.APPLICATION_JSON) +public class MicroServiceResource { + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceResource.class); + + @Context + UriInfo uriInfo; // actual uri info + + @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 List 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_UNPROCESSABLE_ENTITY, 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) MicroServiceFullInfo 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_UNPROCESSABLE_ENTITY, 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) { + + + return MicroServiceWrapper.getInstance().getMicroServiceInstance(serviceName, version); + + + } + + @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_UNPROCESSABLE_ENTITY, 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) MicroServiceFullInfo microServiceInfo, + @Context HttpServletRequest request) { + + String ip=MicroServiceUtil.getRealIp(request); + MicroServiceFullInfo microServiceFullInfo = MicroServiceWrapper.getInstance().saveMicroServiceInstance(microServiceInfo, + false,ip,""); + 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_UNPROCESSABLE_ENTITY, 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_UNPROCESSABLE_ENTITY, 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) { + + MicroServiceWrapper.getInstance().deleteMicroService(serviceName, version); + + } + + @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_UNPROCESSABLE_ENTITY, 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(); + + } + + @GET + @Path("/health") + @ApiOperation(value = "apigateway healthy check ", code = HttpStatus.SC_OK, response = String.class) + @ApiResponses(value = { @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "check fail", response = String.class) }) + @Produces(MediaType.TEXT_PLAIN) + @Timed + public Response health() { + + // redis + Result rst = RedisHealthCheck.getResult(); + if (!rst.isHealthy()) { + LOGGER.warn("health check failed:"+rst.getMessage()); + throw new ExtendedInternalServerErrorException(rst.getMessage()); + } + + //consul + rst = ConsulLinkHealthCheck.getResult(); + if (!rst.isHealthy()) { + LOGGER.warn("health check failed:"+rst.getMessage()); + throw new ExtendedInternalServerErrorException(rst.getMessage()); + } + + return Response.ok("apigateway healthy check:ok").build(); + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/ApiRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/ApiRouteServiceWrapper.java new file mode 100644 index 0000000..80dc607 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/ApiRouteServiceWrapper.java @@ -0,0 +1,273 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.List; + +import org.onap.msb.apiroute.api.ApiRouteInfo; +import org.onap.msb.apiroute.api.exception.ExtendedInternalServerErrorException; +import org.onap.msb.apiroute.api.exception.ExtendedNotFoundException; +import org.onap.msb.apiroute.wrapper.service.ApiRouteService; +import org.onap.msb.apiroute.wrapper.util.CommonUtil; +import org.onap.msb.apiroute.wrapper.util.FileUtil; +import org.onap.msb.apiroute.wrapper.util.JacksonJsonUtil; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +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; + } + + + public List getAllApiRouteInstances(String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + try { + String apiRedisKey=RouteUtil.getMutiRedisKey(RouteUtil.APIROUTE, routeWay); + + return ApiRouteService.getInstance().getMultiApiRouteInstances(apiRedisKey); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + } + + + + /** + * @Title: getApiRouteInstance + * @Description: TODO(Through the name + version for a single service object information) + * @param: @param serviceName + * @param: @param version + * @param: @return + * @return: ApiRouteInfo + */ + public ApiRouteInfo getApiRouteInstance(String serviceName, String version, String host,String publish_port,String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + if ("null".equals(version)) { + version = ""; + } + + RouteUtil.checkServiceNameAndVersion(serviceName,version); + + String apiRedisPrefixedKey=RouteUtil.getAPIRedisPrefixedKey(serviceName, version, host, publish_port, routeWay); + + ApiRouteInfo apiRouteInfo; + try { + apiRouteInfo = ApiRouteService.getInstance().getApiRouteInstance(apiRedisPrefixedKey); + } catch (Exception e) { + LOGGER.error("get ApiRouteInstance throw exception", e); + throw new ExtendedInternalServerErrorException("get ApiRouteInstance throw exception" + e.getMessage()); + } + + + + if (null == apiRouteInfo) { + throw new ExtendedNotFoundException("Api RouteInfo not found"); + } + + return apiRouteInfo; + + } + + + + /** + * @Title updateApiRouteStatus + * @Description TODO(update ApiRoute Status) + * @param serviceName + * @param version + * @param status + * @return + * @return RouteResult + */ + public synchronized ApiRouteInfo updateApiRouteStatus(String serviceName, String version,String host,String publish_port, + String status,String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + if ("null".equals(version)) { + version = ""; + } + + RouteUtil.checkServiceNameAndVersion(serviceName,version); + + RouteUtil.checkServiceStatus(status); + + + String apiRedisPrefixedKey=RouteUtil.getAPIRedisPrefixedKey(serviceName, version, host, publish_port, routeWay); + + try { + ApiRouteService.getInstance().updateApiRouteStatus2Redis(apiRedisPrefixedKey, status); + } catch (Exception e) { + LOGGER.error("update ApiRoute status throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + ApiRouteInfo new_apiRouteInfo = getApiRouteInstance(serviceName, version,host,publish_port,routeWay); + return new_apiRouteInfo; + } + + + /** + * @Title: saveApiRouteInstance + * @Description: TODO(save ApiRouteInstance) + * @param: @param apiRouteInfo + * @param: @return + * @return: ApiRouteInfo + */ + public synchronized ApiRouteInfo saveApiRouteInstance4Rest(ApiRouteInfo apiRouteInfo,String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + RouteUtil.checkRouterInfoFormat(apiRouteInfo); + + try { + saveApiRouteInstance(apiRouteInfo,routeWay); + } catch (Exception e) { + throw new ExtendedInternalServerErrorException("save apiRouteInfo fail: [serviceName]"+apiRouteInfo.getServiceName()+"[version]"+apiRouteInfo.getVersion()+" [routeWay]"+routeWay+e.getMessage()); + } + + return apiRouteInfo; + } + + + + public synchronized void saveApiRouteInstance(ApiRouteInfo apiRouteInfo,String routeWay) throws Exception { + try { + String apiRedisPrefixedKey=RouteUtil.getAPIRedisPrefixedKey(apiRouteInfo.getServiceName(), apiRouteInfo.getVersion(), apiRouteInfo.getHost(), apiRouteInfo.getPublish_port(), routeWay); + + ApiRouteService.getInstance().saveApiRouteService2Redis(apiRouteInfo, apiRedisPrefixedKey); + LOGGER.info("save apiRouteInfo [serviceName]"+apiRouteInfo.getServiceName()+"[version]"+apiRouteInfo.getVersion()+" [routeWay]"+routeWay+" success"); + } catch (Exception e) { + LOGGER.error("save apiRouteInfo [serviceName]"+apiRouteInfo.getServiceName()+"[version]"+apiRouteInfo.getVersion()+" [routeWay]"+routeWay+" throw exception", e); + throw e; + } + + + } + + + + /** + * @Title: deleteApiRoute + * @Description: TODO(delete one ApiRoute) + * @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 host,String publish_port,String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + if ("null".equals(version)) { + version = ""; + } + + RouteUtil.checkServiceNameAndVersion(serviceName,version); + + String apiRedisPrefixedKey=RouteUtil.getAPIRedisPrefixedKey(serviceName, version, host, publish_port, routeWay); + + + try { + ApiRouteService.getInstance() + .deleteApiRouteService2Redis(apiRedisPrefixedKey); + LOGGER.info("delete apiRouteInfo [serviceName]"+serviceName+"[version]"+version+" [host]"+host +" [publish_port]"+publish_port+" [routeWay]"+routeWay+" success"); + + } + catch (ExtendedNotFoundException e) { + throw e; + }catch (Exception e) { + LOGGER.error("delete apiRouteInfo [serviceName]"+serviceName+"[version]"+version+" [host]"+host +" [publish_port]"+publish_port+" [routeWay]"+routeWay+" throw exception", e); + + throw new ExtendedInternalServerErrorException("delete apiRouteInfo [serviceName]"+serviceName+"[version]"+version+e.getMessage()); + } + + + } + + + /** + * @Title: getAllApiDocs + * @Description: TODO(For local ext\initSwaggerJson directory of all the json file directory) + * @param: @return + * @return: String[] + */ + public String[] getAllApiDocs() { + URL apiDocsPath = ApiRouteServiceWrapper.class.getResource("/ext/initSwaggerJson"); + if (apiDocsPath != null) { + String path = apiDocsPath.getPath(); + + try { + return FileUtil.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 getAllrouteByJson(String routeWay){ + + Object[] apirouteArray= ApiRouteServiceWrapper.getInstance().getAllApiRouteInstances(routeWay).toArray(); + Object[] iuirouteArray= IuiRouteServiceWrapper.getInstance().getAllIuiRouteInstances(routeWay).toArray(); + Object[] customrouteArray= CustomRouteServiceWrapper.getInstance().getAllCustomRouteInstances(routeWay).toArray(); + + Object[] temprouteArray =CommonUtil.concat(apirouteArray, iuirouteArray); + Object[] allrouteArray=CommonUtil.concat(temprouteArray, customrouteArray); + + + String allrouteJson; + try { + allrouteJson = JacksonJsonUtil.beanToJson(allrouteArray); + } catch (Exception e) { + LOGGER.error("exportService beanToJson throw Exception", e); + throw new ExtendedInternalServerErrorException("exportService beanToJson throw Exception:"+ e.getMessage()); + } + return allrouteJson; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/CustomRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/CustomRouteServiceWrapper.java new file mode 100644 index 0000000..50747b6 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/CustomRouteServiceWrapper.java @@ -0,0 +1,217 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper; + +import java.util.List; + +import org.onap.msb.apiroute.api.CustomRouteInfo; +import org.onap.msb.apiroute.api.exception.ExtendedInternalServerErrorException; +import org.onap.msb.apiroute.api.exception.ExtendedNotFoundException; +import org.onap.msb.apiroute.wrapper.service.CustomRouteService; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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(get AllCustomRoute Service) + * @param: @return + * @return: CustomRouteInfo[] + */ + public List getAllCustomRouteInstances(String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + try { + String customRedisKey = RouteUtil.getMutiRedisKey(RouteUtil.CUSTOMROUTE, routeWay); + return CustomRouteService.getInstance().getMultiCustomRouteInstances(customRedisKey); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + + } + + + + /** + * @Title: getCustomRouteInstance + * @Description: TODO(get CustomRouteInstance by serviceName) + * @param: @param serviceName + * @param: @return + * @return: CustomRouteInfo + */ + public CustomRouteInfo getCustomRouteInstance(String serviceName, String host, + String publish_port, String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + String customRedisPrefixedKey = + RouteUtil.getRedisPrefixedKey(RouteUtil.CUSTOMROUTE, serviceName, host, publish_port, + routeWay); + + CustomRouteInfo customRouteInfo; + try { + customRouteInfo = + CustomRouteService.getInstance().getCustomRouteInstance(customRedisPrefixedKey); + } catch (Exception e) { + LOGGER.error("get customRouteInstance throw exception", e); + throw new ExtendedInternalServerErrorException("get customRouteInstance throw exception"+ e.getMessage()); + } + + + if (null == customRouteInfo) { + throw new ExtendedNotFoundException("customRoute Info not found"); + + } + + return customRouteInfo; + + } + + + /** + * @Title updateCustomRouteStatus + * @Description TODO(update one CustomRoute Status) + * @param serviceName + * @param status + * @return + * @return RouteResult + */ + public synchronized CustomRouteInfo updateCustomRouteStatus(String serviceName, String host, + String publish_port, String status, String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + RouteUtil.checkServiceStatus(status); + + String customRedisPrefixedKey = + RouteUtil.getRedisPrefixedKey(RouteUtil.CUSTOMROUTE, serviceName, host, publish_port, + routeWay); + + + try { + CustomRouteService.getInstance() + .updateCustomRouteStatus2Redis(customRedisPrefixedKey, status); + } catch (Exception e) { + LOGGER.error("update CustomRoute status throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + CustomRouteInfo new_customRouteInfo = + getCustomRouteInstance(serviceName, host, publish_port, routeWay); + + return new_customRouteInfo; + } + + /** + * @Title: saveCustomRouteInstance + * @Description: TODO(save one CustomRouteInstance) + * @param: @param CustomRouteInfo + * @param: @return + * @return: CustomRouteInfo + */ + public synchronized CustomRouteInfo saveCustomRouteInstance4Rest(CustomRouteInfo customRouteInfo, + String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + RouteUtil.checkRouterInfoFormat(customRouteInfo); + + try { + saveCustomRouteInstance(customRouteInfo, routeWay); + + } catch (Exception e) { + + throw new ExtendedInternalServerErrorException("save CustomRouteInfo fail: [serviceName]"+customRouteInfo.getServiceName()+e.getMessage()); + } + + return customRouteInfo; + + } + + + public synchronized CustomRouteInfo saveCustomRouteInstance(CustomRouteInfo customRouteInfo, + String routeWay) throws Exception { + try { + String customRedisPrefixedKey = + RouteUtil.getRedisPrefixedKey(RouteUtil.CUSTOMROUTE, customRouteInfo.getServiceName(), + customRouteInfo.getHost(), customRouteInfo.getPublish_port(), routeWay);; + + + CustomRouteService.getInstance().saveCustomRouteService2Redis(customRouteInfo, + customRedisPrefixedKey); + LOGGER.info("save CustomRouteInfo [serviceName]"+customRouteInfo.getServiceName()+" [host]"+customRouteInfo.getHost() +" [publish_port]"+customRouteInfo.getPublish_port()+" [routeWay]"+routeWay+" success"); + + } catch (Exception e) { + LOGGER.error("save CustomRouteInfo [serviceName]"+customRouteInfo.getServiceName()+" [host]"+customRouteInfo.getHost() +" [publish_port]"+customRouteInfo.getPublish_port()+" [routeWay]"+routeWay+" throw exception", e); + + throw e; + } + + return customRouteInfo; + + } + + + /** + * @Title: deleteCustomRoute + * @Description: TODO(delete one CustomRoute) + * @param: @param type + * @param: @param serviceName + * @param: @param delKey + * @param: @return + * @return: void + */ + public synchronized void deleteCustomRoute(String serviceName, String host, String publish_port, + String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + String customRedisPrefixedKey = + RouteUtil.getRedisPrefixedKey(RouteUtil.CUSTOMROUTE, serviceName, host, publish_port, + routeWay); + + try { + CustomRouteService.getInstance().deleteCustomRouteService2Redis(customRedisPrefixedKey); + LOGGER.info("delete CustomRouteInfo [serviceName]"+serviceName+" [host]"+host +" [publish_port]"+publish_port+" [routeWay]"+routeWay+" success"); + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + LOGGER.error("delete CustomRouteInfo [serviceName]"+serviceName+" [host]"+host +" [publish_port]"+publish_port+" [routeWay]"+routeWay+" throw exception", e); + throw new ExtendedInternalServerErrorException("delete CustomRouteInfo [serviceName]"+serviceName+e.getMessage()); + } + + + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/InitRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/InitRouteServiceWrapper.java new file mode 100644 index 0000000..76be3d2 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/InitRouteServiceWrapper.java @@ -0,0 +1,416 @@ +package org.onap.msb.apiroute.wrapper; + +import io.dropwizard.jetty.HttpConnectorFactory; +import io.dropwizard.server.SimpleServerFactory; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.URL; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.ApiRouteApp; +import org.onap.msb.apiroute.ApiRouteAppConfig; +import org.onap.msb.apiroute.SyncDataManager; +import org.onap.msb.apiroute.api.ApiRouteInfo; +import org.onap.msb.apiroute.api.CustomRouteInfo; +import org.onap.msb.apiroute.api.DiscoverInfo; +import org.onap.msb.apiroute.api.IuiRouteInfo; +import org.onap.msb.apiroute.api.RouteServer; +import org.onap.msb.apiroute.api.exception.ExtendedNotFoundException; +import org.onap.msb.apiroute.health.ConsulLinkHealthCheck; +import org.onap.msb.apiroute.health.RedisHealthCheck; +import org.onap.msb.apiroute.wrapper.serviceListener.MicroServiceChangeListener; +import org.onap.msb.apiroute.wrapper.serviceListener.RouteNotify; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +import org.onap.msb.apiroute.wrapper.util.FileUtil; +import org.onap.msb.apiroute.wrapper.util.JacksonJsonUtil; +import org.onap.msb.apiroute.wrapper.util.JedisUtil; +import org.onap.msb.apiroute.wrapper.util.RegExpTestUtil; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class InitRouteServiceWrapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(InitRouteServiceWrapper.class); + + + private static InitRouteServiceWrapper instance = new InitRouteServiceWrapper(); + + private InitRouteServiceWrapper() {} + + public static InitRouteServiceWrapper getInstance() { + return instance; + } + + + /** + * The listener registration service changes + */ + public void registerServiceChangeListener() { + + RouteNotify.getInstance().addServiceChangeListener(new MicroServiceChangeListener()); + + } + + public void initFilterConfig(){ + //route init + ConfigUtil.getInstance().initRouteWay(); + ConfigUtil.getInstance().initApiGatewayPort(); + ConfigUtil.getInstance().initRouteNameSpaceMatches(); + ConfigUtil.getInstance().initRouteLabelsMatches(); + ConfigUtil.getInstance().initNodeMetaQueryParam(); + + } + + public void initDataSynchro(){ + + registerServiceChangeListener(); + + boolean ifRedisConnect=startCheckRedisConnect(); + + if(ifRedisConnect){ + initRouteInfoFromJson(); + runConsulClientApp(); + } + } + + public void initHealthCheck() + { + LOGGER.info("start check consul link thread"); + Thread tConsul= new Thread(new ConsulLinkHealthCheck(),"_healthcheck_consul_"); + tConsul.setDaemon(true); + tConsul.start(); + + LOGGER.info("start check redis thread"); + Thread tRedies= new Thread(new RedisHealthCheck(),"_healthcheck_redis_"); + tRedies.setDaemon(true); + tRedies.start(); + } + + + + + public boolean startCheckRedisConnect() { + + int n = 0; + while (true) { + if (!checkRedisConnect()) { + n++; + LOGGER.warn(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) { + LOGGER.error("Initial Route Configuration——redis connection fail,timeout exit..."); + return false; + } + } else { + LOGGER.warn(" Initial Route Configuration——redis connection success..."); + return true; + } + } + + + } + + + // Open the consul to monitor subscription service + public void runConsulClientApp() { + + String consulIP; + int consulPort; + String consulConfSource="Default"; + + + DiscoverInfo discoverInfo = ConfigUtil.getInstance().getDiscoverInfo(); + + if (discoverInfo.isEnabled()) { + LOGGER.warn("starting to initial consul Configuration"); + String[] routeWay = ConfigUtil.getInstance().getRouteWay(); + try { + String sys_consulIp=ConfigUtil.getInstance().getConsul_ip(); + if (StringUtils.isNotBlank(sys_consulIp)) { + consulIP = sys_consulIp.trim(); + consulPort = RouteUtil.consulDeafultPort; + consulConfSource="env:CONSUL_IP"; + } else { + consulIP = discoverInfo.getIp(); + consulPort = discoverInfo.getPort(); + consulConfSource="init discoverInfo"; + } + + LOGGER.warn("init consul sync Address from [ "+consulConfSource+" ]:" + consulIP + ":" + consulPort); + + // 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"); + discoverApiService.setHost("msb"); + + RouteServer[] servers = new RouteServer[1]; + servers[0] = new RouteServer(discoverInfo.getIp(), String.valueOf(discoverInfo.getPort())); + discoverApiService.setServers(servers); + + + for (int i = 0; i < routeWay.length; i++) { + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance4Rest(discoverApiService, + routeWay[i]); + } + + + + // iui + IuiRouteInfo discoverIUIService = new IuiRouteInfo(); + discoverIUIService.setServiceName("msdiscover"); + discoverIUIService.setUrl("/iui/microservices"); + discoverIUIService.setHost("msb"); + discoverIUIService.setServers(servers); + + for (int i = 0; i < routeWay.length; i++) { + IuiRouteServiceWrapper.getInstance() + .saveIuiRouteInstance(discoverIUIService, routeWay[i]); + } + + + /* + * ConsulClientApp consulClientApp = new ConsulClientApp(consulIP, consulPort); + * consulClientApp.startServiceListen(); + */ + + // SyncDataManager syncDataManager = new SyncDataManager(); + // Monitor serviceList change + SyncDataManager.initSyncTask(consulIP, consulPort); + + + LOGGER.warn("start monitor consul service--" + consulIP + ":" + consulPort); + } catch (Exception e) { + LOGGER.error("start monitor consul service fail:" + e.getMessage()); + } + } + + + + } + + + + /** + * @Title: initRouteInfoFromJson + * @Description: TODO(According to the JSON file configuration initialization route data) + * @return: void + */ + public void initRouteInfoFromJson() { + LOGGER.info("starting to initial Route Configuration"); + URL apiDocsPath = InitRouteServiceWrapper.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) { + + String[] routeWay = ConfigUtil.getInstance().getRouteWay(); + String iuiRootPath = ConfigUtil.getInstance().getIUI_ROOT_PATH(); + + try { + List routeList = + JacksonJsonUtil.jsonToListBean(fileContent, new TypeReference>() {}); + for (ApiRouteInfo route : routeList) { + String url = route.getUrl(); + + if (RegExpTestUtil.urlRegExpTest(route.getServiceName())) { + + for (int i = 0; i < routeWay.length; i++) { + try { + + CustomRouteServiceWrapper.getInstance().getCustomRouteInstance( + route.getServiceName(), route.getHost(), "", routeWay[i]); + + } 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()); + customRouteInfo.setHost(route.getHost()); + + + + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfo, + routeWay[i]); + + } + } + } else { + + if (RegExpTestUtil.apiRouteUrlRegExpTest(url) || url.startsWith("/api/microservices/") || url.startsWith("/admin/microservices/")) { + + for (int i = 0; i < routeWay.length; i++) { + try { + + ApiRouteServiceWrapper.getInstance().getApiRouteInstance(route.getServiceName(), + route.getVersion(), route.getHost(), route.getPublish_port(), routeWay[i]); + + } catch (ExtendedNotFoundException e) { + LOGGER.info("initapiRoute: ServiceName--" + route.getServiceName()); + + if (url.startsWith("/api/microservices")) { + if (StringUtils.isNotBlank(System.getenv("dwApp_server_connector_port"))) { + replaceApigatewayPort(route.getServers(), + System.getenv("dwApp_server_connector_port")); + } + } + + if (url.startsWith("/admin/microservices")) { + replaceApigatewayPort(route.getServers(),ConfigUtil.getInstance().getServerPort()); + } + + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance4Rest(route, routeWay[i]); + + } + } + + + } else if (RegExpTestUtil.iuiRouteUrlRegExpTest(url) || url.equals("/iui/microservices")) { + + for (int i = 0; i < routeWay.length; i++) { + try { + + IuiRouteServiceWrapper.getInstance().getIuiRouteInstance(route.getServiceName(), + route.getHost(), "", routeWay[i]); + + } 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()); + iuiRouteInfo.setHost(route.getHost()); + + + if (url.equals("/iui/microservices")) { + iuiRouteInfo.setUrl("/" + iuiRootPath + "/microservices"); + if (StringUtils.isNotBlank(System.getenv("dwApp_server_connector_port"))) { + replaceApigatewayPort(iuiRouteInfo.getServers(), + System.getenv("dwApp_server_connector_port")); + } + } else { + iuiRouteInfo.setUrl(route.getUrl()); + } + + IuiRouteServiceWrapper.getInstance() + .saveIuiRouteInstance(iuiRouteInfo, routeWay[i]); + + + } + } + + } 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); + } + + } + + private void replaceApigatewayPort(RouteServer[] servers, String apigatewayPort) { + for (int i = 0; i < servers.length; i++) { + servers[i].setPort(apigatewayPort); + } + } + + public void initMetricsConfig(ApiRouteAppConfig configuration) { + + SimpleServerFactory simpleServerFactory = + (SimpleServerFactory) configuration.getServerFactory(); + HttpConnectorFactory httpConnectorFactory = + (HttpConnectorFactory) simpleServerFactory.getConnector(); + String metricsUrl = + "http://127.0.0.1:" + httpConnectorFactory.getPort() + + simpleServerFactory.getAdminContextPath() + "/metrics"; + ConfigUtil.getInstance().setMetricsUrl(metricsUrl); + } + + + private boolean checkRedisConnect() { + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + return true; + } catch (Exception e) { + LOGGER.error("checkRedisConnect call redis throw exception", e); + }finally { + JedisUtil.returnJedisInstance(jedis); + } + + return false; + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/IuiRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/IuiRouteServiceWrapper.java new file mode 100644 index 0000000..d66de21 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/IuiRouteServiceWrapper.java @@ -0,0 +1,221 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper; + +import java.util.List; + +import org.onap.msb.apiroute.api.IuiRouteInfo; +import org.onap.msb.apiroute.api.exception.ExtendedInternalServerErrorException; +import org.onap.msb.apiroute.api.exception.ExtendedNotFoundException; +import org.onap.msb.apiroute.wrapper.service.IuiRouteService; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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(get All IuiRouteServices) + * @param: @return + * @return: IuiRouteInfo[] + */ + public List getAllIuiRouteInstances(String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + try { + String iuiRedisKey = RouteUtil.getMutiRedisKey(RouteUtil.IUIROUTE, routeWay); + + return IuiRouteService.getInstance().getMultiIuiRouteInstances(iuiRedisKey); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + + } + + + + /** + * @Title: getIuiRouteInstance + * @Description: TODO(get one IuiRouteInstance by serviceName) + * @param: @param serviceName + * @param: @return + * @return: IuiRouteInfo + */ + public IuiRouteInfo getIuiRouteInstance(String serviceName, String host, String publish_port, + String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + String iuiRedisPrefixedKey = + RouteUtil + .getRedisPrefixedKey(RouteUtil.IUIROUTE, serviceName, host, publish_port, routeWay); + + IuiRouteInfo iuiRouteInfo; + try { + iuiRouteInfo = IuiRouteService.getInstance().getIuiRouteInstance(iuiRedisPrefixedKey); + } catch (Exception e) { + LOGGER.error("get IuiRouteInstance throw exception", e); + throw new ExtendedInternalServerErrorException("get IuiRouteInstance throw exception" + + e.getMessage()); + } + + + + if (null == iuiRouteInfo) { + throw new ExtendedNotFoundException("iui RouteInfo not found"); + } + + return iuiRouteInfo; + } + + + + /** + * @Title updateIuiRouteStatus + * @Description TODO(update one IuiRoute Status) + * @param serviceName + * @param status + * @return + * @return RouteResult + */ + public synchronized IuiRouteInfo updateIuiRouteStatus(String serviceName, String host, + String publish_port, String status, String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + RouteUtil.checkServiceStatus(status); + + try { + String iuiRedisPrefixedKey = + RouteUtil.getRedisPrefixedKey(RouteUtil.IUIROUTE, serviceName, host, publish_port, + routeWay); + + IuiRouteService.getInstance().updateIuiRouteStatus2Redis(iuiRedisPrefixedKey, status); + + + } catch (Exception e) { + LOGGER.error("update IuiRoute status throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + IuiRouteInfo new_iuiRouteInfo = getIuiRouteInstance(serviceName, host, publish_port, routeWay); + + return new_iuiRouteInfo; + } + + /** + * @Title: saveIuiRouteInstance + * @Description: TODO(save one IuiRouteInstance) + * @param: @param IuiRouteInfo + * @param: @return + * @return: IuiRouteInfo + */ + public synchronized IuiRouteInfo saveIuiRouteInstance4Rest(IuiRouteInfo iuiRouteInfo, + String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + RouteUtil.checkRouterInfoFormat(iuiRouteInfo); + + + try { + saveIuiRouteInstance(iuiRouteInfo, routeWay); + } catch (Exception e) { + throw new ExtendedInternalServerErrorException("save iuiRouteInfo fail: [serviceName]" + + iuiRouteInfo.getServiceName() + e.getMessage()); + } + + return iuiRouteInfo; + } + + + public synchronized void saveIuiRouteInstance(IuiRouteInfo iuiRouteInfo, String routeWay) + throws Exception { + try { + String iuiRedisPrefixedKey = + RouteUtil.getRedisPrefixedKey(RouteUtil.IUIROUTE, iuiRouteInfo.getServiceName(), + iuiRouteInfo.getHost(), iuiRouteInfo.getPublish_port(), routeWay); + + IuiRouteService.getInstance().saveIuiRouteService2Redis(iuiRouteInfo, iuiRedisPrefixedKey); + LOGGER.info("save iuiRouteInfo [serviceName]" + iuiRouteInfo.getServiceName() + " [host]" + + iuiRouteInfo.getHost() + " [publish_port]" + iuiRouteInfo.getPublish_port() + + " [routeWay]" + routeWay + " success"); + + } catch (Exception e) { + LOGGER.error("save iuiRouteInfo [serviceName]" + iuiRouteInfo.getServiceName() + " [host]" + + iuiRouteInfo.getHost() + " [publish_port]" + iuiRouteInfo.getPublish_port() + + " [routeWay]" + routeWay + " throw exception", e); + throw e; + } + } + + + + /** + * @Title: deleteIuiRoute + * @Description: TODO(delete one IuiRoute) + * @param: @param type + * @param: @param serviceName + * @param: @param delKey + * @param: @return + * @return: void + */ + public synchronized void deleteIuiRoute(String serviceName, String host, String publish_port, + String routeWay) { + + RouteUtil.checkRouteWay(routeWay); + + String iuiRedisPrefixedKey = + RouteUtil + .getRedisPrefixedKey(RouteUtil.IUIROUTE, serviceName, host, publish_port, routeWay); + + try { + IuiRouteService.getInstance().deleteIuiRouteService2Redis(iuiRedisPrefixedKey); + LOGGER.info("delete iuiRouteInfo [serviceName]" + serviceName + " [host]" + host + + " [publish_port]" + publish_port + " [routeWay]" + routeWay + " success"); + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + LOGGER.error("delete iuiRouteInfo [serviceName]" + serviceName + " [host]" + host + + " [publish_port]" + publish_port + " [routeWay]" + routeWay + " throw exception", e); + throw new ExtendedInternalServerErrorException("delete iuiRouteInfo [serviceName]" + serviceName +e.getMessage()); + } + + + + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/MicroServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/MicroServiceWrapper.java new file mode 100644 index 0000000..a30151e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/MicroServiceWrapper.java @@ -0,0 +1,398 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.api.Node; +import org.onap.msb.apiroute.api.exception.ExtendedInternalServerErrorException; +import org.onap.msb.apiroute.api.exception.ExtendedNotFoundException; +import org.onap.msb.apiroute.api.exception.UnprocessableEntityException; +import org.onap.msb.apiroute.wrapper.dao.RedisAccessWrapper; +import org.onap.msb.apiroute.wrapper.service.MicroServiceFullService; +import org.onap.msb.apiroute.wrapper.serviceListener.RouteNotify; +import org.onap.msb.apiroute.wrapper.util.MicroServiceUtil; +import org.onap.msb.apiroute.wrapper.util.RegExpTestUtil; +import org.onap.msb.apiroute.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 List getAllMicroServiceInstances() { + + try { + return MicroServiceFullService.getInstance().getAllMicroServiceInstances(); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + } + + public Set getAllMicroServiceKey() { + int failedNum = 0; + int retryCount=3; + int failedTimer = 5 * 1000; + + Set serviceKeys=null; + + do { + + try { + serviceKeys= MicroServiceFullService.getInstance().getAllMicroServiceKey(); + break; + + } catch (Exception e) { + + LOGGER.error(failedNum + "/"+retryCount+" : get AllMicroServiceKey fail"+e); + failedNum++; + + try { + Thread.sleep(failedTimer); + } catch (InterruptedException ex) { + LOGGER.warn("get AllMicroServiceKey Thread.sleep throw except:" + ex.getMessage()); + } + } + + }while (failedNum <= retryCount); + + + return serviceKeys; + + } + + + + + /** + * @Title: getMicroServiceInstance + * @Description: (getMicroServiceInstance) + * @param: @param serviceName + * @param: @param version + * @param: @return + * @return: ApiRouteInfo + */ + public MicroServiceFullInfo getMicroServiceInstance(String serviceName, String version) { + if ("null".equals(version)) { + version = ""; + } + serviceName = serviceName.replace("*", "/"); + + RouteUtil.checkServiceNameAndVersion(serviceName, version); + + MicroServiceFullInfo microServiceInfo; + try { + microServiceInfo = + MicroServiceFullService.getInstance().getMicroServiceInstance(serviceName, version); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + if (null == microServiceInfo) { + String errInfo = "microservice not found: serviceName-" + serviceName + ",version-" + version; + throw new ExtendedNotFoundException(errInfo); + + } + + return microServiceInfo; + } + + + + /** + * @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("*", "/"); + + RouteUtil.checkServiceNameAndVersion(serviceName, version); + + RouteUtil.checkServiceStatus(status); + + try { + + MicroServiceFullService.getInstance().updateMicroServiceStatus(serviceName, version, status); + + MicroServiceFullInfo newMicroServiceInfo = + MicroServiceFullService.getInstance().getMicroServiceInstance(serviceName, version); + + // Notify the listeners + RouteNotify.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( + MicroServiceFullInfo microServiceInfo, boolean createOrUpdate, String requestIP, + String serverPort) { + + RouteUtil.checkMicroServiceInfoFormat(microServiceInfo, requestIP); + + try { + + if (createOrUpdate == false) { + deleteServiceAndnoticeRoute(microServiceInfo); + } + + saveServiceAndnoticeRoute(microServiceInfo); + + + MicroServiceFullInfo newMicroServiceInfo = + MicroServiceFullService.getInstance().getMicroServiceInstance( + microServiceInfo.getServiceName(), microServiceInfo.getVersion()); + + + return newMicroServiceInfo; + + } catch (UnprocessableEntityException e) { + throw e; + } catch (Exception e) { + throw new ExtendedInternalServerErrorException("save MicroServiceInfo fail :[serviceName]" + microServiceInfo.getServiceName() + + "[version]" + microServiceInfo.getVersion()+ e.getMessage()); + } + + } + + + public synchronized void deleteMicroService4AllVersion(String serviceName) { + try { + + List microServiceList4AllVersion = + MicroServiceFullService.getInstance().getAllVersionsOfTheService(serviceName); + + if (microServiceList4AllVersion.size() == 0) { + LOGGER.info("delete MicroServiceInfo for All Version Fail:serviceName-" + serviceName + + " not fond"); + } else { + for (MicroServiceFullInfo microServiceInfo : microServiceList4AllVersion) { + deleteServiceAndnoticeRoute(microServiceInfo); + } + } + + } catch (Exception e) { + LOGGER.error("delete MicroServiceInfo for all version :serviceName-" + serviceName +" throw exception", e); + + } + } + + + public synchronized void deleteMicroService(String serviceName, String version) { + if ("null".equals(version)) { + version = ""; + } + serviceName = serviceName.replace("*", "/"); + + RouteUtil.checkServiceNameAndVersion(serviceName, version); + + try { + + MicroServiceFullInfo microServiceInfo = + MicroServiceFullService.getInstance().getMicroServiceInstance(serviceName, version); + + if (microServiceInfo == null) { + LOGGER.error("delete MicroServiceInfo FAIL:serviceName-" + serviceName + ",version-"+ version + " not fond "); + } else { + + deleteServiceAndnoticeRoute(microServiceInfo); + } + + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + + throw new ExtendedInternalServerErrorException("delete MicroServiceInfo serviceName-" + serviceName + ",version-" + version+e.getMessage()); + + } + + + } + + public synchronized void deleteMicroServiceInstance(String serviceName, String version, + String ip, String port) { + if ("null".equals(version)) { + version = ""; + } + serviceName = serviceName.replace("*", "/"); + + RouteUtil.checkServiceNameAndVersion(serviceName, version); + + if (!RegExpTestUtil.ipRegExpTest(ip)) { + throw new UnprocessableEntityException("delete MicroServiceInfo FAIL:IP(" + ip+ ")is not a valid IP address"); + } + + if (!RegExpTestUtil.portRegExpTest(port)) { + throw new UnprocessableEntityException("delete MicroServiceInfo FAIL:Port(" + port + ")is not a valid Port address"); + } + + + try { + MicroServiceFullInfo microServiceInfo = + MicroServiceFullService.getInstance().getMicroServiceInstance(serviceName, version); + + if (microServiceInfo == null) { + throw new UnprocessableEntityException("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 + deleteServiceAndnoticeRoute(microServiceInfo); + } else { + // delete Node + MicroServiceFullService.getInstance().saveMicroServiceInfo2Redis(microServiceInfo); + RouteNotify.getInstance().noticeRouteListener4Update(serviceName, version, + microServiceInfo); + } + + break; + } + } + + if (!ifFindBNode) { + throw new ExtendedNotFoundException("delete MicroServiceInfo FAIL:serviceName-"+ serviceName + ",version-" + version +",node-" + ip + ":" + port + " not fond "); + } + + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + throw new ExtendedInternalServerErrorException("delete MicroServiceInfo :serviceName-"+ serviceName + ",version-" + version+",node-" + ip + ":" + port +"throw exception"+e.getMessage()); + + } + + } + + + public void deleteServiceAndnoticeRoute(MicroServiceFullInfo service) throws Exception { + + try { + // Delete the redis record + MicroServiceFullService.getInstance().deleteMicroService(service.getServiceName(), + service.getVersion()); + LOGGER.info("delete MicroServiceInfo And notice to Route success:[serviceName]"+ service.getServiceName() + "[version]" + service.getVersion()); + + // Notify the listeners + RouteNotify.getInstance().noticeRouteListener4Delete(service); + + } catch (Exception e) { + LOGGER.error("delete MicroService And synchro to Route:[serviceName]"+ service.getServiceName() + "[version]" + service.getVersion()+" throw exception", e); + throw e; + } + } + + public void saveServiceAndnoticeRoute(MicroServiceFullInfo service) throws Exception { + + try { + // save the redis record + MicroServiceFullService.getInstance().saveMicroServiceInfo2Redis(service); + + LOGGER.info("save MicroServiceInfo And notice to Route success:[serviceName]"+ service.getServiceName() + "[version]" + service.getVersion()); + + // Notify the listeners + RouteNotify.getInstance().noticeRouteListener4Add(service); + + + } catch (Exception e) { + LOGGER.error("save MicroServiceInfo And synchro to Route fail :[serviceName]" + service.getServiceName()+ "[version]" + service.getVersion() + " throw exception", e); + throw e; + } + } + + public Set getAllVersion(String serviceName) { + Set serviceVersionSet = new HashSet(); + try { + String pattern = MicroServiceUtil.getServiceKey(serviceName, "*"); + Set serviceKeySet = RedisAccessWrapper.filterKeys(pattern); + + + Pattern serviceKeyRegexPattern = MicroServiceUtil.getServiceKeyRegexPattern(); + for (String serviceKey : serviceKeySet) { + Matcher matcher = serviceKeyRegexPattern.matcher(serviceKey); + if (matcher.matches()) { + serviceVersionSet.add(matcher.group("version")); + } + } + } catch (Exception e) { + LOGGER.error("getAllVersion [serviceName]:" + serviceName + " throw exception", e); + } + + return serviceVersionSet; + + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/CatalogClient.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/CatalogClient.java new file mode 100644 index 0000000..94a709d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/CatalogClient.java @@ -0,0 +1,87 @@ +package org.onap.msb.apiroute.wrapper.consulextend; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseCallback; +import org.onap.msb.apiroute.wrapper.consulextend.util.Http; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; + +/** + * HTTP Client for /v1/catalog/ endpoints. + */ +public class CatalogClient { + + private static final Logger LOGGER = LoggerFactory + .getLogger(CatalogClient.class); + + private static final TypeReference TYPE_SERVICES_MAP = new TypeReference() { + }; + + + private static final String CATALOG_URI_8500 = "/v1/catalog"; + private static final String CATAlOG_URI_10081 = "/api/catalog/v1"; + + private static final String GET_SERVICES_URI = "/services"; + + private static final Http httpClient = Http.getInstance(); + + private HttpHost targetHost = null; + private String catalogUri = CATAlOG_URI_10081; + + CatalogClient(final HttpHost targetHost) { + this.targetHost = targetHost; + if (targetHost.getPort() == 8500) { + catalogUri = CATALOG_URI_8500; + } + } + + /** + * Retrieves all services for a given datacenter with + * {@link com.orbitz.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 com.orbitz.consul.model.ConsulResponse} containing a map + * of service name to list of tags. + */ + public void getServices(CatalogOptions catalogOptions, + QueryOptions queryOptions, + ConsulResponseCallback callback) { + + // prepare access path + // path:10081 vs 8500 + String path = targetHost.toString() + catalogUri + GET_SERVICES_URI; + + // params:wait,index,dc...... + String params = Http.optionsFrom(catalogOptions, queryOptions); + + // node meta: ns,external,internal..... + String node_meta = ConfigUtil.getInstance().getNodeMetaQueryParam(); + + // add params + path = (params != null && !params.isEmpty()) ? path += "?" + params + : path; + + // add node_meta + if (node_meta != null && !node_meta.isEmpty()) { + path = path.contains("?") ? path +"&"+ node_meta : path + "?" + + node_meta; + } + + // async watch services + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("get all services:" + path); + } + httpClient.asyncGetDelayHandle(path, TYPE_SERVICES_MAP, callback); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/Consul.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/Consul.java new file mode 100644 index 0000000..cf8196a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/Consul.java @@ -0,0 +1,97 @@ +package org.onap.msb.apiroute.wrapper.consulextend; + +import org.apache.http.HttpHost; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +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 static final Logger LOGGER = LoggerFactory + .getLogger(Consul.class); + + private final CatalogClient catalogClient; + private final HealthClient healthClient; + + private Consul(CatalogClient catalogClient, HealthClient healthClient) { + this.catalogClient = catalogClient; + this.healthClient = healthClient; + } + + /** + * Get the Catalog HTTP client. + *

+ * /v1/catalog + * + * @return The Catalog HTTP client. + */ + public CatalogClient catalogClient() { + return catalogClient; + } + + /** + * Get the Health HTTP client. + *

+ * /v1/health + * + * @return The Health HTTP client. + */ + public HealthClient healthClient() { + return healthClient; + } + + /** + * Creates a new {@link Builder} object. + * + * @return A new Consul builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Used to create a default Consul client. + * + * @return A default {@link Consul} client. + */ + @VisibleForTesting + public static Consul newClient() { + return builder().build(); + } + + public static class Builder { + + private HttpHost targetHost; + + { + targetHost = new HttpHost(DEFAULT_HTTP_HOST, DEFAULT_HTTP_PORT); + } + + Builder() { + + } + + public Builder withHostAndPort(String hostname, int port) { + this.targetHost = new HttpHost(hostname, port); + return this; + } + + public Consul build() { + LOGGER.info("********build consul:"+targetHost.toString()+"****************"); + CatalogClient catalogClient = new CatalogClient(targetHost); + HealthClient healthClient = new HealthClient(targetHost); + return new Consul(catalogClient,healthClient); + } + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/HealthClient.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/HealthClient.java new file mode 100644 index 0000000..899a57a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/HealthClient.java @@ -0,0 +1,112 @@ +package org.onap.msb.apiroute.wrapper.consulextend; + +import java.util.List; + +import org.apache.http.HttpHost; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseCallback; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.onap.msb.apiroute.wrapper.consulextend.util.Http; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; + +/** + * HTTP Client for /v1/health/ endpoints. + */ +public class HealthClient { + private final static Logger LOGGER = LoggerFactory + .getLogger(HealthClient.class); + + private static final TypeReference> TYPE_SERVICE_HEALTH_LIST = new TypeReference>() { + }; + + private static final String HEALTH_URI_10081 = "/api/health/v1"; + private static final String HEALTH_URI_8500 = "/v1/health"; + private static final String GET_HEALTH_SERVICE_URI = "/service"; + +// private static final String GET_HEALTH_SERVICE_URI = "/v1/health/service"; + +// private static final String GET_HEALTH_SERVICE_URI = "/api/health/v1/service"; + + private final static Http httpClient = Http.getInstance(); + + private HttpHost targetHost = null; + private String healthUri = HEALTH_URI_10081; + + HealthClient(final HttpHost targetHost) { + this.targetHost = targetHost; + + if(targetHost.getPort() == 8500) + { + healthUri = HEALTH_URI_8500; + } + } + + /** + * Asynchronously retrieves the healthchecks for all healthy service + * instances in a given datacenter with + * {@link com.orbitz.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) { + // prepare access path + String path = targetHost.toString() + healthUri + GET_HEALTH_SERVICE_URI + "/"+ service; + + String params = Http.optionsFrom(catalogOptions, queryOptions); + path = (params != null && !params.isEmpty()) ? path += "?" + + params : path; //query all nodes without filter for health + + // async watch +// LOGGER.info("get health paasing service:" + path); + httpClient.asyncGetDelayHandle(path, TYPE_SERVICE_HEALTH_LIST, callback); + } + + /** + * Asynchronously retrieves the healthchecks for all nodes in a given + * datacenter with {@link com.orbitz.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) { + + // prepare access path + String path = targetHost.toString() + healthUri + GET_HEALTH_SERVICE_URI + "/"+ service; + String params = Http.optionsFrom(catalogOptions, queryOptions); + path = (params != null && !params.isEmpty()) ? path += "?" + params + : path; + + // async watch +// LOGGER.debug("get service:" + path); + httpClient.asyncGetDelayHandle(path, TYPE_SERVICE_HEALTH_LIST, callback); + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseCallback.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseCallback.java new file mode 100644 index 0000000..ab5b74a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseCallback.java @@ -0,0 +1,33 @@ +package org.onap.msb.apiroute.wrapper.consulextend.async; + +import com.orbitz.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 com.orbitz.consul.model.ConsulResponse}. + * + * @param consulResponse The Consul response. + */ + void onComplete(ConsulResponse consulResponse); + + /** + * Callback for a successful {@link com.orbitz.consul.model.ConsulResponse}. + * + * @param consulResponse The Consul response. + */ + void onDelayComplete(OriginalConsulResponse originalConsulResponse); + + /** + * Callback for an unsuccessful request. + * + * @param throwable The exception thrown. + */ + void onFailure(Throwable throwable); +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseHeader.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseHeader.java new file mode 100644 index 0000000..ae8291a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/ConsulResponseHeader.java @@ -0,0 +1,27 @@ +package org.onap.msb.apiroute.wrapper.consulextend.async; + +import java.math.BigInteger; + +public class ConsulResponseHeader { + private final long lastContact; + private final boolean knownLeader; + private final BigInteger index; + + public ConsulResponseHeader(long lastContact, boolean knownLeader, BigInteger index) { + this.lastContact = lastContact; + this.knownLeader = knownLeader; + this.index = index; + } + + public long getLastContact() { + return lastContact; + } + + public boolean isKnownLeader() { + return knownLeader; + } + + public BigInteger getIndex() { + return index; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/OriginalConsulResponse.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/OriginalConsulResponse.java new file mode 100644 index 0000000..dbad13b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/async/OriginalConsulResponse.java @@ -0,0 +1,27 @@ +package org.onap.msb.apiroute.wrapper.consulextend.async; + +import org.apache.http.HttpResponse; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class OriginalConsulResponse { + final HttpResponse response; + final TypeReference responseType; + + public OriginalConsulResponse(HttpResponse response, TypeReference responseType) { + this.response = response; + this.responseType = responseType; + + } + + public HttpResponse getResponse() { + return response; + } + + public TypeReference getResponseType() { + return responseType; + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ConsulCache.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ConsulCache.java new file mode 100644 index 0000000..809a8dc --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ConsulCache.java @@ -0,0 +1,301 @@ +package org.onap.msb.apiroute.wrapper.consulextend.cache; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +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.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseCallback; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseHeader; +import org.onap.msb.apiroute.wrapper.consulextend.async.OriginalConsulResponse; +import org.onap.msb.apiroute.wrapper.consulextend.util.Http; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.orbitz.consul.ConsulException; +import com.orbitz.consul.model.ConsulResponse; +import com.orbitz.consul.option.ImmutableQueryOptions; +import com.orbitz.consul.option.QueryOptions; + +/** + * 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); + + @VisibleForTesting + static final String BACKOFF_DELAY_PROPERTY = "com.orbitz.consul.cache.backOffDelay"; + private static final long BACKOFF_DELAY_QTY_IN_MS = getBackOffDelayInMs(System + .getProperties()); + + private final AtomicReference latestIndex = new AtomicReference( + null); + 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; + + ConsulCache(CallbackConsumer callbackConsumer) { + + this.callBackConsumer = callbackConsumer; + + this.responseCallback = new ConsulResponseCallback() { + @Override + public void onComplete(ConsulResponse consulResponse) { + + if (consulResponse.isKnownLeader()) { + if (!isRunning()) { + return; + } + updateIndex(consulResponse); + + for (Listener l : listeners) { + l.notify(consulResponse); + } + + if (state.compareAndSet(State.starting, State.started)) { + initLatch.countDown(); + } + + runCallback(); + } else { + onFailure(new ConsulException( + "Consul cluster has no elected leader")); + } + } + + @Override + public void onDelayComplete( + OriginalConsulResponse originalConsulResponse) { + + try { + // get header + ConsulResponseHeader consulResponseHeader = Http + .consulResponseHeader(originalConsulResponse + .getResponse()); + + if (consulResponseHeader.isKnownLeader()) { + if (!isRunning()) { + return; + } + + boolean isConuslIndexChanged = isConuslIndexChanged(consulResponseHeader + .getIndex()); + // consul index different + if (isConuslIndexChanged) { + + updateIndex(consulResponseHeader.getIndex()); + + // get T type data + ConsulResponse consulResponse = Http + .consulResponse(originalConsulResponse + .getResponseType(), + originalConsulResponse + .getResponse()); + + // notify customer to custom T data + for (Listener l : listeners) { + l.notify(consulResponse); + } + } + + if (state.compareAndSet(State.starting, State.started)) { + initLatch.countDown(); + } + + runCallback(); + + } else { + onFailure(new ConsulException( + "Consul cluster has no elected leader")); + } + } catch (Exception e) { + onFailure(e); + } + + } + + @Override + public void onFailure(Throwable throwable) { + + if (!isRunning()) { + return; + } + LOGGER.error( + String.format( + "Error getting response from consul. will retry in %d %s", + BACKOFF_DELAY_QTY_IN_MS, TimeUnit.MILLISECONDS), + throwable); + + executorService.schedule(new Runnable() { + @Override + public void run() { + runCallback(); + } + }, BACKOFF_DELAY_QTY_IN_MS, TimeUnit.MILLISECONDS); + } + }; + } + + @VisibleForTesting + static long getBackOffDelayInMs(Properties properties) { + String backOffDelay = null; + try { + backOffDelay = properties.getProperty(BACKOFF_DELAY_PROPERTY); + if (!Strings.isNullOrEmpty(backOffDelay)) { + return Long.parseLong(backOffDelay); + } + } catch (Exception ex) { + LOGGER.warn( + backOffDelay != null ? String.format( + "Error parsing property variable %s: %s", + BACKOFF_DELAY_PROPERTY, backOffDelay) : String + .format("Error extracting property variable %s", + BACKOFF_DELAY_PROPERTY), ex); + } + return TimeUnit.SECONDS.toMillis(10); + } + + 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); + } + + private void updateIndex(ConsulResponse consulResponse) { + if (consulResponse != null && consulResponse.getIndex() != null) { + this.latestIndex.set(consulResponse.getIndex()); + } + } + + public void updateIndex(BigInteger index) { + if (index != null) { + this.latestIndex.set(index); + } + } + + protected static QueryOptions watchParams(final BigInteger index, + final int blockSeconds, QueryOptions queryOptions) { + checkArgument(!queryOptions.getIndex().isPresent() + && !queryOptions.getWait().isPresent(), + "Index and wait cannot be overridden"); + + return ImmutableQueryOptions.builder() + .from(watchDefaultParams(index, blockSeconds)) + .token(queryOptions.getToken()) + .consistencyMode(queryOptions.getConsistencyMode()) + .near(queryOptions.getNear()).build(); + } + + private static QueryOptions watchDefaultParams(final BigInteger index, + final 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(ConsulResponse newValues); + } + + public boolean addListener(Listener listener) { + boolean added = listeners.add(listener); + return added; + } + + public List> getListeners() { + return Collections.unmodifiableList(listeners); + } + + public boolean removeListener(Listener listener) { + return listeners.remove(listener); + } + + @VisibleForTesting + protected State getState() { + return state.get(); + } + + private boolean isConuslIndexChanged(final BigInteger index) { + + if (index != null && !index.equals(latestIndex.get())) { + + if (LOGGER.isDebugEnabled()) { + // 第一次ä¸æ‰“å° + if (latestIndex.get() != null) { + LOGGER.debug("consul index compare:new-" + index + " old-" + + latestIndex.get()); + } + + } + + return true; + } + + return false; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServiceHealthCache.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServiceHealthCache.java new file mode 100644 index 0000000..b46ffce --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServiceHealthCache.java @@ -0,0 +1,65 @@ +package org.onap.msb.apiroute.wrapper.consulextend.cache; + +import java.math.BigInteger; +import java.util.List; + +import org.onap.msb.apiroute.wrapper.consulextend.HealthClient; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseCallback; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; + +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; + +public class ServiceHealthCache extends ConsulCache> { + private ServiceHealthCache(CallbackConsumer> callbackConsumer) { + super(callbackConsumer); + } + + /** + * Factory method to construct a string/{@link ServiceHealth} map for a particular service. + *

+ * Keys will be a {@link HostAndPort} object made up of the service's address/port combo + * + * @param healthClient the {@link HealthClient} + * @param serviceName the name of the service + * @param passing include only passing services? + * @return a cache object + */ + public static ServiceHealthCache newCache( + final HealthClient healthClient, + final String serviceName, + final boolean passing, + final CatalogOptions catalogOptions, + final int watchSeconds, + final QueryOptions queryOptions) { + + CallbackConsumer> callbackConsumer = new CallbackConsumer>() { + @Override + public void consume(BigInteger index, + ConsulResponseCallback> callback) { + // TODO Auto-generated method stub + QueryOptions params = watchParams(index, watchSeconds, queryOptions); + if (passing) { + healthClient.getHealthyServiceInstances(serviceName, catalogOptions, params, callback); + } else { + healthClient.getAllServiceInstances(serviceName, catalogOptions, params, callback); + } + } + }; + + return new ServiceHealthCache(callbackConsumer); + } + + public static ServiceHealthCache newCache( + final HealthClient healthClient, + final String serviceName, + final boolean passing, + final CatalogOptions catalogOptions, + final int watchSeconds) { + return newCache(healthClient, serviceName, passing, catalogOptions, watchSeconds, QueryOptions.BLANK); + } + + public static ServiceHealthCache newCache(final HealthClient healthClient, final String serviceName) { + return newCache(healthClient, serviceName, true, CatalogOptions.BLANK, 10); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServicesCatalogCache.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServicesCatalogCache.java new file mode 100644 index 0000000..08c7369 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/cache/ServicesCatalogCache.java @@ -0,0 +1,39 @@ +package org.onap.msb.apiroute.wrapper.consulextend.cache; + +import java.math.BigInteger; + +import org.apache.http.HttpEntity; +import org.onap.msb.apiroute.wrapper.consulextend.CatalogClient; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseCallback; + +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; + +public class ServicesCatalogCache extends ConsulCache { + + private ServicesCatalogCache(CallbackConsumer callbackConsumer) { + super(callbackConsumer); + } + + public static ServicesCatalogCache newCache( + final CatalogClient catalogClient, + final CatalogOptions catalogOptions, + final QueryOptions queryOptions, + final int watchSeconds) { + + CallbackConsumer callbackConsumer = new CallbackConsumer() { + @Override + public void consume(BigInteger index, ConsulResponseCallback callback) { + QueryOptions params = watchParams(index, watchSeconds, queryOptions); + catalogClient.getServices(catalogOptions, params,callback); + } + }; + + return new ServicesCatalogCache(callbackConsumer); + + } + + public static ServicesCatalogCache newCache(final CatalogClient catalogClient) { + return newCache(catalogClient, CatalogOptions.BLANK, QueryOptions.BLANK, 10); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckServiceDataEmptyAndAutoStopWatchFilter.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckServiceDataEmptyAndAutoStopWatchFilter.java new file mode 100644 index 0000000..01b5168 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckServiceDataEmptyAndAutoStopWatchFilter.java @@ -0,0 +1,98 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.msb.apiroute.SyncDataManager; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ImmutableService; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ImmutableServiceHealth; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.Service; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.onap.msb.apiroute.wrapper.queue.QueueManager; +import org.onap.msb.apiroute.wrapper.queue.ServiceData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.model.ConsulResponse; +import com.orbitz.consul.model.health.ImmutableNode; + + + +public class CheckServiceDataEmptyAndAutoStopWatchFilter implements + WatchTask.Filter> { + + private final static Logger LOGGER = LoggerFactory + .getLogger(CheckServiceDataEmptyAndAutoStopWatchFilter.class); + private final String serviceName; + + public CheckServiceDataEmptyAndAutoStopWatchFilter( + final String serviceName) { + this.serviceName = serviceName; + } + + @Override + public boolean filter(ConsulResponse> object) { + // TODO Auto-generated method stub + boolean result = check(object); + + if (!result) { + // create delete + writeServiceToQueue4Del(); + // stop watch + SyncDataManager.stopWatchService(serviceName); + } + + return result; + } + + // when: + // 1)service had been deleted + // 2)service Health check was not passing + // single service return [],size==0 + // stop this service watching task and create delete event + private boolean check(ConsulResponse> object) { + boolean result = true; + + if (object == null || object.getResponse() == null + || object.getResponse().size() == 0) { + LOGGER.info("check service-{},its data is empty", + serviceName); + return false; + } + + return result; + } + + private void writeServiceToQueue4Del() { + ServiceData> data = new ServiceData>(); + data.setDataType(ServiceData.DataType.service); + data.setOperate(ServiceData.Operate.delete); + + // tell the subsequent operation the service name which will be deleted + Service service = ImmutableService.builder().id("").port(0).address("") + .service(serviceName).addTags("").createIndex(0).modifyIndex(0).build(); + ServiceHealth serviceHealth = ImmutableServiceHealth.builder() + .service(service) + .node(ImmutableNode.builder().node("").address("").build()) + .build(); + List serviceHealthList = new ArrayList(); + serviceHealthList.add(serviceHealth); + + data.setData(serviceHealthList); + + LOGGER.info("put delete service[" + + serviceName + + "] to service queue :because of deleted "); + + try { + QueueManager.getInstance().putIn(data); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + LOGGER.warn( + "put delete service[" + + serviceName + + "] to service queue interrupted because of deleted:", + e); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckTagAndAutoStopWatchFilter.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckTagAndAutoStopWatchFilter.java new file mode 100644 index 0000000..311edce --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/CheckTagAndAutoStopWatchFilter.java @@ -0,0 +1,96 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.msb.apiroute.SyncDataManager; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.onap.msb.apiroute.wrapper.queue.QueueManager; +import org.onap.msb.apiroute.wrapper.queue.ServiceData; +import org.onap.msb.apiroute.wrapper.queue.ServiceData.Operate; +import org.onap.msb.apiroute.wrapper.util.ServiceFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.model.ConsulResponse; + +public class CheckTagAndAutoStopWatchFilter implements + WatchTask.Filter> { + + private final static Logger LOGGER = LoggerFactory + .getLogger(CheckTagAndAutoStopWatchFilter.class); + + private final String serviceName; + + public CheckTagAndAutoStopWatchFilter(final String serviceName) { + this.serviceName = serviceName; + } + + // from consul,the response data:List + // filter ServiceHealth list and find the ServiceHealths which satisfy the + // tags conditions + // 1)if all ServiceHealth don't satisfy,create delete event and stop watch + // 2)if have some ServiceHealths satisfy the tags conditions,create update + // event and send these ServiceHealths + @Override + public boolean filter(ConsulResponse> object) { + // TODO Auto-generated method stub + + // find #ServiceHealth# which satisfy the tag conditions + List satisfyList = getSatisfyList(object); + + // no satisfied ServiceHealth + if (satisfyList.isEmpty()) { + + LOGGER.info("put delete service[" + + serviceName + + "] to service queue :because of NO tag meet the conditions"); + + // create delete + writeServiceToQueue(object.getResponse(), + ServiceData.Operate.delete); + // stop watch + //SyncDataManager.stopWatchService(serviceName); + return false; + } + + LOGGER.info("put update service[" + + serviceName + + "] to service queue :which tags meet the conditions"); + + // put the satisfy list to queue + writeServiceToQueue(satisfyList, ServiceData.Operate.update); + + return true; + } + + private List getSatisfyList( + ConsulResponse> object) { + List satisfyList = new ArrayList(); + for (ServiceHealth health : object.getResponse()) { + + if (ServiceFilter.getInstance().isFilterCheck(health)) { + satisfyList.add(health); + } + } + + return satisfyList; + } + + private void writeServiceToQueue(List serviceData, + Operate operate) { + ServiceData> data = new ServiceData>(); + data.setOperate(operate); + data.setDataType(ServiceData.DataType.service); + data.setData(serviceData); + + + try { + QueueManager.getInstance().putIn(data); + } catch (InterruptedException e) { + LOGGER.warn("put " + operate + " service[" + serviceName + + "] to service queue interrupted ", e); + } + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ConsulIndexFilter.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ConsulIndexFilter.java new file mode 100644 index 0000000..14ab2c7 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ConsulIndexFilter.java @@ -0,0 +1,48 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.model.ConsulResponse; + +public class ConsulIndexFilter implements WatchTask.Filter { + + private final static Logger LOGGER = LoggerFactory + .getLogger(ConsulIndexFilter.class); + + private final AtomicReference latestIndex = new AtomicReference( + null); + + @Override + public boolean filter(final ConsulResponse object) { + // TODO Auto-generated method stub + return isChanged(object); + } + + private boolean isChanged(final ConsulResponse consulResponse) { + + if (consulResponse != null && consulResponse.getIndex() != null + && !consulResponse.getIndex().equals(latestIndex.get())) { + + if(LOGGER.isDebugEnabled()){ + //第一次ä¸æ‰“å° + if (latestIndex.get()!=null) { + LOGGER.debug("consul index compare:new-" + + consulResponse.getIndex() + " old-" + + latestIndex.get()); + } + + } + + this.latestIndex.set(consulResponse.getIndex()); + return true; + } + + return false; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ServiceModifyIndexFilter.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ServiceModifyIndexFilter.java new file mode 100644 index 0000000..38fb7c6 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/ServiceModifyIndexFilter.java @@ -0,0 +1,121 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import org.onap.msb.apiroute.wrapper.consulextend.model.health.Service; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.onap.msb.apiroute.wrapper.util.ServiceFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableList; +import com.orbitz.consul.model.ConsulResponse; +import com.orbitz.consul.model.health.HealthCheck; + +public class ServiceModifyIndexFilter implements WatchTask.Filter> { + + private final AtomicReference> lastResponse = + new AtomicReference>(ImmutableList.of()); + + private final static Logger LOGGER = LoggerFactory.getLogger(ServiceModifyIndexFilter.class); + + + @Override + public boolean filter(ConsulResponse> object) { + // TODO Auto-generated method stub + + List newList=object.getResponse(); + if(realFilter(newList)){ + lastResponse.set(ImmutableList.copyOf(newList)); + return true; + } + + return false; + } + + private boolean realFilter(List newList) { + // 1)判断listçš„size,ä¸ç­‰åˆ™æ”¹å˜ + if (newList.size() != lastResponse.get().size()) { + // 第一次ä¸æ‰“å° + if (lastResponse.get().size() != 0) { + LOGGER.info(newList.get(0).getService().getService() + + " instance count is different.new_count:" + newList.size() + " old_count:" + + lastResponse.get().size()); + } + + return true; + } + + + // 2)循环æœåŠ¡å®žä¾‹åˆ¤æ–­æœåŠ¡å†…容和å¥åº·æ£€æŸ¥æ˜¯å¦æ”¹å˜ + for (ServiceHealth newData : newList) { + ServiceHealth sameIdOldData = findSameIdInOldList(newData); + // 若在oldlist中ä¸å­˜åœ¨ï¼Œåˆ™æ”¹å˜ + if (sameIdOldData == null) { + + LOGGER.info(newData.getService().getId() + + " is a new service instance.the createindex:" + + newData.getService().getCreateIndex() + + " the modifyIndex:" + + newData.getService().getModifyIndex()); + + return true; + } + + // 若在oldlist中存在,则比较ModifyIndex的值和å¥åº·æ£€æŸ¥çŠ¶æ€.ä¸ç­‰åˆ™æ”¹å˜ + if(!compareService(newData,sameIdOldData)){ + LOGGER.info(newData.getService().getId() +" instance is change because of modifyIndex or health check" ); + return true; + } + } + + return false; + + + } + + + private boolean compareService(ServiceHealth oldData,ServiceHealth newData) { + + return compareServiceInfo(oldData.getService(),newData.getService()) && + compareServiceHealthStatus(oldData.getChecks(),newData.getChecks()); + + } + + + + private boolean compareServiceInfo(Service oldServiceInfo, Service newServiceInfo) { + if (oldServiceInfo.getModifyIndex() != newServiceInfo.getModifyIndex()) { + LOGGER.info(newServiceInfo.getId() + " new_modifyIndex:" + + newServiceInfo.getModifyIndex() + " old_modifyIndex:" + + oldServiceInfo.getModifyIndex()); + return false; + } + return true; + } + + private boolean compareServiceHealthStatus(List oldData, List newData) { + boolean oldHealthCheck=ServiceFilter.getInstance().isFilterHealthCheck(oldData); + boolean newHealthCheck=ServiceFilter.getInstance().isFilterHealthCheck(newData); + return oldHealthCheck==newHealthCheck; + + } + + + private ServiceHealth findSameIdInOldList(ServiceHealth newData) { + for (ServiceHealth oldData : lastResponse.get()) { + if (oldData.getService().getId().equals(newData.getService().getId())) { + return oldData; + } + } + + return null; + } + + public boolean resetModifyIndex() { + // clear last response + lastResponse.set(ImmutableList.of()); + return true; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchCatalogServicesTask.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchCatalogServicesTask.java new file mode 100644 index 0000000..c21295f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchCatalogServicesTask.java @@ -0,0 +1,90 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import org.apache.http.HttpEntity; +import org.onap.msb.apiroute.wrapper.consulextend.CatalogClient; +import org.onap.msb.apiroute.wrapper.consulextend.cache.ServicesCatalogCache; +import org.onap.msb.apiroute.wrapper.consulextend.cache.ConsulCache.Listener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; + +public class WatchCatalogServicesTask extends WatchTask { + + private final static Logger LOGGER = LoggerFactory + .getLogger(WatchCatalogServicesTask.class); + + private ServicesCatalogCache servicesCache = null; + + public WatchCatalogServicesTask( + final CatalogClient catalogClient, + final CatalogOptions catalogOptions, + final QueryOptions queryOptions, + final int watchSeconds) + { + initCache(catalogClient,catalogOptions,queryOptions,watchSeconds); + } + + public WatchCatalogServicesTask( + final CatalogClient catalogClient, + final int watchSeconds) + { + initCache(catalogClient,CatalogOptions.BLANK,QueryOptions.BLANK,watchSeconds); + } + + public WatchCatalogServicesTask( + final CatalogClient catalogClient) + { + initCache(catalogClient,CatalogOptions.BLANK,QueryOptions.BLANK,10); + } + + private ServicesCatalogCache initCache(final CatalogClient catalogClient, + final CatalogOptions catalogOptions, + final QueryOptions queryOptions, + final int watchSeconds) { + LOGGER.info("************create all services watch task*****************"); + servicesCache = ServicesCatalogCache.newCache(catalogClient, + catalogOptions, queryOptions, watchSeconds); + + servicesCache + .addListener((Listener) new InternalListener()); + + return servicesCache; + } + + @Override + public boolean startWatch() { + // TODO Auto-generated method stub + if(servicesCache!=null) + { + try { + servicesCache.start(); + LOGGER.info("************start all services watch task*****************"); + return true; + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.warn("start service list watch failed:", e); + } + } + + return false; + } + + @Override + public boolean stopWatch() { + // TODO Auto-generated method stub + if (servicesCache != null) { + try { + servicesCache.stop(); + LOGGER.info("************stop all services watch task*****************"); + return true; + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.warn("stop service list watch failed:", e); + } + } + return false; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchServiceHealthTask.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchServiceHealthTask.java new file mode 100644 index 0000000..b0d64a7 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchServiceHealthTask.java @@ -0,0 +1,128 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import java.math.BigInteger; +import java.util.List; + +import org.onap.msb.apiroute.wrapper.consulextend.HealthClient; +import org.onap.msb.apiroute.wrapper.consulextend.cache.ServiceHealthCache; +import org.onap.msb.apiroute.wrapper.consulextend.cache.ConsulCache.Listener; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; + +public class WatchServiceHealthTask extends WatchTask> { + private final static Logger LOGGER = LoggerFactory + .getLogger(WatchServiceHealthTask.class); + + private ServiceHealthCache serviceHealthCache = null; + private String serviceName=""; + + public String getServiceName() { + return serviceName; + } + + public WatchServiceHealthTask(final HealthClient healthClient, + final String serviceName,final boolean passing, + final CatalogOptions catalogOptions, final int watchSeconds, + final QueryOptions queryOptions) { + initCache(healthClient, serviceName, passing, catalogOptions, + watchSeconds, queryOptions); + } + + public WatchServiceHealthTask(final HealthClient healthClient, + final String serviceName,final boolean passing, + final int watchSeconds) + + { + initCache(healthClient, serviceName, passing, CatalogOptions.BLANK, + watchSeconds, QueryOptions.BLANK); + } + + public WatchServiceHealthTask(final HealthClient healthClient, + final String serviceName, final int watchSeconds) + + { + initCache(healthClient, serviceName, true, CatalogOptions.BLANK, + watchSeconds, QueryOptions.BLANK); + } + + private ServiceHealthCache initCache(final HealthClient healthClient, + final String serviceName,final boolean passing, + final CatalogOptions catalogOptions, final int watchSeconds, + final QueryOptions queryOptions) { +// LOGGER.info("************create {} watch task*****************",serviceName); + this.serviceName = serviceName; + serviceHealthCache = ServiceHealthCache.newCache(healthClient, + serviceName, passing, catalogOptions, watchSeconds, + queryOptions); + + serviceHealthCache + .addListener((Listener>) new InternalListener()); + + return serviceHealthCache; + } + + public boolean startWatch() { + + if(serviceHealthCache!=null) + { + try { + serviceHealthCache.start(); + LOGGER.info("************start {} watch task*****************",serviceName); + return true; + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.warn("start service watch failed:", e); + } + } + + return false; + + } + + public boolean stopWatch(){ + if (serviceHealthCache != null) { + try { + serviceHealthCache.stop(); + LOGGER.info("************stop {} watch task*****************",serviceName); + return true; + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.warn("stop service watch failed:", e); + } + } + + return false; + } + + + public boolean resetIndex() + { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("reset " + serviceName + " consul index"); + } + + //reset consul index + serviceHealthCache.updateIndex(BigInteger.valueOf(0)); + + + //reset modify index + for (WatchTask.Filter> filter : getAllFilters()) { + if (filter instanceof ServiceModifyIndexFilter) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("reset " + serviceName + " modify index"); + } + return ((ServiceModifyIndexFilter) filter).resetModifyIndex(); + } + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("reset modify index.did not find filter:" + serviceName); + } + + return false; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchTask.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchTask.java new file mode 100644 index 0000000..2ada19d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WatchTask.java @@ -0,0 +1,87 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import java.util.concurrent.CopyOnWriteArrayList; + +import org.onap.msb.apiroute.wrapper.consulextend.cache.ConsulCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.model.ConsulResponse; + +public abstract class WatchTask { + private final CopyOnWriteArrayList> filters = new CopyOnWriteArrayList>(); + private final CopyOnWriteArrayList> handlers = new CopyOnWriteArrayList>(); + private final static Logger LOGGER = LoggerFactory + .getLogger(WatchTask.class); + + //start + public abstract boolean startWatch(); + + //stop + public abstract boolean stopWatch(); + + // filters + public interface Filter { + public boolean filter(final ConsulResponse object); + } + + public boolean addFilter(Filter filter) { + boolean added = filters.add(filter); + return added; + } + + public void removeAllFilter() { + filters.clear(); + } + + + public final CopyOnWriteArrayList> getAllFilters(){ + return filters; + } + + // handlers + public interface Handler { + void handle(final ConsulResponse object); + } + + public boolean addHandler(Handler handler) { + boolean added = handlers.add(handler); + return added; + } + + public void removeAllHandler() { + handlers.clear(); + } + + // internal listener + protected class InternalListener implements ConsulCache.Listener { + @Override + public void notify(ConsulResponse newValues) { + + long startTime = System.currentTimeMillis(); + + // filter + for (Filter f : filters) { + // false,return + if (!f.filter(newValues)) { + return; + } + } + + // handle + for (Handler h : handlers) { + h.handle(newValues); + } + + long endTime = System.currentTimeMillis(); + + if(endTime-startTime > 10*1000) + { + LOGGER.info("WatchTask THEAD WORK TIMEOUT"); + } + } + + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WriteBufferHandler.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WriteBufferHandler.java new file mode 100644 index 0000000..6df10e3 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/expose/WriteBufferHandler.java @@ -0,0 +1,36 @@ +package org.onap.msb.apiroute.wrapper.consulextend.expose; + +import org.onap.msb.apiroute.wrapper.queue.QueueManager; +import org.onap.msb.apiroute.wrapper.queue.ServiceData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.model.ConsulResponse; + +public class WriteBufferHandler implements WatchTask.Handler { + + private static final Logger LOGGER = LoggerFactory + .getLogger(WriteBufferHandler.class); + private final ServiceData.DataType dataType; + + + public WriteBufferHandler(final ServiceData.DataType dataType) { + this.dataType =dataType; + } + + @Override + public void handle(ConsulResponse object) { + // TODO Auto-generated method stub + ServiceData data = new ServiceData(); + data.setDataType(dataType); + data.setData(object.getResponse()); + + try { + QueueManager.getInstance().putIn(data); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + LOGGER.warn("put data to buffer interrupted:", e); + } + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/Service.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/Service.java new file mode 100644 index 0000000..fd7f3fa --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/Service.java @@ -0,0 +1,39 @@ +package org.onap.msb.apiroute.wrapper.consulextend.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; +import com.google.common.collect.ImmutableList; +import org.immutables.value.Value; + +import java.util.List; + +@Value.Immutable +@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(); + + @JsonProperty("CreateIndex") + public abstract int getCreateIndex(); + + @JsonProperty("ModifyIndex") + public abstract int getModifyIndex(); +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/ServiceHealth.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/ServiceHealth.java new file mode 100644 index 0000000..a739b4b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/model/health/ServiceHealth.java @@ -0,0 +1,30 @@ +package org.onap.msb.apiroute.wrapper.consulextend.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; +import com.google.common.collect.ImmutableList; +import com.orbitz.consul.model.health.HealthCheck; +import com.orbitz.consul.model.health.Node; + +import org.immutables.value.Value; + +import java.util.List; +@Value.Immutable +@JsonSerialize(as = ImmutableServiceHealth.class) +@JsonDeserialize(as = ImmutableServiceHealth.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class ServiceHealth { + + @JsonProperty("Node") + public abstract Node getNode(); + + @JsonProperty("Service") + public abstract Service getService(); + + @JsonProperty("Checks") + @JsonDeserialize(as = ImmutableList.class, contentAs = HealthCheck.class) + public abstract List getChecks(); + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/util/Http.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/util/Http.java new file mode 100644 index 0000000..9f0980c --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/consulextend/util/Http.java @@ -0,0 +1,281 @@ +package org.onap.msb.apiroute.wrapper.consulextend.util; + +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.concurrent.FutureCallback; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClients; +import org.eclipse.jetty.http.HttpStatus; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseCallback; +import org.onap.msb.apiroute.wrapper.consulextend.async.ConsulResponseHeader; +import org.onap.msb.apiroute.wrapper.consulextend.async.OriginalConsulResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.orbitz.consul.ConsulException; +import com.orbitz.consul.model.ConsulResponse; +import com.orbitz.consul.option.CatalogOptions; +import com.orbitz.consul.option.QueryOptions; +import com.orbitz.consul.util.Jackson; + +public class Http { + private static final Logger LOGGER = LoggerFactory.getLogger(Http.class); + + private final static CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients + .custom().setMaxConnTotal(Integer.MAX_VALUE) + .setMaxConnPerRoute(Integer.MAX_VALUE).build(); + + private static Http instance = null; + + private Http() { + } + + public static Http getInstance() { + if (instance == null) { + instance = new Http(); + httpAsyncClient.start(); + } + + return instance; + } + + // async get data from consul,and handle response immediately + public void asyncGet(String requestURI, + final TypeReference responseType, + final ConsulResponseCallback callback, final Integer... okCodes) { + // LOGGER.info("Async request:"+requestURI); + + httpAsyncClient.execute(new HttpGet(requestURI), + new FutureCallback() { + + public void completed(final HttpResponse response) { + callback.onComplete(consulResponse(responseType, + response)); + } + + public void failed(final Exception ex) { + callback.onFailure(ex); + } + + public void cancelled() { + LOGGER.warn("cancelled async request"); + } + }); + } + + // async get data from consul,and handle response delay + public void asyncGetDelayHandle(String requestURI, + final TypeReference responseType, + final ConsulResponseCallback callback, final Integer... okCodes) { + + httpAsyncClient.execute(new HttpGet(requestURI), + new FutureCallback() { + + public void completed(final HttpResponse response) { + OriginalConsulResponse originalConsulResponse = new OriginalConsulResponse( + response, responseType); + + //handle not 2xx code + if (!isSuccessful(response)) { + + LOGGER.warn("response statuscode:" + + response.getStatusLine().getStatusCode()); + + callback.onFailure(new ConsulException( + "response statuscode:" + + response.getStatusLine() + .getStatusCode())); + } else { + callback.onDelayComplete(originalConsulResponse); + } + + } + + public void failed(final Exception ex) { + callback.onFailure(ex); + } + + public void cancelled() { + LOGGER.warn("cancelled async request"); + } + }); + } + + public static ConsulResponseHeader consulResponseHeader( + HttpResponse response) { + String indexHeaderValue = response.getFirstHeader("X-Consul-Index") + .getValue(); + String lastContactHeaderValue = response.getFirstHeader( + "X-Consul-Lastcontact").getValue(); + String knownLeaderHeaderValue = response.getFirstHeader( + "X-Consul-Knownleader").getValue(); + + BigInteger index = indexHeaderValue == null ? new BigInteger("0") + : new BigInteger(indexHeaderValue); + long lastContact = lastContactHeaderValue == null ? 0 : Long + .parseLong(lastContactHeaderValue); + boolean knownLeader = knownLeaderHeaderValue == null ? false : Boolean + .parseBoolean(knownLeaderHeaderValue); + + return new ConsulResponseHeader(lastContact, knownLeader, index); + } + + public static ConsulResponse consulResponse( + TypeReference responseType, HttpResponse response) { + + String indexHeaderValue = response.getFirstHeader("X-Consul-Index") + .getValue(); + String lastContactHeaderValue = response.getFirstHeader( + "X-Consul-Lastcontact").getValue(); + String knownLeaderHeaderValue = response.getFirstHeader( + "X-Consul-Knownleader").getValue(); + + BigInteger index = indexHeaderValue == null ? new BigInteger("0") + : new BigInteger(indexHeaderValue); + long lastContact = lastContactHeaderValue == null ? 0 : Long + .parseLong(lastContactHeaderValue); + boolean knownLeader = knownLeaderHeaderValue == null ? false : Boolean + .parseBoolean(knownLeaderHeaderValue); + + ConsulResponse consulResponse = new ConsulResponse(readResponse( + response, responseType), lastContact, knownLeader, index); + return consulResponse; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static T readResponse(HttpResponse response, + TypeReference responseType) { + + // read streamed entity + T object; + + // HttpEntity,read original data. + Type _type = responseType.getType(); + if (_type instanceof Class + && (((Class) _type).isAssignableFrom(HttpEntity.class))) { + object = (T) response.getEntity(); + return object; + } + + // String,read original data. + if (_type instanceof Class + && (((Class) _type).isAssignableFrom(String.class))) { + + try { + + object = (T) IOUtils + .toString(response.getEntity().getContent()); + response.getEntity().getContent().close(); + + } catch (UnsupportedOperationException e) { + object = (T) ""; + LOGGER.warn("covert streamed entity to String exception:", e); + } catch (IOException e) { + object = (T) ""; + LOGGER.warn("covert streamed entity to String exception:", e); + } + + return object; + } + + // change data type + try { + object = Jackson.MAPPER.readValue( + response.getEntity().getContent(), responseType); + } catch (IOException e) { + LOGGER.warn("covert streamed entity to object exception:", e); + object = readDefaultResponse(responseType); + } + + return object; + } + + @SuppressWarnings("unchecked") + public static T readDefaultResponse(TypeReference responseType) { + Type _type = responseType.getType(); + if (_type instanceof ParameterizedType + && ((ParameterizedType) _type).getRawType() == List.class) { + return (T) ImmutableList.of(); + } else if (_type instanceof ParameterizedType + && ((ParameterizedType) _type).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 " + _type); + } + } + + public static boolean isSuccessful(HttpResponse response, + Integer... okCodes) { + return HttpStatus.isSuccess(response.getStatusLine().getStatusCode()) + || Sets.newHashSet(okCodes).contains( + response.getStatusLine().getStatusCode()); + } + + public static String optionsFrom(CatalogOptions catalogOptions, + QueryOptions queryOptions) { + String params = ""; + + if (catalogOptions != null) { + Map options = catalogOptions.toQuery(); + + if (options.containsKey("dc")) { + params += "dc=" + options.get("dc"); + } + if (options.containsKey("tag")) { + params += params.isEmpty() ? "" : "&"; + params += "tag=" + options.get("tag"); + } + } + + if (queryOptions != null) { + Map options = queryOptions.toQuery(); + + if (options.containsKey("consistent")) { + params += params.isEmpty() ? "" : "&"; + params += "consistent=" + options.get("consistent"); + } + if (options.containsKey("stale")) { + params += params.isEmpty() ? "" : "&"; + params += "stale=" + options.get("stale"); + } + if (options.containsKey("wait")) { + params += params.isEmpty() ? "" : "&"; + params += "wait=" + options.get("wait"); + } + + if (options.containsKey("index")) { + params += params.isEmpty() ? "" : "&"; + params += "index=" + options.get("index"); + } + if (options.containsKey("token")) { + params += params.isEmpty() ? "" : "&"; + params += "token=" + options.get("token"); + } + if (options.containsKey("near")) { + params += params.isEmpty() ? "" : "&"; + params += "near=" + options.get("near"); + } + if (options.containsKey("dc")) { + params += params.isEmpty() ? "" : "&"; + params += "dc=" + options.get("dc"); + } + } + return params; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOConstants.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOConstants.java new file mode 100644 index 0000000..ab3b3f1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOConstants.java @@ -0,0 +1,6 @@ +package org.onap.msb.apiroute.wrapper.dao; + +public class DAOConstants { + public static final String ROUTE_KIND = "route"; + public static final String SERVICE_KIND = "service"; +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOFactory.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOFactory.java new file mode 100644 index 0000000..0fb3e14 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/DAOFactory.java @@ -0,0 +1,18 @@ +package org.onap.msb.apiroute.wrapper.dao; + +import org.onap.msb.apiroute.wrapper.dao.route.IRouteDAO; +import org.onap.msb.apiroute.wrapper.dao.route.RouteDAOImpl; +import org.onap.msb.apiroute.wrapper.dao.service.IServiceDAO; +import org.onap.msb.apiroute.wrapper.dao.service.ServiceDAOImpl; + +public class DAOFactory { + private static final IRouteDAO routeDAO = new RouteDAOImpl(); + private static final IServiceDAO serviceDAO = new ServiceDAOImpl(); + + public static IRouteDAO getRouteDAO(){ + return routeDAO; + } + public static IServiceDAO getServiceDAO(){ + return serviceDAO; + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/RedisAccessWrapper.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/RedisAccessWrapper.java new file mode 100644 index 0000000..06799b1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/RedisAccessWrapper.java @@ -0,0 +1,133 @@ +package org.onap.msb.apiroute.wrapper.dao; + +import org.onap.msb.apiroute.wrapper.util.JedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.ScanParams; +import redis.clients.jedis.ScanResult; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class RedisAccessWrapper { + private static final Logger LOGGER = LoggerFactory.getLogger(RedisAccessWrapper.class); + //An iteration starts when the cursor is set to 0 + private static final String REDIS_SCAN_POINTER_NEW_ITERATION = "0"; + //An iteration terminated when the cursor returned by the server is 0 + private static final String REDIS_SCAN_POINTER_ITERATION_END = "0"; + private static final int REDIS_SCAN_COUNT = 50; + + + public static void save(String key,String value) throws Exception { + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + jedis.set(key,value); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + } + + public static String query(String key) throws Exception { + Jedis jedis = null; + String value = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + value = jedis.get(key); + }finally { + JedisUtil.returnJedisInstance(jedis); + } + return value; + } + + public static long delete(String key) throws Exception { + Jedis jedis = null; + long reply = 0L; + try { + jedis = JedisUtil.borrowJedisInstance(); + reply = jedis.del(key); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + return reply; + } + + public static boolean isExist(String key) throws Exception { + boolean isExist = false; + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + isExist = jedis.exists(key); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + return isExist; + } + + public static List queryMultiKeys(String keyPattern) throws Exception { + Set keySet = filterKeys(keyPattern); + List valueList = new ArrayList<>(); + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + for(String key : keySet){ + String value = jedis.get(key); + if(value !=null && !"".equals(value)){ + valueList.add(value); + } + } + } finally { + JedisUtil.returnJedisInstance(jedis); + } + return valueList; + } + + public static long deleteMultiKeys(String keyPattern) throws Exception { + Set keySet = filterKeys(keyPattern); + long replySum = 0L; + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + for(String key : keySet){ + long reply = jedis.del(key); + replySum = replySum + reply; + } + } finally { + JedisUtil.returnJedisInstance(jedis); + } + return replySum; + } + + /** + * filter the keys according to the given pattern + * using "scan" instead of using "keys", incrementally iterate the keys space + * @param pattern the input filter pattern + * @return the matched keys set + */ + public static Set filterKeys(String pattern) throws Exception{ + long start = System.currentTimeMillis(); + Jedis jedis = null; + Set filteredKeys = new HashSet<>(); + ScanParams scanParams = new ScanParams(); + scanParams.match(pattern); + scanParams.count(REDIS_SCAN_COUNT); + try { + jedis = JedisUtil.borrowJedisInstance(); + ScanResult scanResult = jedis.scan(REDIS_SCAN_POINTER_NEW_ITERATION,scanParams); + filteredKeys.addAll(scanResult.getResult()); + while(!scanResult.getStringCursor().equals(REDIS_SCAN_POINTER_ITERATION_END)){ + scanResult = jedis.scan(scanResult.getStringCursor(),scanParams); + filteredKeys.addAll(scanResult.getResult()); + } + } finally { + JedisUtil.returnJedisInstance(jedis); + } + long end = System.currentTimeMillis(); + long costTime = end-start; + LOGGER.info("filterKeys " + pattern + " count:" + filteredKeys.size() + " cost: " + costTime); + return filteredKeys; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/IRouteDAO.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/IRouteDAO.java new file mode 100644 index 0000000..f2f51c1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/IRouteDAO.java @@ -0,0 +1,18 @@ +package org.onap.msb.apiroute.wrapper.dao.route; + +import java.util.List; + +import org.onap.msb.apiroute.wrapper.dao.route.bean.RouteInfo; + +public interface IRouteDAO { + public void saveRoute(String key, RouteInfo routeInfo) throws Exception; + + public RouteInfo queryRoute(String key) throws Exception; + + public List queryMultiRoute(String keyPattern) throws Exception; + + public long deleteRoute(String key) throws Exception; + + public long deleteMultiRoute(String keyPattern) throws Exception; + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/RouteDAOImpl.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/RouteDAOImpl.java new file mode 100644 index 0000000..83e868b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/RouteDAOImpl.java @@ -0,0 +1,63 @@ +package org.onap.msb.apiroute.wrapper.dao.route; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.onap.msb.apiroute.wrapper.dao.RedisAccessWrapper; +import org.onap.msb.apiroute.wrapper.dao.route.bean.RouteInfo; +import org.onap.msb.apiroute.wrapper.util.Jackson; + +public class RouteDAOImpl implements IRouteDAO{ + public void saveRoute(String key, RouteInfo routeInfo) throws Exception { + String routeInfoStr = null; + // change orginal url from “/†to empty string accord to the rewrite rule while forwarding + if("/".equals(routeInfo.getSpec().getUrl())){ + routeInfo.getSpec().setUrl(""); + } + try { + routeInfoStr = Jackson.MAPPER.writeValueAsString(routeInfo); + } catch (JsonProcessingException e) { + throw new Exception("error occurred while parsing RouteInfo to json data",e); + } + RedisAccessWrapper.save(key, routeInfoStr); + } + + public RouteInfo queryRoute(String key) throws Exception { + RouteInfo routeInfo = null; + String routeInfoStr = RedisAccessWrapper.query(key); + if (null == routeInfoStr || "".equals(routeInfoStr)) + return null; + try { + routeInfo = Jackson.MAPPER.readValue(routeInfoStr, RouteInfo.class); + } catch (IOException e) { + throw new Exception("error occurred while parsing the redis json data to RouteInfo",e); + } + return routeInfo; + } + + public List queryMultiRoute(String keyPattern) throws Exception { + List routeInfoStrList = RedisAccessWrapper.queryMultiKeys(keyPattern); + List routeInfoList = new ArrayList<>(); + for (String routeInfoStr : routeInfoStrList) { + RouteInfo routeInfo = null; + try { + routeInfo = Jackson.MAPPER.readValue(routeInfoStr, RouteInfo.class); + routeInfoList.add(routeInfo); + } catch (IOException e) { + throw new Exception("error occurred while parsing the redis json data to RouteInfo",e); + } + } + return routeInfoList; + } + + public long deleteRoute(String key) throws Exception { + return RedisAccessWrapper.delete(key); + } + + public long deleteMultiRoute(String keyPattern) throws Exception{ + return RedisAccessWrapper.deleteMultiKeys(keyPattern); + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Metadata.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Metadata.java new file mode 100644 index 0000000..3e11fcc --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Metadata.java @@ -0,0 +1,48 @@ +package org.onap.msb.apiroute.wrapper.dao.route.bean; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class Metadata { + private String name; + private String namespace; + private String uid = ""; + //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") + //private Date creationTimestamp; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") + private Date updateTimestamp; + private Map labels = new HashMap(); + private String[] annotations = null; + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Metadata metadata = (Metadata) o; + return Objects.equals(name, metadata.name) && + Objects.equals(namespace, metadata.namespace) && + Objects.equals(uid, metadata.uid) && + //Objects.equals(creationTimestamp, metadata.creationTimestamp) && + Objects.equals(updateTimestamp, metadata.updateTimestamp) && + Objects.equals(labels, metadata.labels) && + Objects.equals(annotations, metadata.annotations); + } + + @Override + public int hashCode() { + //return Objects.hash(name, namespace, uid, creationTimestamp, updateTimestamp, labels, annotations); + return Objects.hash(name, namespace, uid, updateTimestamp, labels, annotations); + } + */ +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Node.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Node.java new file mode 100644 index 0000000..a328c3c --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Node.java @@ -0,0 +1,32 @@ +package org.onap.msb.apiroute.wrapper.dao.route.bean; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class Node { + private String ip; + private int port; + private int weight=0; + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Node node = (Node) o; + return Objects.equals(port, node.port) && + Objects.equals(weight, node.weight) && + Objects.equals(ip, node.ip); + } + + @Override + public int hashCode() { + return Objects.hash(ip, port, weight); + } + */ +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/RouteInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/RouteInfo.java new file mode 100644 index 0000000..e5ab7bf --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/RouteInfo.java @@ -0,0 +1,79 @@ +package org.onap.msb.apiroute.wrapper.dao.route.bean; + +import org.onap.msb.apiroute.wrapper.dao.DAOConstants; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class RouteInfo { + private String kind = DAOConstants.ROUTE_KIND; + private String apiVersion = ""; + private String status = ""; + private Metadata metadata; + private Spec spec; + + /** + Example route: + { + "kind" : "route", + "apiVersion" : "v1", + "status" : "1" + "metadata" : { + "name" : "kubernetes", + "namespace" : "default", + "uid" : "0b6f198e-c6ab-11e6-86aa-fa163ee2118b", + "creationTimestamp" : "2016-12-20T11:54:21Z", + "updateTimestamp" : "", + "labels" : { + "component" : "apiserver", + "provider" : "kubernetes" + }, + "annotations" : {} + }, + "spec" : { + "visualRange" : 0, + "url" : "", + "publish_port" : "", + "host" : "", + "apijson" : "", + "apijsontype" : "" + "metricsUrl" : "" + "consulServiceName" : "" + "useOwnUpstream" : "" //是å¦ä½¿ç”¨è¯¥æœåŠ¡ç‹¬ç«‹çš„upstreamè½¬å‘ + "publishProtocol" : "", //å‘布地å€ä½¿ç”¨http还是httpåè®® + "enable_ssl" : "0|1", //转å‘时,使用http还是http转å‘。http:0/https:1 + "controll" : "", //是å¦å¯ä»¥ä¿®æ”¹ + "nodes" : [{ + "ip" : 10.10.10.2, + "port" : 8080, + "weight" : "" + } + ], + } +} + */ + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RouteInfo that = (RouteInfo) o; + return Objects.equals(kind, that.kind) && + Objects.equals(apiVersion, that.apiVersion) && + Objects.equals(status, that.status) && + Objects.equals(metadata, that.metadata) && + Objects.equals(spec, that.spec); + } + + @Override + public int hashCode() { + return Objects.hash(kind, apiVersion, status, metadata, spec); + } + */ +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Spec.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Spec.java new file mode 100644 index 0000000..8e9a54b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/route/bean/Spec.java @@ -0,0 +1,52 @@ +package org.onap.msb.apiroute.wrapper.dao.route.bean; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class Spec { + private String visualRange = ""; + private String url = ""; + private String publish_port; + private String host = ""; + private String apijson = ""; + private String apijsontype = ""; + private String metricsUrl = ""; + private String consulServiceName = ""; + private String useOwnUpstream = ""; + private String publish_protocol = ""; + private boolean enable_ssl = false; + private String control = ""; + private Node[] nodes; + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Spec spec = (Spec) o; + return Objects.equals(enable_ssl, spec.enable_ssl) && + Objects.equals(visualRange, spec.visualRange) && + Objects.equals(url, spec.url) && + Objects.equals(publish_port, spec.publish_port) && + Objects.equals(host, spec.host) && + Objects.equals(apijson, spec.apijson) && + Objects.equals(apijsontype, spec.apijsontype) && + Objects.equals(metricsUrl, spec.metricsUrl) && + Objects.equals(consulServiceName, spec.consulServiceName) && + Objects.equals(useOwnUpstream, spec.useOwnUpstream) && + Objects.equals(publish_protocol, spec.publish_protocol) && + Objects.equals(control, spec.control) && + Arrays.equals(nodes, spec.nodes); + } + + @Override + public int hashCode() { + return Objects.hash(visualRange, url, publish_port, host, apijson, apijsontype, metricsUrl, consulServiceName, useOwnUpstream, publish_protocol, enable_ssl, control, nodes); + } + */ +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/IServiceDAO.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/IServiceDAO.java new file mode 100644 index 0000000..d63f656 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/IServiceDAO.java @@ -0,0 +1,17 @@ +package org.onap.msb.apiroute.wrapper.dao.service; + +import java.util.List; + +import org.onap.msb.apiroute.wrapper.dao.service.bean.ServiceInfo; + +public interface IServiceDAO { + public void saveService(String key, ServiceInfo serviceInfo) throws Exception; + + public ServiceInfo queryService(String key) throws Exception; + + public List queryMultiService(String keyPattern) throws Exception; + + public long deleteService(String key) throws Exception; + + public long deleteMultiService(String keyPattern) throws Exception; +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/ServiceDAOImpl.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/ServiceDAOImpl.java new file mode 100644 index 0000000..e9f4586 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/ServiceDAOImpl.java @@ -0,0 +1,59 @@ +package org.onap.msb.apiroute.wrapper.dao.service; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.onap.msb.apiroute.wrapper.dao.RedisAccessWrapper; +import org.onap.msb.apiroute.wrapper.dao.service.bean.ServiceInfo; +import org.onap.msb.apiroute.wrapper.util.Jackson; + +public class ServiceDAOImpl implements IServiceDAO{ + public void saveService(String key, ServiceInfo serviceInfo) throws Exception { + String serviceInfoStr = null; + try { + serviceInfoStr = Jackson.MAPPER.writeValueAsString(serviceInfo); + } catch (JsonProcessingException e) { + throw new Exception("error occurred while parsing ServiceInfo to json data",e); + } + RedisAccessWrapper.save(key, serviceInfoStr); + } + + public ServiceInfo queryService(String key) throws Exception { + ServiceInfo serviceInfo = null; + String serviceInfoStr = RedisAccessWrapper.query(key); + if (null == serviceInfoStr || "".equals(serviceInfoStr)) + return null; + try { + serviceInfo = Jackson.MAPPER.readValue(serviceInfoStr, ServiceInfo.class); + } catch (IOException e) { + throw new Exception("error occurred while parsing the redis json data to ServiceInfo",e); + } + return serviceInfo; + } + + public List queryMultiService(String keyPattern) throws Exception { + List serviceInfoStrList = RedisAccessWrapper.queryMultiKeys(keyPattern); + List routeInfoList = new ArrayList<>(); + for (String serviceInfoStr : serviceInfoStrList) { + ServiceInfo serviceInfo = null; + try { + serviceInfo = Jackson.MAPPER.readValue(serviceInfoStr, ServiceInfo.class); + routeInfoList.add(serviceInfo); + } catch (IOException e) { + throw new Exception("error occurred while parsing the redis json data to ServiceInfo",e); + } + } + return routeInfoList; + } + + public long deleteService(String key) throws Exception { + return RedisAccessWrapper.delete(key); + } + + public long deleteMultiService(String keyPattern) throws Exception{ + return RedisAccessWrapper.deleteMultiKeys(keyPattern); + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Metadata.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Metadata.java new file mode 100644 index 0000000..6ce6126 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Metadata.java @@ -0,0 +1,50 @@ +package org.onap.msb.apiroute.wrapper.dao.service.bean; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class Metadata { + private String name; + private String namespace; + private String uid = ""; + //@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") + //private Date creationTimestamp; + // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") + private Date updateTimestamp; + private Map labels = new HashMap(); + //private String[] annotations = new String[]{}; + private String[] annotations = null; + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Metadata metadata = (Metadata) o; + return Objects.equals(name, metadata.name) && + Objects.equals(namespace, metadata.namespace) && + Objects.equals(uid, metadata.uid) && + //Objects.equals(creationTimestamp, metadata.creationTimestamp) && + Objects.equals(updateTimestamp, metadata.updateTimestamp) && + Objects.equals(labels, metadata.labels) && + Objects.equals(annotations, metadata.annotations); + } + + @Override + public int hashCode() { + //return Objects.hash(name, namespace, uid, creationTimestamp, updateTimestamp, labels, annotations); + return Objects.hash(name, namespace, uid, updateTimestamp, labels, annotations); + } + */ +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Node.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Node.java new file mode 100644 index 0000000..fecf4bd --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Node.java @@ -0,0 +1,32 @@ +package org.onap.msb.apiroute.wrapper.dao.service.bean; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class Node { + private String ip; + private String port; + private int ttl=-1; + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Node node = (Node) o; + return Objects.equals(port, node.port) && + Objects.equals(ttl, node.ttl) && + Objects.equals(ip, node.ip); + } + + @Override + public int hashCode() { + return Objects.hash(ip, port, ttl); + } + */ +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/ServiceInfo.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/ServiceInfo.java new file mode 100644 index 0000000..01ab3a9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/ServiceInfo.java @@ -0,0 +1,76 @@ +package org.onap.msb.apiroute.wrapper.dao.service.bean; + +import org.onap.msb.apiroute.wrapper.dao.DAOConstants; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class ServiceInfo { + private String kind = DAOConstants.SERVICE_KIND; + private String apiVersion = ""; + private String status = ""; + private Metadata metadata; + private Spec spec; + + /** + Example Service: + { + "kind" : "service", + "apiVersion" : "v1", + "metadata" : { + "name" : "kubernetes", + "namespace" : "default", + "uid" : "0b6f198e-c6ab-11e6-86aa-fa163ee2118b", + "creationTimestamp" : "2016-12-20T11:54:21Z", + "labels" : { + "component" : "apiserver", + "provider" : "kubernetes" + }, + "annotations" : {} + }, + "spec" : { + "visualRange" : 0, + "url" : "", + "path" : "", + "publish_port" : "", + "host" : "", + "protocol" : "", + "lb_policy" : "", + "enable_ssl" : "0|1", //转å‘时,使用http还是http转å‘。http:0/https:1 + "nodes" : [{ + "ip" : 10.10.10.2, + "port" : 8080, + "ttl" : + } + ], + } + "status" : "" + } + + */ + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ServiceInfo that = (ServiceInfo) o; + return Objects.equals(kind, that.kind) && + Objects.equals(apiVersion, that.apiVersion) && + Objects.equals(status, that.status) && + Objects.equals(metadata, that.metadata) && + Objects.equals(spec, that.spec); + } + + @Override + public int hashCode() { + return Objects.hash(kind, apiVersion, status, metadata, spec); + } + */ +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Spec.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Spec.java new file mode 100644 index 0000000..65e769b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/dao/service/bean/Spec.java @@ -0,0 +1,44 @@ +package org.onap.msb.apiroute.wrapper.dao.service.bean; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +public class Spec { + private String visualRange = ""; + private String url = ""; + private String path = ""; + private String publish_port; + private String host = ""; + private String protocol = ""; + private String lb_policy = ""; + private boolean enable_ssl = false; + private Node[] nodes; + + /* + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Spec spec = (Spec) o; + return Objects.equals(enable_ssl, spec.enable_ssl) && + Objects.equals(visualRange, spec.visualRange) && + Objects.equals(url, spec.url) && + Objects.equals(path, spec.path) && + Objects.equals(publish_port, spec.publish_port) && + Objects.equals(host, spec.host) && + Objects.equals(protocol, spec.protocol) && + Objects.equals(lb_policy, spec.lb_policy) && + Arrays.equals(nodes, spec.nodes); + } + + @Override + public int hashCode() { + return Objects.hash(visualRange, url, path, publish_port, host, protocol, lb_policy, enable_ssl, nodes); + } + */ +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/BaseQueue.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/BaseQueue.java new file mode 100644 index 0000000..dce3b12 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/BaseQueue.java @@ -0,0 +1,36 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public abstract class BaseQueue { + + private final List>> queueArray= new ArrayList>>(); + + public BaseQueue(int queueNum,int queueCapacity) + { + for(int i=0;queueNum>0 && i>(queueCapacity)); + } + } + + public int getQueneNum(){ + return queueArray.size(); + } + + protected BlockingQueue> getQueue(int index) + { + return queueArray.get(index); + } + + public abstract void put(final ServiceData data) throws InterruptedException; + + public abstract ServiceData take(final int queueIndex) throws InterruptedException; + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/QueueManager.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/QueueManager.java new file mode 100644 index 0000000..20525c8 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/QueueManager.java @@ -0,0 +1,65 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.util.List; +import java.util.Map; + +import org.apache.http.HttpEntity; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +public class QueueManager { + + private static final Logger LOGGER = LoggerFactory + .getLogger(QueueManager.class); + + private final BaseQueue serviceListQueue; + private final BaseQueue> serviceQueue; + + private volatile static QueueManager instance = null; + + public static QueueManager getInstance() { + if (instance == null) { + synchronized (QueueManager.class) { + if (instance == null) { + instance = new QueueManager(); + } + } + } + return instance; + } + + private QueueManager() { + serviceListQueue = new ServiceListQueue( + RouteUtil.SERVICE_LIST_QUEUE_CAPACITY); + serviceQueue = new ServiceQueue(RouteUtil.SERVICE_DATA_QUEUE_NUM, + RouteUtil.SERVICE_QUEUE_CAPACITY); + } + + public ServiceData takeFromServiceListQueue( + int queueIndex) throws InterruptedException { + return serviceListQueue.take(queueIndex); + } + + public ServiceData> takeFromServiceQueue(int queueIndex) + throws InterruptedException { + return serviceQueue.take(queueIndex); + } + + + @SuppressWarnings("unchecked") + public void putIn(ServiceData data) throws InterruptedException { + + if (data.getDataType() == ServiceData.DataType.service_list) { + LOGGER.debug("putIn service_list queue success"); + serviceListQueue.put((ServiceData) data); + } else if (data.getDataType() == ServiceData.DataType.service) { + serviceQueue.put((ServiceData>) data); + } else { + LOGGER.warn("DATA TYPE NOT SUPPORT:"+data.getDataType()); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceConsumer.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceConsumer.java new file mode 100644 index 0000000..29d705f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceConsumer.java @@ -0,0 +1,167 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.onap.msb.apiroute.SyncDataManager; +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.health.RedisHealthCheck; +import org.onap.msb.apiroute.wrapper.MicroServiceWrapper; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.onap.msb.apiroute.wrapper.util.CommonUtil; +import org.onap.msb.apiroute.wrapper.util.ServiceFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class ServiceConsumer implements Runnable { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceConsumer.class); + + private boolean isRunning = true; + + private int index; + + + private static final int retryCount=3; + + //缓存æœåŠ¡ä¿¡æ¯ï¼škey:æœåŠ¡å 和对应的版本列表Set + private final Map> lastVersionResponse = new HashMap>(); + + public ServiceConsumer(final int index) { + this.index = index; + } + + + public void run() { + + LOGGER.info("run Service Consumer Thread [" + index + "]"); + + while (isRunning) { + try { + ServiceData> serviceData; + + serviceData = QueueManager.getInstance().takeFromServiceQueue(index); + + // LOGGER.info("Service Consumer Thread [" + index + + // "] take out serviceData from Queue successfully"); + + if (serviceData.getOperate() == ServiceData.Operate.delete) { + // 删除æœåŠ¡ + deleteMicroService(serviceData); + } else { + // æ›´æ–°æœåŠ¡ + updateMicroService(serviceData); + } + } catch (InterruptedException e) { + LOGGER.error("ServiceConsumer throw InterruptedException: ", e); + Thread.currentThread().interrupt(); + } + + } + } + + + + private void deleteMicroService(ServiceData> serviceData) { + String serviceName = null; + try { + if (serviceData.getData() == null || serviceData.getData().size() == 0) { + throw new Exception("sysn deleteMicroService is wrong:serviceData is empty"); + } + + serviceName = serviceData.getData().get(0).getService().getService(); +// LOGGER.info("Service Consumer [" + index + "] start to delete MicroService:[serviceName] " +// + serviceName); + + //ServiceListCache.removeService(serviceName); + MicroServiceWrapper.getInstance().deleteMicroService4AllVersion(serviceName); + + } catch (Exception e) { + LOGGER.error("delete MicroServiceInfo 4AllVersion fail from consul:[serviceName]" + serviceName, e); + //删除失败,é‡è¯•ä¸‰æ¬¡ + for(int i=0;i> serviceData) { + + if (serviceData.getData() == null || serviceData.getData().size() == 0) { + LOGGER.warn("sysn updateMicroService is wrong:serviceData is empty "); + return; + } + + String serviceName = ""; + + try { + + serviceName = serviceData.getData().get(0).getService().getService(); + List serviceNodeList = serviceData.getData(); + + + Map microServiceInfo4version = + ServiceFilter.getInstance().transMicroServiceInfoFromConsul(serviceNodeList); + + // 删除数æ®åº“中已ä¸å­˜åœ¨çš„版本å·æœåŠ¡ä¿¡æ¯ + Set newAllVersion = microServiceInfo4version.keySet(); + + if (lastVersionResponse.containsKey(serviceName)) { + Set dbAllVersionSet = lastVersionResponse.get(serviceName); + // Set dbAllVersionSet=MicroServiceWrapper.getInstance().getAllVersion(serviceName); + Set delVersionList = CommonUtil.getDiffrent(newAllVersion, dbAllVersionSet); + + if (delVersionList.size() > 0) { + + LOGGER.info("MicroService version is change from consul:[serviceName]" + serviceName + + "[version]" + delVersionList); + + + for (String version : delVersionList) { + MicroServiceWrapper.getInstance().deleteMicroService(serviceName, version); + } + + } + } + + lastVersionResponse.put(serviceName, newAllVersion); + + for (Map.Entry entry : microServiceInfo4version.entrySet()) { + MicroServiceFullInfo new_microServiceFullInfo = entry.getValue(); + MicroServiceWrapper.getInstance().saveServiceAndnoticeRoute(new_microServiceFullInfo); + + } + + + } catch (Exception e) { + LOGGER.error("update MicroServiceInfo fail from consul:[serviceName]" + serviceName); + //更新失败,é‡ç½®ä»»åŠ¡æœåŠ¡çš„modifyIndex,等待é‡æ–°æ›´æ–° + RedisHealthCheck.writeCheckFlag = true; + SyncDataManager.resetIndex(serviceName); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceData.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceData.java new file mode 100644 index 0000000..aaadbe9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceData.java @@ -0,0 +1,53 @@ +package org.onap.msb.apiroute.wrapper.queue; + +public class ServiceData { + public static enum Type { + consul + }; + + public static enum Operate { + update, delete + }; + + public static enum DataType { + service_list, service + } + + private Type type = Type.consul; + private DataType dataType; + private T data; + private Operate operate = Operate.update; + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public DataType getDataType() { + return dataType; + } + + public void setDataType(DataType dataType) { + this.dataType = dataType; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public Operate getOperate() { + return operate; + } + + public void setOperate(Operate operate) { + this.operate = operate; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListCache.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListCache.java new file mode 100644 index 0000000..59be2e4 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListCache.java @@ -0,0 +1,35 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class ServiceListCache { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceListCache.class); + + private final static AtomicReference> serviceNameList4Cache = new AtomicReference>(new HashSet()); + + public static Set getLatestServiceNamelist() { + return serviceNameList4Cache.get(); + } + + public static void setLatestServiceNamelist(Set newServicenamelist){ + serviceNameList4Cache.set(newServicenamelist); + LOGGER.info("------current total Watch Service Num :"+ newServicenamelist.size()); + } + + public synchronized static void removeService(String serviceName){ + + Set servicenamelist=serviceNameList4Cache.get(); + servicenamelist.remove(serviceName); + serviceNameList4Cache.set(servicenamelist); + LOGGER.info("------current total Watch Service Num :"+ servicenamelist.size()); + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListConsumer.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListConsumer.java new file mode 100644 index 0000000..e3a8aa7 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListConsumer.java @@ -0,0 +1,209 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.http.HttpEntity; +import org.onap.msb.apiroute.SyncDataManager; +import org.onap.msb.apiroute.wrapper.MicroServiceWrapper; +import org.onap.msb.apiroute.wrapper.util.CommonUtil; +import org.onap.msb.apiroute.wrapper.util.ServiceFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + + +public class ServiceListConsumer implements Runnable { + + private static final Logger LOGGER = LoggerFactory + .getLogger(ServiceListConsumer.class); + + private boolean isRunning = true; + + private int index; + + + public ServiceListConsumer() { + this.index = 0; + } + + public void run() { + LOGGER.info("run ServiceList Consumer Thread [" + index + "]"); + + while (isRunning) { + try { + // å–最新一æ¡è®°å½• + ServiceData serviceData = QueueManager + .getInstance().takeFromServiceListQueue(index); + LOGGER.debug("ServiceList Consumer Thread [" + index + + "] take out serviceData from Queue successfully"); + + HttpEntity newValues = serviceData.getData(); + + Set newServiceNameList = filterServiceList(newValues); + + if (ServiceListCache.getLatestServiceNamelist().size() == 0) { + boolean initSuccess=initServiceList(newServiceNameList); + if(initSuccess){ + ServiceListCache.setLatestServiceNamelist(newServiceNameList); + } + } else { + updateServiceList(newServiceNameList); + ServiceListCache.setLatestServiceNamelist(newServiceNameList); + } + + + } catch (Exception e) { + LOGGER.error( + "ServiceListConsumer throw Exception: ", e); + } + } + } + + private void startWatchService(String serviceName) { + // start to Watch service nodes + + SyncDataManager.startWatchService(serviceName); + } + + private void updateServiceList(Set newServiceNameList) { + Set registerServiceNameList = CommonUtil.getDiffrent( + ServiceListCache.getLatestServiceNamelist(), newServiceNameList); + + if (registerServiceNameList.size() > 0) { + LOGGER.info("***need to start Watch Service num from consul :" + + registerServiceNameList.size()); + + for (String serviceName : registerServiceNameList) { + startWatchService(serviceName); + } + } + } + + private boolean initServiceList(Set newServiceNameList) { + LOGGER.info("***start to initialize service List when System startup ***"); + + Set dbServiceNameList = MicroServiceWrapper + .getInstance().getAllMicroServiceKey(); + + if(dbServiceNameList==null){ + LOGGER.error("init ServiceList from redis fail "); + return false; + } + + + // 对比删除redisè„æ•°æ® + Set delServiceNameList = CommonUtil.getDiffrent( + newServiceNameList, dbServiceNameList); + + LOGGER.info("***need to delete Service num from redis :" + + delServiceNameList.size()); + for (String serviceName : delServiceNameList) { + try { + MicroServiceWrapper.getInstance() + .deleteMicroService4AllVersion(serviceName); + LOGGER.info("delete MicroService success from initialize:[serviceName]" + + serviceName); + + } catch (Exception e) { + LOGGER.error( + "initialize serviceList :Delete MicroServiceInfo serviceName:" + + serviceName + " FAIL : ", e); + } + } + + // å¯åŠ¨åŒæ­¥å¼€å¯ç›‘å¬å…¨éƒ¨æœåŠ¡åˆ—表 + LOGGER.info("***need to start Watch Service num from initialize :" + + newServiceNameList.size()); + + for (String serviceName : newServiceNameList) { + startWatchService(serviceName); + } + + return true; + + } + + /*private ImmutableSet filterServiceList( + final Map> serviceList) { + if (serviceList == null || serviceList.isEmpty()) { + return ImmutableSet.of(); + } + + final ImmutableSet.Builder builder = ImmutableSet.builder(); + + for (Map.Entry> entry : serviceList.entrySet()) { + + String key = entry.getKey(); + if (key != null && !"consul".equals(key)) { + + List value = entry.getValue(); + if (ServiceFilter.getInstance().isFilterService(value)) { + builder.add(key); + } + } + } + + LOGGER.info("consul all service num:" + serviceList.size()); + LOGGER.info("consul filter service num:" + builder.build().size()); + + return builder.build(); + } +*/ + private Set filterServiceList(final HttpEntity serviceList) { + + if (serviceList == null || serviceList.getContentLength() == 0) { + return new HashSet(); + } + + final Set builder = new HashSet(); + + JsonFactory f = new JsonFactory(); + JsonParser jp = null; + List tagList = null; + int inputServiceNum = 0; + try { + jp = f.createParser(serviceList.getContent()); + jp.nextToken(); + while (jp.nextToken() != JsonToken.END_OBJECT) { + String serviceName = jp.getCurrentName(); + inputServiceNum++; + jp.nextToken(); + tagList = new ArrayList<>(); + while (jp.nextToken() != JsonToken.END_ARRAY) { + tagList.add(jp.getText()); + } + + if (serviceName != null && !"consul".equals(serviceName)) { + if (ServiceFilter.getInstance().isFilterService(tagList)) { + builder.add(serviceName); + } + } + } + } catch (IOException e) { + LOGGER.warn("parse service list error",e); + return new HashSet(); + } finally { + try { + jp.close(); + } catch (IOException e) { + LOGGER.warn("parse service list error",e); + return new HashSet(); + } + } + + int latestServiceNum=ServiceListCache.getLatestServiceNamelist().size(); +// if(latestServiceNum!=builder.size()){ + LOGGER.info("[consul] all service num:" + inputServiceNum+ ", filter service num: new——" + builder.size()+" old——"+latestServiceNum); +// } + + return builder; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListQueue.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListQueue.java new file mode 100644 index 0000000..84ea286 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceListQueue.java @@ -0,0 +1,59 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; + +import org.apache.http.HttpEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServiceListQueue extends BaseQueue { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceListQueue.class); + + + private static final int SERVICE_LIST_DATA_QUEUE_NUM = 1; + private static final int SERVICE_LIST_QUEUE_INDEX = 0; + + public ServiceListQueue(final int queueCapacity) { + super(SERVICE_LIST_DATA_QUEUE_NUM,queueCapacity); + } + + @Override + public void put(ServiceData data) throws InterruptedException { + BlockingQueue> queue=getQueue(SERVICE_LIST_QUEUE_INDEX); + + int size=queue.size(); +// LOGGER.info("before put ServiceListQueue[size:"+size+"] success :[service num]"+data.getData().size()); + //先清空队列 + if(size>0){ + queue.clear(); + } + //æ’入记录 + queue.put(data); + + } + + @Override + public ServiceData take(int queueIndex) throws InterruptedException { + BlockingQueue> queue = getQueue(queueIndex); + ServiceData serviceData = queue.take(); + return serviceData; + + /*//å–队列最新一æ¡æ•°æ® + if (queue.isEmpty()) { + LOGGER.info("take a single serviceData from ServiceListQueue "); + return serviceData; + } else { + List>>> serviceDataList = + new ArrayList>>>(); + //一次性从BlockingQueue获å–æ‰€æœ‰æ•°æ® + queue.drainTo(serviceDataList); + LOGGER.info("take multiple serviceDatas from ServiceListQueue :[num]"+serviceDataList.size()); + return serviceDataList.get(serviceDataList.size() - 1); + }*/ + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceQueue.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceQueue.java new file mode 100644 index 0000000..6c179fc --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/queue/ServiceQueue.java @@ -0,0 +1,45 @@ +package org.onap.msb.apiroute.wrapper.queue; + +import java.util.List; +import java.util.concurrent.BlockingQueue; + +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class ServiceQueue extends BaseQueue> { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceQueue.class); + + private int queneNum; + + public ServiceQueue(final int queneNum,final int queueCapacity) { + super(queneNum,queueCapacity); + this.queneNum=queneNum; + } + + + @Override + public void put(final ServiceData> data) throws InterruptedException { + if(data.getData()==null || data.getData().size()==0) return; + + String serviceName = data.getData().get(0).getService().getService(); + long serviceNameHashCode=serviceName.hashCode() & 0x7FFFFFFF; + int queneIndex=(int) (serviceNameHashCode % queneNum); + +// LOGGER.info("put ServiceQueue [serviceName.hashCode():"+serviceNameHashCode+",queneIndex:"+queneIndex+",queneNum:"+queneNum+"] :[serviceName]"+serviceName); + + BlockingQueue>> queue=getQueue(queneIndex); + queue.put(data); + + LOGGER.info("put ServiceQueue[index:"+queneIndex+",size:"+queue.size()+"] success :[serviceName]"+serviceName); + } + + @Override + public ServiceData> take(final int queueIndex) throws InterruptedException { + return getQueue(queueIndex).take(); + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/ApiRouteService.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/ApiRouteService.java new file mode 100644 index 0000000..c8265b3 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/ApiRouteService.java @@ -0,0 +1,159 @@ +package org.onap.msb.apiroute.wrapper.service; + +import org.onap.msb.apiroute.api.ApiRouteInfo; +import org.onap.msb.apiroute.api.RouteServer; +import org.onap.msb.apiroute.wrapper.dao.DAOFactory; +import org.onap.msb.apiroute.wrapper.dao.route.IRouteDAO; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Metadata; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Node; +import org.onap.msb.apiroute.wrapper.dao.route.bean.RouteInfo; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Spec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + + +public class ApiRouteService { + private static final Logger LOGGER = LoggerFactory.getLogger(ApiRouteService.class); + private static final ApiRouteService instance = new ApiRouteService(); + private IRouteDAO routeDAO = DAOFactory.getRouteDAO(); + + private ApiRouteService() { + } + + public static ApiRouteService getInstance() { + return instance; + } + + public void saveApiRouteService2Redis(ApiRouteInfo apiRouteInfo, String routeKey) throws Exception { + if(apiRouteInfo ==null){ + throw new Exception("input apiRouteInfo to be saved is null!"); + } + RouteInfo routeInfo = APIRouteAdapter.toRouteInfo(apiRouteInfo); + routeDAO.saveRoute(routeKey, routeInfo); + } + + public long deleteApiRouteService2Redis(String routeKey) throws Exception { + return routeDAO.deleteRoute(routeKey); + } + + public long deleteMultiApiRouteService2Redis(String routeKeyPattern) throws Exception { + return routeDAO.deleteMultiRoute(routeKeyPattern); + } + + public ApiRouteInfo getApiRouteInstance(String routeKey) throws Exception { + ApiRouteInfo apiRouteInfo = null; + RouteInfo routeInfo = null; + routeInfo = routeDAO.queryRoute(routeKey); + if(routeInfo!=null) { + apiRouteInfo = APIRouteAdapter.fromRouteInfo(routeInfo); + } + return apiRouteInfo; + } + + public List getMultiApiRouteInstances(String apiRedisKeyPattern) throws Exception { + List apiRouteList = new ArrayList<>(); + List routeInfoList = routeDAO.queryMultiRoute(apiRedisKeyPattern); + for (RouteInfo routeInfo : routeInfoList) { + if (routeInfo != null) { + ApiRouteInfo apiRouteInfo = APIRouteAdapter.fromRouteInfo(routeInfo);; + apiRouteList.add(apiRouteInfo); + } + } + return apiRouteList; + } + + public void updateApiRouteStatus2Redis(String routeKey,String status) throws Exception { + RouteInfo routeInfo = routeDAO.queryRoute(routeKey); + if(routeInfo != null){ + routeInfo.setStatus(status); + routeDAO.saveRoute(routeKey,routeInfo); + }else{ + throw new Exception("service to be updated is not exist! Update failed"); + } + } +} + +class APIRouteAdapter { + public static RouteInfo toRouteInfo(ApiRouteInfo apiRouteInfo) { + RouteInfo routeInfo = new RouteInfo(); + routeInfo.setApiVersion(apiRouteInfo.getVersion()); + routeInfo.setStatus(apiRouteInfo.getStatus()); + + + Spec spec = new Spec(); + spec.setVisualRange(apiRouteInfo.getVisualRange()); + spec.setUrl(apiRouteInfo.getUrl().trim()); + spec.setPublish_port(apiRouteInfo.getPublish_port()); + spec.setHost(apiRouteInfo.getHost()); + spec.setApijson(apiRouteInfo.getApiJson()); + spec.setApijsontype(apiRouteInfo.getApiJsonType()); + spec.setMetricsUrl(apiRouteInfo.getMetricsUrl()); + spec.setConsulServiceName(apiRouteInfo.getConsulServiceName()); + spec.setUseOwnUpstream(apiRouteInfo.getUseOwnUpstream()); + spec.setPublish_protocol(apiRouteInfo.getPublishProtocol()); + spec.setEnable_ssl(apiRouteInfo.isEnable_ssl()); + spec.setControl(apiRouteInfo.getControl()); + RouteServer[] routeServers = apiRouteInfo.getServers(); + List nodeList = new ArrayList<>(); + for (RouteServer server: routeServers){ + Node node = new Node(); + node.setIp(server.getIp()); + node.setPort(Integer.parseInt(server.getPort())); + node.setWeight(server.getWeight()); + nodeList.add(node); + } + spec.setNodes(nodeList.toArray(new Node[]{})); + routeInfo.setSpec(spec); + + Metadata metadata = new Metadata(); + metadata.setName(apiRouteInfo.getServiceName()); + metadata.setNamespace(apiRouteInfo.getNamespace()); + Calendar now = Calendar.getInstance(); + now.set(Calendar.MILLISECOND, 0); + metadata.setUpdateTimestamp(now.getTime()); + routeInfo.setMetadata(metadata); + + return routeInfo; + } + + public static ApiRouteInfo fromRouteInfo(RouteInfo routeInfo) { + ApiRouteInfo apiRouteInfo = new ApiRouteInfo(); + + apiRouteInfo.setVersion(routeInfo.getApiVersion()); + apiRouteInfo.setStatus(routeInfo.getStatus()); + + Spec spec = routeInfo.getSpec(); + apiRouteInfo.setVisualRange(spec.getVisualRange()); + apiRouteInfo.setUrl(spec.getUrl()); + apiRouteInfo.setPublish_port(spec.getPublish_port()); + apiRouteInfo.setHost(spec.getHost()); + apiRouteInfo.setApiJson(spec.getApijson()); + apiRouteInfo.setApiJsonType(spec.getApijsontype()); + apiRouteInfo.setMetricsUrl(spec.getMetricsUrl()); + apiRouteInfo.setConsulServiceName(spec.getConsulServiceName()); + apiRouteInfo.setUseOwnUpstream(spec.getUseOwnUpstream()); + apiRouteInfo.setPublishProtocol(spec.getPublish_protocol()); + apiRouteInfo.setEnable_ssl(spec.isEnable_ssl()); + apiRouteInfo.setControl(spec.getControl()); + Node[] nodes = spec.getNodes(); + List routeServerList = new ArrayList<>(); + for (Node node: nodes){ + RouteServer routeServer = new RouteServer(); + routeServer.setIp(node.getIp()); + routeServer.setPort(String.valueOf(node.getPort())); + routeServer.setWeight(node.getWeight()); + routeServerList.add(routeServer); + } + apiRouteInfo.setServers(routeServerList.toArray(new RouteServer[]{})); + + Metadata metadata = routeInfo.getMetadata(); + apiRouteInfo.setServiceName(metadata.getName()); + apiRouteInfo.setNamespace(metadata.getNamespace()); + + return apiRouteInfo; + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/CustomRouteService.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/CustomRouteService.java new file mode 100644 index 0000000..6ea680e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/CustomRouteService.java @@ -0,0 +1,152 @@ +package org.onap.msb.apiroute.wrapper.service; + +import org.onap.msb.apiroute.api.CustomRouteInfo; +import org.onap.msb.apiroute.api.RouteServer; +import org.onap.msb.apiroute.wrapper.dao.DAOFactory; +import org.onap.msb.apiroute.wrapper.dao.route.IRouteDAO; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Metadata; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Node; +import org.onap.msb.apiroute.wrapper.dao.route.bean.RouteInfo; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Spec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + + +public class CustomRouteService { + private static final Logger LOGGER = LoggerFactory.getLogger(CustomRouteService.class); + + private static final CustomRouteService instance = new CustomRouteService(); + private IRouteDAO routeDAO = DAOFactory.getRouteDAO(); + + private CustomRouteService() { + } + + public static CustomRouteService getInstance() { + return instance; + } + + public void saveCustomRouteService2Redis(CustomRouteInfo customRouteInfo, String routeKey) throws Exception { + if(customRouteInfo ==null){ + throw new Exception("input customRouteInfo to be saved is null!"); + } + RouteInfo routeInfo = CustomRouteAdapter.toRouteInfo(customRouteInfo); + routeDAO.saveRoute(routeKey, routeInfo); + } + + public long deleteCustomRouteService2Redis(String routeKey) throws Exception { + return routeDAO.deleteRoute(routeKey); + } + + public long deleteMultiCustomRouteService2Redis(String routeKeyPattern) throws Exception { + return routeDAO.deleteMultiRoute(routeKeyPattern); + } + + public CustomRouteInfo getCustomRouteInstance(String routeKey) throws Exception { + CustomRouteInfo customRouteInfo = null; + RouteInfo routeInfo = null; + routeInfo = routeDAO.queryRoute(routeKey); + if(routeInfo!=null) { + customRouteInfo = CustomRouteAdapter.fromRouteInfo(routeInfo); + } + return customRouteInfo; + } + + public List getMultiCustomRouteInstances(String customRedisKeyPattern) throws Exception { + List customRouteList = new ArrayList<>(); + List routeInfoList = routeDAO.queryMultiRoute(customRedisKeyPattern); + for (RouteInfo routeInfo : routeInfoList) { + if (routeInfo != null) { + CustomRouteInfo customRouteInfo = CustomRouteAdapter.fromRouteInfo(routeInfo);; + customRouteList.add(customRouteInfo); + } + } + return customRouteList; + } + + public void updateCustomRouteStatus2Redis(String routeKey,String status) throws Exception { + RouteInfo routeInfo = routeDAO.queryRoute(routeKey); + if(routeInfo != null){ + routeInfo.setStatus(status); + routeDAO.saveRoute(routeKey,routeInfo); + }else{ + throw new Exception("service to be updated is not exist! Update failed"); + } + } + +} + +class CustomRouteAdapter { + public static RouteInfo toRouteInfo(CustomRouteInfo customRouteInfo) { + RouteInfo routeInfo = new RouteInfo(); + routeInfo.setStatus(customRouteInfo.getStatus()); + + + Spec spec = new Spec(); + spec.setVisualRange(customRouteInfo.getVisualRange()); + spec.setUrl(customRouteInfo.getUrl().trim()); + spec.setPublish_port(customRouteInfo.getPublish_port()); + spec.setHost(customRouteInfo.getHost()); + spec.setConsulServiceName(customRouteInfo.getConsulServiceName()); + spec.setUseOwnUpstream(customRouteInfo.getUseOwnUpstream()); + spec.setPublish_protocol(customRouteInfo.getPublishProtocol()); + spec.setEnable_ssl(customRouteInfo.isEnable_ssl()); + spec.setControl(customRouteInfo.getControl()); + RouteServer[] routeServers = customRouteInfo.getServers(); + List nodeList = new ArrayList<>(); + for (RouteServer server: routeServers){ + Node node = new Node(); + node.setIp(server.getIp()); + node.setPort(Integer.parseInt(server.getPort())); + node.setWeight(server.getWeight()); + nodeList.add(node); + } + spec.setNodes(nodeList.toArray(new Node[]{})); + routeInfo.setSpec(spec); + + Metadata metadata = new Metadata(); + metadata.setName(customRouteInfo.getServiceName()); + metadata.setNamespace(customRouteInfo.getNamespace()); + Calendar now = Calendar.getInstance(); + now.set(Calendar.MILLISECOND, 0); + metadata.setUpdateTimestamp(now.getTime()); + routeInfo.setMetadata(metadata); + + return routeInfo; + } + + public static CustomRouteInfo fromRouteInfo(RouteInfo routeInfo) { + CustomRouteInfo customRouteInfo = new CustomRouteInfo(); + customRouteInfo.setStatus(routeInfo.getStatus()); + + Spec spec = routeInfo.getSpec(); + customRouteInfo.setVisualRange(spec.getVisualRange()); + customRouteInfo.setUrl(spec.getUrl()); + customRouteInfo.setPublish_port(spec.getPublish_port()); + customRouteInfo.setHost(spec.getHost()); + customRouteInfo.setConsulServiceName(spec.getConsulServiceName()); + customRouteInfo.setUseOwnUpstream(spec.getUseOwnUpstream()); + customRouteInfo.setPublishProtocol(spec.getPublish_protocol()); + customRouteInfo.setEnable_ssl(spec.isEnable_ssl()); + customRouteInfo.setControl(spec.getControl()); + Node[] nodes = spec.getNodes(); + List routeServerList = new ArrayList<>(); + for (Node node: nodes){ + RouteServer routeServer = new RouteServer(); + routeServer.setIp(node.getIp()); + routeServer.setPort(String.valueOf(node.getPort())); + routeServer.setWeight(node.getWeight()); + routeServerList.add(routeServer); + } + customRouteInfo.setServers(routeServerList.toArray(new RouteServer[]{})); + + Metadata metadata = routeInfo.getMetadata(); + customRouteInfo.setServiceName(metadata.getName()); + customRouteInfo.setNamespace(metadata.getNamespace()); + + return customRouteInfo; + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/IuiRouteService.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/IuiRouteService.java new file mode 100644 index 0000000..c46cb75 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/IuiRouteService.java @@ -0,0 +1,151 @@ +package org.onap.msb.apiroute.wrapper.service; + +import org.onap.msb.apiroute.api.IuiRouteInfo; +import org.onap.msb.apiroute.api.RouteServer; +import org.onap.msb.apiroute.wrapper.dao.DAOFactory; +import org.onap.msb.apiroute.wrapper.dao.route.IRouteDAO; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Metadata; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Node; +import org.onap.msb.apiroute.wrapper.dao.route.bean.RouteInfo; +import org.onap.msb.apiroute.wrapper.dao.route.bean.Spec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + + +public class IuiRouteService { + private static final Logger LOGGER = LoggerFactory.getLogger(CustomRouteService.class); + + private static final IuiRouteService instance = new IuiRouteService(); + private IRouteDAO routeDAO = DAOFactory.getRouteDAO(); + + private IuiRouteService() { + } + + public static IuiRouteService getInstance() { + return instance; + } + + public void saveIuiRouteService2Redis(IuiRouteInfo iuiRouteInfo, String routeKey) throws Exception { + if(iuiRouteInfo ==null){ + throw new Exception("input apiRouteInfo to be saved is null!"); + } + RouteInfo routeInfo = IuiRouteAdapter.toRouteInfo(iuiRouteInfo); + routeDAO.saveRoute(routeKey, routeInfo); + } + + public long deleteIuiRouteService2Redis(String routeKey) throws Exception { + return routeDAO.deleteRoute(routeKey); + } + + public long deleteMultiIuiRouteService2Redis(String routeKeyPattern) throws Exception { + return routeDAO.deleteMultiRoute(routeKeyPattern); + } + + public IuiRouteInfo getIuiRouteInstance(String routeKey) throws Exception { + IuiRouteInfo iuiRouteInfo = null; + RouteInfo routeInfo = null; + routeInfo = routeDAO.queryRoute(routeKey); + if(routeInfo!=null) { + iuiRouteInfo = IuiRouteAdapter.fromRouteInfo(routeInfo); + } + return iuiRouteInfo; + } + + public List getMultiIuiRouteInstances(String apiRedisKeyPattern) throws Exception { + List iuiRouteList = new ArrayList<>(); + List routeInfoList = routeDAO.queryMultiRoute(apiRedisKeyPattern); + for (RouteInfo routeInfo : routeInfoList) { + if (routeInfo != null) { + IuiRouteInfo iuiRouteInfo = IuiRouteAdapter.fromRouteInfo(routeInfo);; + iuiRouteList.add(iuiRouteInfo); + } + } + return iuiRouteList; + } + + public void updateIuiRouteStatus2Redis(String routeKey,String status) throws Exception { + RouteInfo routeInfo = routeDAO.queryRoute(routeKey); + if(routeInfo != null){ + routeInfo.setStatus(status); + routeDAO.saveRoute(routeKey,routeInfo); + }else{ + throw new Exception("service to be updated is not exist! Update failed"); + } + } + +} + +class IuiRouteAdapter { + public static RouteInfo toRouteInfo(IuiRouteInfo iuiRouteInfo) { + RouteInfo routeInfo = new RouteInfo(); + routeInfo.setStatus(iuiRouteInfo.getStatus()); + + + Spec spec = new Spec(); + spec.setVisualRange(iuiRouteInfo.getVisualRange()); + spec.setUrl(iuiRouteInfo.getUrl().trim()); + spec.setPublish_port(iuiRouteInfo.getPublish_port()); + spec.setHost(iuiRouteInfo.getHost()); + spec.setConsulServiceName(iuiRouteInfo.getConsulServiceName()); + spec.setUseOwnUpstream(iuiRouteInfo.getUseOwnUpstream()); + spec.setPublish_protocol(iuiRouteInfo.getPublishProtocol()); + spec.setEnable_ssl(iuiRouteInfo.isEnable_ssl()); + spec.setControl(iuiRouteInfo.getControl()); + RouteServer[] routeServers = iuiRouteInfo.getServers(); + List nodeList = new ArrayList<>(); + for (RouteServer server: routeServers){ + Node node = new Node(); + node.setIp(server.getIp()); + node.setPort(Integer.parseInt(server.getPort())); + node.setWeight(server.getWeight()); + nodeList.add(node); + } + spec.setNodes(nodeList.toArray(new Node[]{})); + routeInfo.setSpec(spec); + + Metadata metadata = new Metadata(); + metadata.setName(iuiRouteInfo.getServiceName()); + metadata.setNamespace(iuiRouteInfo.getNamespace()); + Calendar now = Calendar.getInstance(); + now.set(Calendar.MILLISECOND, 0); + metadata.setUpdateTimestamp(now.getTime()); + routeInfo.setMetadata(metadata); + return routeInfo; + } + + public static IuiRouteInfo fromRouteInfo(RouteInfo routeInfo) { + IuiRouteInfo iuiRouteInfo = new IuiRouteInfo(); + iuiRouteInfo.setStatus(routeInfo.getStatus()); + + Spec spec = routeInfo.getSpec(); + iuiRouteInfo.setVisualRange(spec.getVisualRange()); + iuiRouteInfo.setUrl(spec.getUrl()); + iuiRouteInfo.setPublish_port(spec.getPublish_port()); + iuiRouteInfo.setHost(spec.getHost()); + iuiRouteInfo.setConsulServiceName(spec.getConsulServiceName()); + iuiRouteInfo.setUseOwnUpstream(spec.getUseOwnUpstream()); + iuiRouteInfo.setPublishProtocol(spec.getPublish_protocol()); + iuiRouteInfo.setEnable_ssl(spec.isEnable_ssl()); + iuiRouteInfo.setControl(spec.getControl()); + Node[] nodes = spec.getNodes(); + List routeServerList = new ArrayList<>(); + for (Node node: nodes){ + RouteServer routeServer = new RouteServer(); + routeServer.setIp(node.getIp()); + routeServer.setPort(String.valueOf(node.getPort())); + routeServer.setWeight(node.getWeight()); + routeServerList.add(routeServer); + } + iuiRouteInfo.setServers(routeServerList.toArray(new RouteServer[]{})); + + Metadata metadata = routeInfo.getMetadata(); + iuiRouteInfo.setServiceName(metadata.getName()); + iuiRouteInfo.setNamespace(metadata.getNamespace()); + + return iuiRouteInfo; + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/MicroServiceFullService.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/MicroServiceFullService.java new file mode 100644 index 0000000..af005ae --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/service/MicroServiceFullService.java @@ -0,0 +1,220 @@ +package org.onap.msb.apiroute.wrapper.service; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.wrapper.dao.DAOFactory; +import org.onap.msb.apiroute.wrapper.dao.RedisAccessWrapper; +import org.onap.msb.apiroute.wrapper.dao.service.IServiceDAO; +import org.onap.msb.apiroute.wrapper.dao.service.bean.Metadata; +import org.onap.msb.apiroute.wrapper.dao.service.bean.ServiceInfo; +import org.onap.msb.apiroute.wrapper.dao.service.bean.Spec; +import org.onap.msb.apiroute.wrapper.util.MicroServiceUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableSet; + +public class MicroServiceFullService { + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceFullService.class); + + private static MicroServiceFullService instance = new MicroServiceFullService(); + + private IServiceDAO serviceDAO = DAOFactory.getServiceDAO(); + + private MicroServiceFullService() { + } + + public static MicroServiceFullService getInstance() { + return instance; + } + + public List getAllMicroServiceInstances() throws Exception { + String serviceKeyPattern = MicroServiceUtil.getPrefixedKey("*"); + + List microServiceFullInfoList = new ArrayList<>(); + List serviceInfoList = serviceDAO.queryMultiService(serviceKeyPattern); + for (ServiceInfo serviceInfo : serviceInfoList) { + if (serviceInfo != null) { + MicroServiceFullInfo microServiceFullInfo = MicroServiceFullAdapter.fromServiceInfo(serviceInfo); + ; + microServiceFullInfoList.add(microServiceFullInfo); + } + } + return microServiceFullInfoList; + } + + public Set getAllMicroServiceKey() throws Exception { + final Set builder = new HashSet(); + + String serviceKeyPattern = MicroServiceUtil.getPrefixedKey("*"); + Set serviceKeySet = RedisAccessWrapper.filterKeys(serviceKeyPattern); + + Pattern serviceKeyRegexPattern = MicroServiceUtil.getServiceKeyRegexPattern(); + for (String serviceKey : serviceKeySet) { + Matcher matcher = serviceKeyRegexPattern.matcher(serviceKey); + if (matcher.matches()) { + builder.add(matcher.group("servicename")); + } + } + return builder; + } + + public void saveMicroServiceInfo2Redis(MicroServiceFullInfo microServiceFullInfo) throws Exception { + if(microServiceFullInfo ==null){ + throw new Exception("input microServiceInfo to be saved is null!"); + } + ServiceInfo serviceInfo = MicroServiceFullAdapter.toServiceInfo(microServiceFullInfo); + String serviceKey = MicroServiceUtil.getServiceKey(microServiceFullInfo.getServiceName(),microServiceFullInfo.getVersion()); + serviceDAO.saveService(serviceKey,serviceInfo); + } + + public void updateMicroServiceStatus(String serviceName, String version, String status) + throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + String serviceKey = MicroServiceUtil.getServiceKey(serviceName, version); + ServiceInfo serviceInfo = serviceDAO.queryService(serviceKey); + if(serviceInfo != null){ + serviceInfo.setStatus(status); + serviceDAO.saveService(serviceKey,serviceInfo); + } + } + + public boolean existsMicroServiceInstance(String serviceName, String version) + throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + String serviceKey = MicroServiceUtil.getServiceKey(serviceName, version); + return RedisAccessWrapper.isExist(serviceKey); + } + + public MicroServiceFullInfo getMicroServiceInstance(String serviceName, String version) + throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + String serviceKey = MicroServiceUtil.getServiceKey(serviceName, version); + + MicroServiceFullInfo microServiceInfo = null; + + ServiceInfo serviceInfo = null; + serviceInfo = serviceDAO.queryService(serviceKey); + if(serviceInfo!=null) { + microServiceInfo = MicroServiceFullAdapter.fromServiceInfo(serviceInfo); + } + return microServiceInfo; + } + + /** + * query all the versions of the given ServiceName + * @param serviceName + * @return + * @throws Exception + */ + public List getAllVersionsOfTheService(String serviceName) throws Exception { + String serviceKeyPattern = MicroServiceUtil.getPrefixedKey(serviceName, "*"); + + List microServiceFullInfoList = new ArrayList<>(); + List serviceInfoList = serviceDAO.queryMultiService(serviceKeyPattern); + for (ServiceInfo serviceInfo : serviceInfoList) { + if (serviceInfo != null) { + MicroServiceFullInfo microServiceFullInfo = MicroServiceFullAdapter.fromServiceInfo(serviceInfo); + microServiceFullInfoList.add(microServiceFullInfo); + } + } + return microServiceFullInfoList; + } + + public void deleteMicroService(String serviceName, String version) throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + String serviceKey = MicroServiceUtil.getServiceKey(serviceName, version); + serviceDAO.deleteService(serviceKey); + } + + public long deleteMultiMicroService(String keyPattern) throws Exception { + return serviceDAO.deleteMultiService(keyPattern); + } +} + +class MicroServiceFullAdapter { + public static ServiceInfo toServiceInfo(MicroServiceFullInfo microServiceFullInfo) { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setApiVersion(microServiceFullInfo.getVersion()); + serviceInfo.setStatus(microServiceFullInfo.getStatus()); + + + Spec spec = new Spec(); + spec.setVisualRange(microServiceFullInfo.getVisualRange()); + spec.setUrl(microServiceFullInfo.getUrl()); + spec.setPublish_port(microServiceFullInfo.getPublish_port()); + spec.setHost(microServiceFullInfo.getHost()); + spec.setProtocol(microServiceFullInfo.getProtocol()); + spec.setLb_policy(microServiceFullInfo.getLb_policy()); + spec.setEnable_ssl(microServiceFullInfo.isEnable_ssl()); + Set nodeSet = microServiceFullInfo.getNodes(); + List serviceNodeList = new ArrayList<>(); + for (org.onap.msb.apiroute.api.Node node : nodeSet) { + org.onap.msb.apiroute.wrapper.dao.service.bean.Node serviceNode = new org.onap.msb.apiroute.wrapper.dao.service.bean.Node(); + serviceNode.setIp(node.getIp()); + serviceNode.setPort(node.getPort()); + serviceNode.setTtl(node.getTtl()); + serviceNodeList.add(serviceNode); + } + spec.setNodes(serviceNodeList.toArray(new org.onap.msb.apiroute.wrapper.dao.service.bean.Node[]{})); + serviceInfo.setSpec(spec); + + Metadata metadata = new Metadata(); + metadata.setName(microServiceFullInfo.getServiceName()); + metadata.setNamespace(microServiceFullInfo.getNamespace()); + Calendar now = Calendar.getInstance(); + now.set(Calendar.MILLISECOND, 0); + metadata.setUpdateTimestamp(now.getTime()); + serviceInfo.setMetadata(metadata); + + return serviceInfo; + } + + public static MicroServiceFullInfo fromServiceInfo(ServiceInfo serviceInfo) { + MicroServiceFullInfo microServiceFullInfo = new MicroServiceFullInfo(); + + microServiceFullInfo.setVersion(serviceInfo.getApiVersion()); + microServiceFullInfo.setStatus(serviceInfo.getStatus()); + + Spec spec = serviceInfo.getSpec(); + microServiceFullInfo.setVisualRange(spec.getVisualRange()); + microServiceFullInfo.setUrl(spec.getUrl()); + microServiceFullInfo.setPath(spec.getPath()); + microServiceFullInfo.setPublish_port(spec.getPublish_port()); + microServiceFullInfo.setHost(spec.getHost()); + microServiceFullInfo.setProtocol(spec.getProtocol()); + microServiceFullInfo.setLb_policy(spec.getLb_policy()); + microServiceFullInfo.setEnable_ssl(spec.isEnable_ssl()); + org.onap.msb.apiroute.wrapper.dao.service.bean.Node[] serviceNodes = spec.getNodes(); + List nodeList = new ArrayList<>(); + for (org.onap.msb.apiroute.wrapper.dao.service.bean.Node serviceNode : serviceNodes) { + org.onap.msb.apiroute.api.Node node = new org.onap.msb.apiroute.api.Node(); + node.setIp(serviceNode.getIp()); + node.setPort(String.valueOf(serviceNode.getPort())); + node.setTtl(serviceNode.getTtl()); + nodeList.add(node); + } + microServiceFullInfo.setNodes(new HashSet(nodeList)); + + Metadata metadata = serviceInfo.getMetadata(); + microServiceFullInfo.setServiceName(metadata.getName()); + microServiceFullInfo.setNamespace(metadata.getNamespace()); + + return microServiceFullInfo; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/IMicroServiceChangeListener.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/IMicroServiceChangeListener.java new file mode 100644 index 0000000..68e31f8 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/IMicroServiceChangeListener.java @@ -0,0 +1,37 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper.serviceListener; + +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.api.Node; + + +public interface IMicroServiceChangeListener { + public void onSave(MicroServiceFullInfo microServiceInfo) throws Exception; + + public void onDelete(MicroServiceFullInfo microServiceInfo) throws Exception; + + public void onChange(String serviceName,String version,MicroServiceFullInfo microServiceInfo) throws Exception; + + public void onStatusChange(String serviceName,String version,String host, String protocol,String publish_port, + String status); + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/MicroServiceChangeListener.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/MicroServiceChangeListener.java new file mode 100644 index 0000000..73b6a9c --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/MicroServiceChangeListener.java @@ -0,0 +1,779 @@ +package org.onap.msb.apiroute.wrapper.serviceListener; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.api.ApiRouteInfo; +import org.onap.msb.apiroute.api.CustomRouteInfo; +import org.onap.msb.apiroute.api.DiscoverInfo; +import org.onap.msb.apiroute.api.IuiRouteInfo; +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.api.Node; +import org.onap.msb.apiroute.api.PublishFullAddress; +import org.onap.msb.apiroute.api.RouteServer; +import org.onap.msb.apiroute.wrapper.ApiRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.CustomRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.IuiRouteServiceWrapper; +import org.onap.msb.apiroute.wrapper.util.CommonUtil; +import org.onap.msb.apiroute.wrapper.util.ConfigUtil; +import org.onap.msb.apiroute.wrapper.util.HttpClientUtil; +import org.onap.msb.apiroute.wrapper.util.JacksonJsonUtil; +import org.onap.msb.apiroute.wrapper.util.RegExpTestUtil; +import org.onap.msb.apiroute.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class MicroServiceChangeListener implements IMicroServiceChangeListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceChangeListener.class); + + @Override + public void onDelete(MicroServiceFullInfo microServiceInfo) throws Exception { + + String path = microServiceInfo.getPath(); + + String[] routeWay = ConfigUtil.getInstance().getRouteWay(); + + for (int i = 0; i < routeWay.length; i++) { + + if (StringUtils.isNotBlank(path) && !"/".equals(path)) { + // 1.按path优先判断类型 + String host = getHost(microServiceInfo); + deleteServiceByUrl(path, host, microServiceInfo.getPublish_port(), routeWay[i]); + } else { + // 2.1 域å判断url + if (RouteUtil.ROUTEWAY_DOMAIN.equals(routeWay[i]) && ifRootByDomain(microServiceInfo)) { + deleteServiceByDomain4Root(microServiceInfo); + } else { + // 2.2 按å议优先判断类型 + deleteServiceByProtocol(microServiceInfo, routeWay[i]); + } + + } + + + } + + } + + + @Override + public void onSave(MicroServiceFullInfo microServiceInfo) throws Exception { + + String path = microServiceInfo.getPath(); + String[] routeWay = ConfigUtil.getInstance().getRouteWay(); + + for (int i = 0; i < routeWay.length; i++) { + // 1.按path优先判断类型 + if (StringUtils.isNotBlank(path) && !"/".equals(path)) { + saveServiceByPath(microServiceInfo, routeWay[i]); + } else { + // 2.1 域å判断url + if (RouteUtil.ROUTEWAY_DOMAIN.equals(routeWay[i]) && ifRootByDomain(microServiceInfo)) { + saveServiceByDomain4Root(microServiceInfo); + } else { + // 2.2 按å议优先判断类型 + saveServiceByProtocol(microServiceInfo, routeWay[i]); + } + } + } + + } + + + //判断按åè®®å‘布地å€æ˜¯å¦å’Œæ³¨å†Œçš„URL一致,如果一致å‘布地å€ä¿å­˜ä¸º/,å¦åˆ™ä¿å­˜ä¸ºå议类型的å‘å¸ƒåœ°å€ + private boolean ifRootByDomain(MicroServiceFullInfo microServiceInfo){ + + + if("/".equals(microServiceInfo.getUrl())) return true; + + String protocol = microServiceInfo.getProtocol(); + String routeName = + RouteUtil.getRouteNameByns(microServiceInfo.getServiceName(), + microServiceInfo.getNamespace()); + String publishUrl=""; + String version = ""; + if (StringUtils.isNotBlank(microServiceInfo.getVersion())) { + version = "/" + microServiceInfo.getVersion(); + } + + switch (protocol) { + case RouteUtil.PROTOCOL_UI: + publishUrl = "/iui/" + routeName; + break; + case RouteUtil.PROTOCOL_REST: + publishUrl = "/api/" + routeName + version; + break; + case RouteUtil.PROTOCOL_HTTP: + publishUrl = "/" + routeName + version; + break; + } + return publishUrl.equals(microServiceInfo.getUrl()); + + } + + + private void saveServiceByDomain4Root(MicroServiceFullInfo microServiceInfo) throws Exception { + + CustomRouteInfo[] customRouteInfos = + this.buildCustomRouteInfo(microServiceInfo, "/", RouteUtil.ROUTEWAY_DOMAIN); + for (int i = 0; i < customRouteInfos.length; i++) { + customRouteInfos[i].setUrl("/"); + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfos[i], + RouteUtil.ROUTEWAY_DOMAIN); + } + } + + private void deleteServiceByDomain4Root(MicroServiceFullInfo microServiceInfo) throws Exception { + + CustomRouteServiceWrapper.getInstance().deleteCustomRoute("/", getHost(microServiceInfo), + microServiceInfo.getPublish_port(), RouteUtil.ROUTEWAY_DOMAIN); + } + + /** + * @Title saveServiceByProtocol + * @Description TODO(按用户注册åè®®ä¿å­˜æœåŠ¡) + * @param microServiceInfo + * @param routeWay + * @return void + * @throws Exception + */ + private void saveServiceByProtocol(MicroServiceFullInfo microServiceInfo, String routeWay) + throws Exception { + String protocol = microServiceInfo.getProtocol(); + String routeName = + RouteUtil.getRouteNameByns(microServiceInfo.getServiceName(), + microServiceInfo.getNamespace()); + + switch (protocol) { + case RouteUtil.PROTOCOL_UI: + IuiRouteInfo[] iuiRouteInfos = + this.buildIuiRouteInfo(microServiceInfo, routeName, routeWay); + for (int i = 0; i < iuiRouteInfos.length; i++) { + IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(iuiRouteInfos[i], routeWay); + } + break; + + case RouteUtil.PROTOCOL_REST: + + ApiRouteInfo[] apiRouteInfos = + this.buildApiRouteInfo(microServiceInfo, routeName, microServiceInfo.getVersion(), + routeWay); + for (int i = 0; i < apiRouteInfos.length; i++) { + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(apiRouteInfos[i], routeWay); + } + break; + case RouteUtil.PROTOCOL_HTTP: + CustomRouteInfo[] customRouteInfos = + this.buildCustomRouteInfo(microServiceInfo, + getHttpName(routeName, microServiceInfo.getVersion()), routeWay); + for (int i = 0; i < customRouteInfos.length; i++) { + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfos[i], + routeWay); + } + break; + } + } + + /** + * @Title deleteServiceByProtocol + * @Description TODO(按用户注册å议删除æœåŠ¡) + * @param microServiceInfo + * @param routeWay + * @return void + */ + private void deleteServiceByProtocol(MicroServiceFullInfo microServiceInfo, String routeWay) { + String protocol = microServiceInfo.getProtocol(); + String host = getHost(microServiceInfo); + String routeName = + RouteUtil.getRouteNameByns(microServiceInfo.getServiceName(), + microServiceInfo.getNamespace()); + + if (RouteUtil.PROTOCOL_UI.equals(protocol)) { + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + // two ports + String[] publishPorts = StringUtils.split(microServiceInfo.getPublish_port(), "|"); + if (publishPorts.length == 2) { + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(routeName, host, publishPorts[0], + routeWay); + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(routeName, host, publishPorts[1], + routeWay); + return; + } + } + + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(routeName, host, + microServiceInfo.getPublish_port(), routeWay); + } else if (RouteUtil.PROTOCOL_REST.equals(protocol)) { + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + // two ports + String[] publishPorts = StringUtils.split(microServiceInfo.getPublish_port(), "|"); + if (publishPorts.length == 2) { + ApiRouteServiceWrapper.getInstance().deleteApiRoute(routeName, + microServiceInfo.getVersion(), host, publishPorts[0], routeWay); + ApiRouteServiceWrapper.getInstance().deleteApiRoute(routeName, + microServiceInfo.getVersion(), host, publishPorts[1], routeWay); + return; + } + } + ApiRouteServiceWrapper.getInstance().deleteApiRoute(routeName, microServiceInfo.getVersion(), + host, microServiceInfo.getPublish_port(), routeWay); + } else if (RouteUtil.PROTOCOL_HTTP.equals(protocol)) { + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + // two ports + String[] publishPorts = StringUtils.split(microServiceInfo.getPublish_port(), "|"); + if (publishPorts.length == 2) { + CustomRouteServiceWrapper.getInstance().deleteCustomRoute( + getHttpName(routeName, microServiceInfo.getVersion()), host, publishPorts[0], + routeWay); + CustomRouteServiceWrapper.getInstance().deleteCustomRoute( + getHttpName(routeName, microServiceInfo.getVersion()), host, publishPorts[1], + routeWay); + return; + } + } + CustomRouteServiceWrapper.getInstance().deleteCustomRoute( + getHttpName(routeName, microServiceInfo.getVersion()), host, + microServiceInfo.getPublish_port(), routeWay); + } + } + + /** + * @Title saveServiceByUrl + * @Description TODO(按URL地å€åˆ¤æ–­æœåŠ¡å议并ä¿å­˜åˆ°è·¯ç”±è¡¨) + * @param url + * @param microServiceInfo + * @param routeWay + * @return void + * @throws Exception + */ + private void saveServiceByPath(MicroServiceFullInfo microServiceInfo, String routeWay) + throws Exception { + String redis_serviceName; + String path=microServiceInfo.getPath(); + if (RegExpTestUtil.apiRouteUrlRegExpTest(path)) { + // protocol:"REST" + String[] serviceKey = RegExpTestUtil.apiServiceNameMatch4URL(path); + if (serviceKey == null) { + LOGGER.error("save api Service ByUrl is error:[url]" + path); + return; + } + redis_serviceName = serviceKey[0]; + String redis_serviceVersion = serviceKey[1]; + + ApiRouteInfo[] apiRouteInfos = + this.buildApiRouteInfo(microServiceInfo, redis_serviceName, redis_serviceVersion, + routeWay); + for (int i = 0; i < apiRouteInfos.length; i++) { + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(apiRouteInfos[i], routeWay); + } + } else if (RegExpTestUtil.iuiRouteUrlRegExpTest(path)) { + // protocol:"UI" + // æ ¹æ®url获å–æœåŠ¡å + redis_serviceName = RegExpTestUtil.iuiServiceNameMatch4URL(path); + if (redis_serviceName == null) { + LOGGER.error("save iui Service ByUrl is error:[url]" + path); + return; + } + IuiRouteInfo[] iuiRouteInfos = + this.buildIuiRouteInfo(microServiceInfo, redis_serviceName, routeWay); + for (int i = 0; i < iuiRouteInfos.length; i++) { + IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(iuiRouteInfos[i], routeWay); + } + } else { + // protocol:"HTTP"; + redis_serviceName = path; + CustomRouteInfo[] customRouteInfos = + this.buildCustomRouteInfo(microServiceInfo, redis_serviceName, routeWay); + for (int i = 0; i < customRouteInfos.length; i++) { + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfos[i], + routeWay); + } + } + } + + /** + * @Title deleteServiceByUrl + * @Description TODO(按URL地å€åˆ¤æ–­æœåŠ¡å议并从路由表删除) + * @param url + * @param host + * @param publish_port + * @param routeWay + * @return void + */ + private void deleteServiceByUrl(String url, String host, String publish_port, String routeWay) { + // æ ¹æ®Urlæ ¼å¼åˆ¤æ–­æœåŠ¡ç±»åž‹ + String redis_serviceName; + + if (RegExpTestUtil.apiRouteUrlRegExpTest(url)) { + // protocol:"REST" + String[] serviceKey = RegExpTestUtil.apiServiceNameMatch4URL(url); + if (serviceKey == null) { + LOGGER.error("delete api Service ByUrl is error:[url]" + url); + return; + } + + redis_serviceName = serviceKey[0]; + String redis_serviceVersion = serviceKey[1]; + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + // two ports + String[] publishPorts = StringUtils.split(publish_port, "|"); + if (publishPorts.length == 2) { + ApiRouteServiceWrapper.getInstance().deleteApiRoute(redis_serviceName, + redis_serviceVersion, host, publishPorts[0], routeWay); + ApiRouteServiceWrapper.getInstance().deleteApiRoute(redis_serviceName, + redis_serviceVersion, host, publishPorts[1], routeWay); + return; + } + } + + ApiRouteServiceWrapper.getInstance().deleteApiRoute(redis_serviceName, redis_serviceVersion, + host, publish_port, routeWay); + + + + } else if (RegExpTestUtil.iuiRouteUrlRegExpTest(url)) { + // protocol:"UI" + // æ ¹æ®url获å–æœåŠ¡å + redis_serviceName = RegExpTestUtil.iuiServiceNameMatch4URL(url); + if (redis_serviceName == null) { + LOGGER.error("delete iui Service ByUrl is error:[url]" + url); + return; + } + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + // two ports + String[] publishPorts = StringUtils.split(publish_port, "|"); + if (publishPorts.length == 2) { + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(redis_serviceName, host, + publishPorts[0], routeWay); + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(redis_serviceName, host, + publishPorts[1], routeWay); + return; + } + } + + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(redis_serviceName, host, publish_port, + routeWay); + + + } else { + // protocol:"HTTP"; + redis_serviceName = url; + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + // two ports + String[] publishPorts = StringUtils.split(publish_port, "|"); + if (publishPorts.length == 2) { + CustomRouteServiceWrapper.getInstance().deleteCustomRoute(redis_serviceName, host, + publishPorts[0], routeWay); + CustomRouteServiceWrapper.getInstance().deleteCustomRoute(redis_serviceName, host, + publishPorts[1], routeWay); + return; + } + } + + CustomRouteServiceWrapper.getInstance().deleteCustomRoute(redis_serviceName, host, + publish_port, routeWay); + } + + } + + + + /** + * @Title getCustomName + * @Description TODO(获å–HTTPæœåŠ¡è·¯ç”±å) + * @param routeName + * @param version + * @return + * @return String + */ + private String getHttpName(String routeName, String version) { + if (!routeName.startsWith("/")) { + routeName = "/" + routeName; + } + + if (StringUtils.isNotBlank(version)) { + routeName += "/" + version; + } + return routeName; + } + + + private String getHost(MicroServiceFullInfo microServiceInfo) { + String host; + if (StringUtils.isNotBlank(microServiceInfo.getHost())) { + host = microServiceInfo.getHost().toLowerCase(); + } else { + // host为空,å–默认规则 æœåŠ¡å-ns + host = microServiceInfo.getServiceName().toLowerCase(); + } + + return host; + } + + + + @Override + public void onChange(String serviceName, String version, MicroServiceFullInfo microServiceInfo) + throws Exception { + // TODO Auto-generated method stub + + if (RouteUtil.PROTOCOL_UI.equals(microServiceInfo.getProtocol())) { + IuiRouteInfo[] iuiRouteInfos = + this.buildIuiRouteInfo(microServiceInfo, serviceName, RouteUtil.ROUTEWAY_IP); + for (int i = 0; i < iuiRouteInfos.length; i++) { + IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(iuiRouteInfos[i], + RouteUtil.ROUTEWAY_IP); + } + } else if (RouteUtil.PROTOCOL_REST.equals(microServiceInfo.getProtocol())) { + ApiRouteInfo[] apiRouteInfos = + this.buildApiRouteInfo(microServiceInfo, serviceName, version, RouteUtil.ROUTEWAY_IP); + for (int i = 0; i < apiRouteInfos.length; i++) { + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(apiRouteInfos[i], + RouteUtil.ROUTEWAY_IP); + } + } else if (RouteUtil.PROTOCOL_HTTP.equals(microServiceInfo.getProtocol())) { + if (!serviceName.startsWith("/")) { + serviceName = "/" + serviceName; + } + CustomRouteInfo[] customRouteInfos = + this.buildCustomRouteInfo(microServiceInfo, serviceName, RouteUtil.ROUTEWAY_IP); + for (int i = 0; i < customRouteInfos.length; i++) { + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfos[i], + RouteUtil.ROUTEWAY_IP); + } + } + } + + + @Override + public void onStatusChange(String serviceName, String version, String host, String protocol, + String publish_port, String status) { + + // 获å–æœåŠ¡çš„host + + if (StringUtils.isBlank(host)) { + host = serviceName.toLowerCase(); + } + + if (RouteUtil.PROTOCOL_UI.equals(protocol)) { + + IuiRouteServiceWrapper.getInstance().updateIuiRouteStatus(serviceName, host, publish_port, + status, RouteUtil.ROUTEWAY_IP); + + } else if (RouteUtil.PROTOCOL_REST.equals(protocol)) { + ApiRouteServiceWrapper.getInstance().updateApiRouteStatus(serviceName, version, host, + publish_port, status, RouteUtil.ROUTEWAY_IP); + + } else if (RouteUtil.PROTOCOL_HTTP.equals(protocol)) { + if (!serviceName.startsWith("/")) { + serviceName = "/" + serviceName; + } + CustomRouteServiceWrapper.getInstance().updateCustomRouteStatus(serviceName, host, + publish_port, status, RouteUtil.ROUTEWAY_IP); + } + + + } + + private boolean buildRouteHttpProtocol(MicroServiceFullInfo microServiceInfo, String routeWay) { + + // Portalåè®®å¤„ç† + if (RouteUtil.CUSTOM_PORTAL.equals(microServiceInfo.getCustom())) { + if (RouteUtil.ROUTEWAY_DOMAIN.equals(routeWay)) { + return true; + } else { + return false; + } + } + + // 自定义开å¯SSLå¤„ç† + return microServiceInfo.isEnable_ssl(); + + } + + private RouteServer[] buildRouteNodes(MicroServiceFullInfo microServiceInfo, String routeWay) { + + // 针对custom=portal场景的域å路由使用apigatewayå‘布地å€ä½œä¸ºnode + if (RouteUtil.CUSTOM_PORTAL.equals(microServiceInfo.getCustom())) { + if (RouteUtil.ROUTEWAY_DOMAIN.equals(routeWay)) { + + String discoverServiceName = + RouteUtil.getRouteNameByns(microServiceInfo.getServiceName(), + microServiceInfo.getNamespace()); + List publishNodes = + getPublishNodes(discoverServiceName, microServiceInfo.getVersion(), + microServiceInfo.getNamespace()); + if (publishNodes != null && publishNodes.size() > 0) { + RouteServer[] routeServers = new RouteServer[publishNodes.size()]; + int i = 0; + for (Node node : publishNodes) { + RouteServer routeServer = new RouteServer(node.getIp(), node.getPort()); + routeServers[i] = routeServer; + i++; + } + return routeServers; + } + } + } + + + Set nodes = microServiceInfo.getNodes(); + RouteServer[] routeServers = new RouteServer[nodes.size()]; + int n = 0; + for (Node node : nodes) { + RouteServer routeServer = new RouteServer(node.getIp(), node.getPort()); + routeServers[n] = routeServer; + n++; + } + + return routeServers; + + } + + /** + * From MicroServiceInfo to ApiRouteInfo + * + * @param microServiceInfo + * @return + */ + private ApiRouteInfo[] buildApiRouteInfo(MicroServiceFullInfo microServiceInfo, + String redis_serviceName, String redis_version, String routeWay) { + + ApiRouteInfo apiRouteInfo = new ApiRouteInfo(); + apiRouteInfo.setUrl(microServiceInfo.getUrl()); + + apiRouteInfo.setServers(buildRouteNodes(microServiceInfo, routeWay)); + + apiRouteInfo.setVisualRange(RouteUtil.getVisualRangeByRouter(microServiceInfo.getVisualRange())); + + + if ("ip_hash".equals(microServiceInfo.getLb_policy())) { + apiRouteInfo.setUseOwnUpstream("1"); + } + + apiRouteInfo.setConsulServiceName(microServiceInfo.getServiceName()); + apiRouteInfo.setServiceName(redis_serviceName); + apiRouteInfo.setVersion(redis_version); + apiRouteInfo.setApiJson(microServiceInfo.getUrl() + "/swagger.json"); + apiRouteInfo.setMetricsUrl("/admin/metrics"); + apiRouteInfo.setEnable_ssl(buildRouteHttpProtocol(microServiceInfo, routeWay)); + // 默认 HttpProtocolå’ŒPublishProtocol=http + if (apiRouteInfo.isEnable_ssl()) { + apiRouteInfo.setPublishProtocol("https"); + } + + // 获å–æœåŠ¡çš„host + String host = getHost(microServiceInfo); + + apiRouteInfo.setHost(host.toLowerCase()); + apiRouteInfo.setNamespace(microServiceInfo.getNamespace()); + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + + if (StringUtils.isNotBlank(microServiceInfo.getPublish_port())) { + apiRouteInfo.setPublishProtocol("https"); + } + + // 获å–æœåŠ¡çš„å‘布端å£(支æŒå¤šç«¯å£æ ¼å¼:https|http) + String[] publishPorts = StringUtils.split(microServiceInfo.getPublish_port(), "|"); + if (publishPorts.length == 2) { + apiRouteInfo.setPublishProtocol("https"); + apiRouteInfo.setPublish_port(publishPorts[0]); + + try { + ApiRouteInfo apiRouteInfo_http = (ApiRouteInfo) apiRouteInfo.clone(); + apiRouteInfo.setPublishProtocol("http"); + apiRouteInfo.setPublish_port(publishPorts[1]); + return new ApiRouteInfo[] {apiRouteInfo, apiRouteInfo_http}; + } catch (CloneNotSupportedException e) { + LOGGER.error("CLONE is wrong:" + apiRouteInfo); + return new ApiRouteInfo[] {apiRouteInfo}; + } + + } + } + + + + apiRouteInfo.setPublish_port(microServiceInfo.getPublish_port()); + return new ApiRouteInfo[] {apiRouteInfo}; + + + } + + + /** + * From MicroServiceInfo to CustomRouteInfo + * + * @param microServiceInfo + * @return + */ + private CustomRouteInfo[] buildCustomRouteInfo(MicroServiceFullInfo microServiceInfo, + String redis_serviceName, String routeWay) { + + CustomRouteInfo customRouteInfo = new CustomRouteInfo(); + customRouteInfo.setUrl(microServiceInfo.getUrl()); + + + customRouteInfo.setServers(buildRouteNodes(microServiceInfo, routeWay)); + + customRouteInfo.setVisualRange(RouteUtil.getVisualRangeByRouter(microServiceInfo.getVisualRange())); + + if ("ip_hash".equals(microServiceInfo.getLb_policy())) { + customRouteInfo.setUseOwnUpstream("1"); + } + + customRouteInfo.setConsulServiceName(microServiceInfo.getServiceName()); + customRouteInfo.setServiceName(redis_serviceName); + + // 获å–æœåŠ¡çš„host + String host = getHost(microServiceInfo); + + customRouteInfo.setHost(host.toLowerCase()); + customRouteInfo.setNamespace(microServiceInfo.getNamespace()); + customRouteInfo.setEnable_ssl(buildRouteHttpProtocol(microServiceInfo, routeWay)); + + if (customRouteInfo.isEnable_ssl()) { + customRouteInfo.setPublishProtocol("https"); + } + + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + if (StringUtils.isNotBlank(microServiceInfo.getPublish_port())) { + customRouteInfo.setPublishProtocol("https"); + } + + String[] publishPorts = StringUtils.split(microServiceInfo.getPublish_port(), "|"); + if (publishPorts.length == 2) { + // 获å–æœåŠ¡çš„å‘布端å£(支æŒå¤šç«¯å£æ ¼å¼:https|http) + customRouteInfo.setPublishProtocol("https"); + customRouteInfo.setPublish_port(publishPorts[0]); + + try { + CustomRouteInfo customRouteInfo_http = (CustomRouteInfo) customRouteInfo.clone(); + customRouteInfo.setPublishProtocol("http"); + customRouteInfo.setPublish_port(publishPorts[1]); + return new CustomRouteInfo[] {customRouteInfo, customRouteInfo_http}; + } catch (CloneNotSupportedException e) { + LOGGER.error("CLONE is wrong:" + customRouteInfo); + return new CustomRouteInfo[] {customRouteInfo}; + } + + } + } + + + customRouteInfo.setPublish_port(microServiceInfo.getPublish_port()); + return new CustomRouteInfo[] {customRouteInfo}; + } + + + /** + * From MicroServiceInfo to IuiRouteInfo + * + * @param microServiceInfo + * @return + */ + private IuiRouteInfo[] buildIuiRouteInfo(MicroServiceFullInfo microServiceInfo, + String redis_serviceName, String routeWay) { + + IuiRouteInfo iuiRouteInfo = new IuiRouteInfo(); + iuiRouteInfo.setUrl(microServiceInfo.getUrl()); + + iuiRouteInfo.setServers(buildRouteNodes(microServiceInfo, routeWay)); + + iuiRouteInfo.setVisualRange(RouteUtil.getVisualRangeByRouter(microServiceInfo.getVisualRange())); + + if ("ip_hash".equals(microServiceInfo.getLb_policy())) { + iuiRouteInfo.setUseOwnUpstream("1"); + } + + + iuiRouteInfo.setConsulServiceName(microServiceInfo.getServiceName()); + iuiRouteInfo.setServiceName(redis_serviceName); + + // 获å–æœåŠ¡çš„host + String host = getHost(microServiceInfo); + + iuiRouteInfo.setHost(host.toLowerCase()); + iuiRouteInfo.setNamespace(microServiceInfo.getNamespace()); + iuiRouteInfo.setEnable_ssl(buildRouteHttpProtocol(microServiceInfo, routeWay)); + if (iuiRouteInfo.isEnable_ssl()) { + iuiRouteInfo.setPublishProtocol("https"); + } + + if (RouteUtil.ROUTEWAY_IP.equals(routeWay)) { + + if (StringUtils.isNotBlank(microServiceInfo.getPublish_port())) { + iuiRouteInfo.setPublishProtocol("https"); + } + + String[] publishPorts = StringUtils.split(microServiceInfo.getPublish_port(), "|"); + if (publishPorts.length == 2) { + // 获å–æœåŠ¡çš„å‘布端å£(支æŒå¤šç«¯å£æ ¼å¼:https|http) + iuiRouteInfo.setPublishProtocol("https"); + iuiRouteInfo.setPublish_port(publishPorts[0]); + + try { + IuiRouteInfo iuiRouteInfo_http = (IuiRouteInfo) iuiRouteInfo.clone(); + iuiRouteInfo.setPublishProtocol("http"); + iuiRouteInfo.setPublish_port(publishPorts[1]); + return new IuiRouteInfo[] {iuiRouteInfo, iuiRouteInfo_http}; + } catch (CloneNotSupportedException e) { + LOGGER.error("CLONE is wrong:" + iuiRouteInfo); + return new IuiRouteInfo[] {iuiRouteInfo}; + } + + } + } + iuiRouteInfo.setPublish_port(microServiceInfo.getPublish_port()); + return new IuiRouteInfo[] {iuiRouteInfo}; + } + + + + private List getPublishNodes(String discoverServiceName, String version, String namespace) { + List nodes = new ArrayList(); + + if (StringUtils.isBlank(version)) { + version = "null"; + } + + DiscoverInfo discoverInfo = ConfigUtil.getInstance().getDiscoverInfo(); + + String allpublishaddressUrl = + (new StringBuilder().append("http://").append(discoverInfo.toString()) + .append(RouteUtil.MSB_ROUTE_URL).append("/").append(discoverServiceName) + .append("/version/").append(version).append("/allpublishaddress?namespace=") + .append(namespace).append("&visualRange=0")).toString(); + + String resultJson = HttpClientUtil.httpGet(allpublishaddressUrl); + List publishFullAddressList = + JacksonJsonUtil + .jsonToListBean(resultJson, new TypeReference>() {}); + if (publishFullAddressList != null && publishFullAddressList.size() > 0) { + for (PublishFullAddress publishFullAddress : publishFullAddressList) { + if (StringUtils.isNotBlank(publishFullAddress.getIp()) + && "https".equals(publishFullAddress.getPublish_protocol())) { + nodes.add(new Node(publishFullAddress.getIp(), publishFullAddress.getPort())); + } + + } + } + + return nodes; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/RouteNotify.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/RouteNotify.java new file mode 100644 index 0000000..3cd2d8a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/serviceListener/RouteNotify.java @@ -0,0 +1,77 @@ +package org.onap.msb.apiroute.wrapper.serviceListener; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.wrapper.util.ServiceFilter; + +public class RouteNotify { + + private static RouteNotify instance = new RouteNotify(); + + private List serviceListenerlist = + new ArrayList(); + + private RouteNotify() {} + + public static RouteNotify getInstance() { + return instance; + } + + + public void addServiceChangeListener(IMicroServiceChangeListener listener) { + synchronized (serviceListenerlist) { + serviceListenerlist.add(listener); + } + } + + + /* public void removeServiceChangeListener(IMicroServiceChangeListener listener) { + synchronized (serviceListenerlist) { + serviceListenerlist.remove(listener); + } + }*/ + + + public void noticeRouteListener4Update(String serviceName, String version, MicroServiceFullInfo microServiceInfo) throws Exception { + if (ServiceFilter.getInstance().isNeedNotifyByProtocol(microServiceInfo.getProtocol())) { + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onChange(serviceName, version, microServiceInfo); + } + } + + } + + public void noticeUpdateStatusListener(MicroServiceFullInfo microServiceInfo, String status) { + + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onStatusChange(microServiceInfo.getServiceName(), + microServiceInfo.getVersion(), microServiceInfo.getHost(),microServiceInfo.getProtocol(), microServiceInfo.getPublish_port(),status); + } + } + + + + + public void noticeRouteListener4Add(MicroServiceFullInfo microServiceInfo) throws Exception { + if (ServiceFilter.getInstance().isNeedNotifyByProtocol(microServiceInfo.getProtocol())) { + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onSave(microServiceInfo); + } + } + } + + public void noticeRouteListener4Delete(MicroServiceFullInfo microServiceInfo) throws Exception { + if (ServiceFilter.getInstance().isNeedNotifyByProtocol(microServiceInfo.getProtocol())) { + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onDelete(microServiceInfo); + } + } + } + + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/CommonUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/CommonUtil.java new file mode 100644 index 0000000..2ad84a7 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/CommonUtil.java @@ -0,0 +1,76 @@ +package org.onap.msb.apiroute.wrapper.util; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +public class CommonUtil { + + public static final int SC_OK = 200; + + 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 strArray, String str) { + String[] array = StringUtils.split(strArray, ","); + return contain(array, str); + } + + public static boolean contain(String[] array, String str) { + for (int i = 0; i < array.length; i++) { + if (array[i].trim().equals(str)) { + return true; + } + } + return false; + + } + + public static boolean contain(String[] array, String value[]) { + for (int i = 0; i < array.length; i++) { + for (int n = 0; n < value.length; n++) { + if (array[i].equals(value[n])) { + return true; + } + } + } + return false; + + } + + /** + * @param + * @Title getDiffrent + * @Description TODO(Extract the list1 and list2 different data sets) + * @param list1 + * @param list2 + * @return TODO(a new List in list2 but not in list1) + * @return List + */ + public static Set getDiffrent(Set list1, Set list2) { + + HashSet set_all = new HashSet(); + + for (T t1 : list1) { + set_all.add(t1); + } + + + Set diff = new HashSet(); + + for (T t2 : list2) { + if (set_all.add(t2)) { // in list2 but not in list1 + diff.add(t2); + } + } + + + return diff; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ConfigUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ConfigUtil.java new file mode 100644 index 0000000..80f99f4 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ConfigUtil.java @@ -0,0 +1,446 @@ +package org.onap.msb.apiroute.wrapper.util; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.ApiRouteAppConfig; +import org.onap.msb.apiroute.api.DiscoverInfo; +import org.onap.msb.apiroute.wrapper.InitRouteServiceWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@SuppressWarnings("unchecked") +public class ConfigUtil { + private final static ConfigUtil instance = new ConfigUtil(); + + + private ConfigUtil() {} + + public static ConfigUtil getInstance() { + return instance; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class); + + private String serverPort="80"; + + private String IUI_ROOT_PATH="iui"; + + private String API_ROOT_PATH="api"; + + private String namespaceMatches="all"; + + private String visualRangeMatches="0"; + + private String nodeMetaQueryParam=""; + + private String network_plane_typeMatches=""; + + private String[] routeWay={"ip"}; + + private Map labelMapMatches; + + private DiscoverInfo discoverInfo=new DiscoverInfo(); + + private String consul_ip=""; + + private String metricsUrl = "http://127.0.0.1:8066/admin/metrics"; + + public void initRootPath() { + String apiRootPathConfSource="Default"; + String iuiRootPathConfSource="Default"; + + try { + + URL urlRootPath = + ConfigUtil.class.getResource("/ext/initUrlRootPath/initUrlRootPath.json"); + if (urlRootPath != null) { + String path = urlRootPath.getPath(); + + LOGGER.warn("read initUrlRootPath:" + path); + + String fileContent = FileUtil.readFile(path); + ObjectMapper mapper = new ObjectMapper(); + + Map map = mapper.readValue(fileContent, HashMap.class); + if (map.get("iuiRootPath") != null) { + IUI_ROOT_PATH = map.get("iuiRootPath"); + iuiRootPathConfSource="initUrlRootPath.json"; + } + if (map.get("apiRootPath") != null) { + API_ROOT_PATH = map.get("apiRootPath"); + apiRootPathConfSource="initUrlRootPath.json"; + } + + } + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.error("init UrlRootPath throw exception", e); + } + + LOGGER.warn("init IUI_ROOT_PATH from ["+iuiRootPathConfSource+"]:"+IUI_ROOT_PATH); + LOGGER.warn("init API_ROOT_PATH from ["+apiRootPathConfSource+"]:"+API_ROOT_PATH); + + } + + public void initApiGatewayPort() { + + String env_APIGATEWAY_EXPOSE_PORT=System.getenv("APIGATEWAY_EXPOSE_PORT"); + String httpExposePortConfSource="Default"; + try { + // read initApiGatewayConfig + if (StringUtils.isBlank(env_APIGATEWAY_EXPOSE_PORT)) { + URL apiGatewayConfigPath = + ConfigUtil.class + .getResource("/ext/initApiGatewayConfig/initApiGatewayConfig.json"); + if (apiGatewayConfigPath != null) { + String path = apiGatewayConfigPath.getPath(); + + LOGGER.warn("read initApiGatewayConfig:" + path); + + String fileContent = FileUtil.readFile(path); + ObjectMapper mapper = new ObjectMapper(); + + Map labelMap = mapper.readValue(fileContent, Map.class); + if (labelMap.get("port") != null) { + serverPort = (String) labelMap.get("port"); + httpExposePortConfSource="initApiGatewayConfig.json"; + } + } + } else { + serverPort = env_APIGATEWAY_EXPOSE_PORT; + httpExposePortConfSource="env:APIGATEWAY_EXPOSE_PORT"; + } + LOGGER.warn("init APIGATEWAY http publish Port from ["+httpExposePortConfSource+"]:"+serverPort); + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error( + "read initApiGatewayConfig Files or env(APIGATEWAY_EXPOSE_PORT) throw exception", e); + } + + + } + + public void initConsulIp() { + String sys_consulIp=System.getenv("CONSUL_IP"); + if (StringUtils.isNotBlank(sys_consulIp)) { + consul_ip=sys_consulIp; + LOGGER.warn("init consul_Ip from [env:CONSUL_IP]:" + sys_consulIp); + } + else{ + LOGGER.warn("init consul_Ip from [env:CONSUL_IP] is blank"); + } + + + } + + public void initRouteNameSpaceMatches() { + String env_NAMESPACE=System.getenv("NAMESPACE"); + String namespaceConfSource="Default"; + try { + // read NAMESPACE + if (StringUtils.isBlank(env_NAMESPACE)) { + URL routeLabelsPath = + InitRouteServiceWrapper.class + .getResource("/ext/initRouteLabels/initRouteLabelsMatches.json"); + if (routeLabelsPath != null) { + String path = routeLabelsPath.getPath(); + + String fileContent = FileUtil.readFile(path); + ObjectMapper mapper = new ObjectMapper(); + + Map labelMap = mapper.readValue(fileContent, Map.class); + if (labelMap.get("namespace") != null) { + namespaceMatches = (String) labelMap.get("namespace"); + namespaceConfSource="initRouteLabelsMatches.json"; + } + } + } else { + namespaceMatches =env_NAMESPACE; + namespaceConfSource="env:NAMESPACE"; + } + LOGGER.warn("init namespace Filter from ["+namespaceConfSource+"]:" + namespaceMatches); + } + catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("read initRouteNameSpaceMatches Files or env(NAMESPACE) throw exception", + e); + } + + + + } + /** + * @Title: initRouteLabelsMatches + * @Description: TODO(According to the environment variable or a JSON file configuration + * initialization Route filter conditions) + * @return: void + */ + public void initRouteLabelsMatches() { + String env_ROUTE_LABELS=System.getenv("ROUTE_LABELS"); + String visualRangeConfSource="Default"; + String networkPlaneConfSource="Default"; + String labelConfSource="Default"; + try { + + // read ROUTE_LABELS + if (StringUtils.isBlank(env_ROUTE_LABELS)) { + URL routeLabelsPath = + InitRouteServiceWrapper.class + .getResource("/ext/initRouteLabels/initRouteLabelsMatches.json"); + if (routeLabelsPath != null) { + String path = routeLabelsPath.getPath(); + + String fileContent = FileUtil.readFile(path); + ObjectMapper mapper = new ObjectMapper(); + + Map labelMap = mapper.readValue(fileContent, Map.class); + if (labelMap.get("predefineLabels") != null) { + Map predefineLabelMapMatches = + (Map) labelMap.get("predefineLabels"); + if (predefineLabelMapMatches.get("visualRange") != null) { + visualRangeMatches = predefineLabelMapMatches.get("visualRange"); + visualRangeConfSource="initRouteLabelsMatches.json"; + } + if (predefineLabelMapMatches.get("network_plane_type") != null) { + network_plane_typeMatches = + predefineLabelMapMatches.get("network_plane_type"); + networkPlaneConfSource="initRouteLabelsMatches.json"; + } + } + + if (labelMap.get("customLabels") != null) { + labelMapMatches = (Map) labelMap.get("customLabels"); + labelConfSource="initRouteLabelsMatches.json"; + } + + } + } else { + String[] env_routeLabels = StringUtils.split(env_ROUTE_LABELS, ","); + Map labelMap = new HashMap(); + + for (int i = 0; i < env_routeLabels.length; i++) { + String[] labels = StringUtils.split(env_routeLabels[i], ":"); + + if ("visualRange".equals(labels[0])) { + visualRangeMatches = labels[1]; + visualRangeConfSource="env:ROUTE_LABELS"; + } else if ("network_plane_type".equals(labels[0])) { + network_plane_typeMatches = labels[1]; + networkPlaneConfSource="env:ROUTE_LABELS"; + } else { + labelMap.put(labels[0], labels[1]); + } + + } + + labelConfSource="env:ROUTE_LABELS"; + labelMapMatches = labelMap; + + } + LOGGER.warn("init visualRange Filter from [ "+visualRangeConfSource+" ]:" + visualRangeMatches); + LOGGER.warn("init network_plane_type Filter from [ "+networkPlaneConfSource+" ]:" + network_plane_typeMatches); + LOGGER.warn("init customLabels Filter from [ "+labelConfSource+" ]:" + labelMapMatches); + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.error( + "read initRouteLabelsPathMatches Files or env(ROUTE_LABELS) throw exception", + e); + } + } + + public void initRouteWay() { + String env_ROUTE_WAY=System.getenv("ROUTE_WAY"); + try { + // read NAMESPACE + if (StringUtils.isBlank(env_ROUTE_WAY)) { + URL routeLabelsPath = + InitRouteServiceWrapper.class.getResource("/ext/initRouteWay/initRouteWay.json"); + if (routeLabelsPath != null) { + String path = routeLabelsPath.getPath(); + + String fileContent = FileUtil.readFile(path); + ObjectMapper mapper = new ObjectMapper(); + + Map routeWayMap = mapper.readValue(fileContent, Map.class); + String routeWayFromConfig=(String)routeWayMap.get("routeWay"); + if (StringUtils.isNotBlank(routeWayFromConfig)) { + routeWay = + StringUtils.split(routeWayFromConfig, RouteUtil.SPLIT_LINE); + LOGGER.warn("init RouteWay from [initRouteWay.json]:" + routeWayFromConfig); + } + } + } else { + routeWay = StringUtils.split(env_ROUTE_WAY, RouteUtil.SPLIT_LINE); + LOGGER.warn("read initRouteWay from [env:ROUTE_WAY]:" + env_ROUTE_WAY); + } + + + + + + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("read initRouteWay Files or env(ROUTE_WAY) throw exception", e); + } + } + + + public void initDiscoverInfo(ApiRouteAppConfig configuration){ + DiscoverInfo config_discoverInfo = configuration.getDiscoverInfo(); + + + discoverInfo.setEnabled(config_discoverInfo.isEnabled()); + + String discoverInfoConfSource="yaml config"; + + if (config_discoverInfo.isEnabled()) { + + String discoverIP; + String env_SDCLIENT_IP=System.getenv("SDCLIENT_IP"); + + if (StringUtils.isBlank(env_SDCLIENT_IP)) { + // yml + discoverIP = config_discoverInfo.getIp(); + } else { + discoverIP = env_SDCLIENT_IP; + discoverInfoConfSource="env:SDCLIENT_IP"; + } + + discoverInfo.setIp(discoverIP.trim()); + discoverInfo.setPort(config_discoverInfo.getPort()); + } + + LOGGER.warn("init DiscoverInfo from ["+discoverInfoConfSource+"]--" + discoverInfo.toString()+" Enabled:"+discoverInfo.isEnabled()); + } + + public void initNodeMetaQueryParam() { + // judge consul register node:caltalog + String env_CONSUL_REGISTER_MODE = System.getenv("CONSUL_REGISTER_MODE"); + + if (env_CONSUL_REGISTER_MODE == null + || !env_CONSUL_REGISTER_MODE.trim().equals("catalog")) { + nodeMetaQueryParam = ""; + return; + } + + // visual range + String nodemeta_visualrange = nodemeta_visualrange(visualRangeMatches); + + LOGGER.warn("calc nodemeta_visualrange from [" + visualRangeMatches + + "]:" + nodemeta_visualrange); + + nodeMetaQueryParam = nodemeta_visualrange; + + // name space + String nodemeta_namespace = nodemeta_namespace(namespaceMatches); + LOGGER.warn("calc nodemeta_namespace from [" + namespaceMatches + "]:" + + nodemeta_namespace); + + if (!nodeMetaQueryParam.isEmpty() && !nodemeta_namespace.isEmpty()) { + nodeMetaQueryParam += "&"; + } + nodeMetaQueryParam += nodemeta_namespace; + + /* + * // nodemeta = (!nodemeta_visualrange.isEmpty() && !nodemeta_namespace + * .isEmpty()) ? nodemeta_visualrange + "&" + nodemeta_namespace : + * nodemeta_visualrange + nodemeta_namespace; + */ + + } + + private String nodemeta_visualrange(final String visualRangeMatches) { + + if (visualRangeMatches == null || visualRangeMatches.isEmpty()) { + return ""; + } + + // external:0 + if (visualRangeMatches.trim().equals("0")) { + return "node-meta=external:true"; + } + + // internal:1 + if (visualRangeMatches.trim().equals("1")) { + return "node-meta=internal:true"; + } + + return ""; + } + + + private String nodemeta_namespace(final String namespaceMatches) { + + // exclude null,"",all,&,|,! + if (namespaceMatches == null || namespaceMatches.isEmpty() + || namespaceMatches.contains("all") + || namespaceMatches.contains("&") + || namespaceMatches.contains("|") + || namespaceMatches.contains("!")) { + return ""; + } + + return "node-meta=ns:" + namespaceMatches; + } + + public String getServerPort() { + return serverPort; + } + + public String getIUI_ROOT_PATH() { + return IUI_ROOT_PATH; + } + + public String getAPI_ROOT_PATH() { + return API_ROOT_PATH; + } + + public String getNamespaceMatches() { + return namespaceMatches; + } + + public String getVisualRangeMatches() { + return visualRangeMatches; + } + + public String getNetwork_plane_typeMatches() { + return network_plane_typeMatches; + } + + public String[] getRouteWay() { + return routeWay.clone(); + } + + public Map getLabelMapMatches() { + return labelMapMatches; + } + + public DiscoverInfo getDiscoverInfo() { + return discoverInfo; + } + + public String getMetricsUrl() { + return metricsUrl; + } + + public void setMetricsUrl(String metricsUrl) { + this.metricsUrl = metricsUrl; + } + + public String getNodeMetaQueryParam() { + return nodeMetaQueryParam; + } + + public String getConsul_ip() { + return consul_ip; + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/FileUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/FileUtil.java new file mode 100644 index 0000000..1e89f82 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/FileUtil.java @@ -0,0 +1,78 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.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; + StringBuffer fileContent = new StringBuffer(); + 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.append(tempString); + } + reader.close(); + } catch (IOException e) { + throw e; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + throw e; + } + } + } + return fileContent.toString(); + } + + /** + * Read all the files under a folder + */ + public static String[] readfile(String filepath) throws FileNotFoundException, IOException { + File file = new File(filepath); + if (file.isDirectory()) { + String[] filelist = file.list(); + return filelist; + } + return null; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpClientUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpClientUtil.java new file mode 100644 index 0000000..eb5ed1e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpClientUtil.java @@ -0,0 +1,117 @@ +package org.onap.msb.apiroute.wrapper.util; + +import java.io.IOException; + +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.config.RequestConfig; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HttpClientUtil { + + private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); + + private static int connectionTimeOut = 2*1000; + + + public static String httpGet(String url){ + String result = null; + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpGet httpGet = new HttpGet(url); + httpGet.addHeader("Content-type", "application/json; charset=utf-8"); + httpGet.setHeader("Accept", "application/json"); + try { + CloseableHttpResponse res = httpClient.execute(httpGet); + result = EntityUtils.toString(res.getEntity()); + if (res.getStatusLine().getStatusCode() != CommonUtil.SC_OK) { + logger.error(result); + } + res.close(); + } catch (ClientProtocolException e) { + logger.error(url + ":httpGetWithJSON connect faild"); + } catch (IOException e) { + logger.error( url + ":httpGetWithJSON connect faild"); + } finally { + try { + httpClient.close(); + } catch (IOException e) { + logger.error(url + ":close httpClient faild"); + } + } + + return result; + + } + + public static HttpGetResult httpGetStatusAndBody(String url){ + HttpGetResult result= new HttpGetResult(); + String body = null; + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpGet httpGet = new HttpGet(url); + httpGet.addHeader("Content-type", "application/json; charset=utf-8"); + httpGet.setHeader("Accept", "application/json"); + + RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectionTimeOut).build(); + httpGet.setConfig(requestConfig); + + try { + CloseableHttpResponse res = httpClient.execute(httpGet); + body = EntityUtils.toString(res.getEntity()); + if (res.getStatusLine().getStatusCode() != CommonUtil.SC_OK) { + logger.error(body); + } + result.setBody(body); + result.setStatusCode(res.getStatusLine().getStatusCode()); + res.close(); + } catch (ClientProtocolException e) { + logger.error(url + ":httpGetWithJSON connect faild",e); + } catch (IOException e) { + logger.error( url + ":httpGetWithJSON connect faild",e); + } finally { + try { + httpClient.close(); + } catch (IOException e) { + logger.error(url + ":close httpClient faild"); + } + } + + return result; + + } + + public static int httpGetStatus(String url) throws Exception{ + int iStatus=500; + CloseableHttpClient httpClient = HttpClients.createDefault(); + + + HttpGet httpGet = new HttpGet(url); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build();//设置请求和传输超时时间 + httpGet.setConfig(requestConfig); + httpGet.addHeader("Content-type", "application/json; charset=utf-8"); + httpGet.setHeader("Accept", "application/json"); + try { + CloseableHttpResponse res = httpClient.execute(httpGet); + + iStatus=res.getStatusLine().getStatusCode(); + res.close(); + } catch (ClientProtocolException e) { + logger.error(url + " httpGet connect faild:"+e.getMessage()); + } catch (IOException e) { + logger.error(url + " httpGet connect faild:"+e.getMessage()); + } finally { + try { + httpClient.close(); + } catch (IOException e) { + logger.error(url + " httpGet close faild:"+e.getMessage()); + } + } + + return iStatus; + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpGetResult.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpGetResult.java new file mode 100644 index 0000000..5b943e3 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/HttpGetResult.java @@ -0,0 +1,19 @@ +package org.onap.msb.apiroute.wrapper.util; + +public class HttpGetResult { + private int statusCode; + private String body; + public int getStatusCode() { + return statusCode; + } + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + public String getBody() { + return body; + } + public void setBody(String body) { + this.body = body; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/Jackson.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/Jackson.java new file mode 100644 index 0000000..20b60d4 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/Jackson.java @@ -0,0 +1,28 @@ +package org.onap.msb.apiroute.wrapper.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.guava.GuavaModule; +import com.fasterxml.jackson.datatype.jdk7.Jdk7Module; +import com.fasterxml.jackson.datatype.joda.JodaModule; + +public class Jackson { + //use static singleton, make sure to reuse! + public static final ObjectMapper MAPPER = newObjectMapper(); + + private Jackson() { + /* singleton */ + } + + private static ObjectMapper newObjectMapper() { + final ObjectMapper mapper = new ObjectMapper(); + return configure(mapper); + } + + private static ObjectMapper configure(ObjectMapper mapper) { + mapper.registerModule(new GuavaModule()); + mapper.registerModule(new JodaModule()); + mapper.registerModule(new Jdk7Module()); + + return mapper; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JacksonJsonUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JacksonJsonUtil.java new file mode 100644 index 0000000..4a7f50e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JacksonJsonUtil.java @@ -0,0 +1,110 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper.util; + +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 volatile static ObjectMapper mapper = null; + + private static ObjectMapper getMapperInstance() { + if (mapper == null) { + synchronized (JacksonJsonUtil.class) { + 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; + + ObjectMapper objectMapper = getMapperInstance(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + json = objectMapper.writeValueAsString(obj); + + 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 T jsonToListBean(String json, TypeReference valueTypeRef) { + try { + + ObjectMapper objectMapper = getMapperInstance(); + + + return objectMapper.readValue(json, valueTypeRef); + + } catch (Exception e) { + String errorMsg = " JsonTobean faild:" + e.getMessage(); + logger.error(errorMsg); + } + return null; + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JedisUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JedisUtil.java new file mode 100644 index 0000000..ac9421b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/JedisUtil.java @@ -0,0 +1,208 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper.util; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.wrapper.InitRouteServiceWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + + + +public 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 volatile static JedisPool jedisPool = null; + + + public static String propertiesName = "redis.properties"; + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + + private JedisUtil() { + // private constructor + + } + + private synchronized static JedisPool initialPool() throws IOException { + + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(50); + config.setMaxIdle(30); + config.setMaxWaitMillis(5000); + config.setTestOnBorrow(false); + config.setTestOnReturn(true); + + URL urlPath = JedisUtil.class.getResource("/ext/redisConf/redis.properties"); + if (urlPath != null) { + String propertiesPath = urlPath.getPath(); + + + 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!"); + } + + + // Set up the connection pool basic information + String strHost = bundle.getString("redis.host"); + if (StringUtils.isNotEmpty(strHost)) { + host = strHost; + } + + // redis port: first read from env + if (StringUtils.isNotBlank(System.getenv("APIGATEWAY_REDIS_PORT"))) { + port = Integer.parseInt(System.getenv("APIGATEWAY_REDIS_PORT")); + } else { + String strPort = bundle.getString("redis.port"); + if (StringUtils.isNotEmpty(strPort)) { + port = Integer.parseInt(strPort); + } + } + + + String strTimeout = bundle.getString("redis.connectionTimeout"); + if (StringUtils.isNotEmpty(strTimeout)) { + connectionTimeout = Integer.parseInt(strTimeout); + } + + + String strDbIndex = bundle.getString("redis.db_index"); + if (StringUtils.isNotEmpty(strDbIndex)) { + DEFAULT_DB_INDEX = Integer.parseInt(strDbIndex); + } + + String strMaxTotal = bundle.getString("redis.pool.maxTotal"); + if (StringUtils.isNotEmpty(strMaxTotal)) { + config.setMaxTotal(Integer.parseInt(strMaxTotal)); + } + + String strMaxIdle = bundle.getString("redis.pool.maxIdle"); + if (StringUtils.isNotEmpty(strMaxIdle)) { + config.setMaxIdle(Integer.parseInt(strMaxIdle)); + } + + String strMaxWaitMillis = bundle.getString("redis.pool.maxWaitMillis"); + if (StringUtils.isNotEmpty(strMaxWaitMillis)) { + config.setMaxWaitMillis(Long.parseLong(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)); + } + + } + } + + StringBuffer redisinfo = new StringBuffer(); + redisinfo.append("------redis.properties------").append(LINE_SEPARATOR); + redisinfo.append("redis.host: ").append(host).append(":").append(port).append(LINE_SEPARATOR); + redisinfo.append("redis.connectionTimeout: ").append(connectionTimeout).append(LINE_SEPARATOR); + redisinfo.append("redis.pool.maxTotal: ").append(config.getMaxTotal()).append(LINE_SEPARATOR); + redisinfo.append("redis.pool.maxIdle: ").append(config.getMaxIdle()).append(LINE_SEPARATOR); + redisinfo.append("redis.pool.maxWaitMillis: ").append(config.getMaxWaitMillis()) + .append(LINE_SEPARATOR); + redisinfo.append("redis.pool.testOnBorrow: ").append(config.getTestOnBorrow()) + .append(LINE_SEPARATOR); + redisinfo.append("redis.pool.testOnReturn: ").append(config.getTestOnReturn()) + .append(LINE_SEPARATOR); + + + LOGGER.info(redisinfo.toString()); + return new JedisPool(config, host, port, connectionTimeout); + + } + + /** + * From the connection pool to obtain jedis instance, use the default database index number 0 + * + * @return + * @throws Exception + */ + public static Jedis borrowJedisInstance() throws Exception { + return borrowJedisInstance(DEFAULT_DB_INDEX); + } + + /** + * From the connection pool to obtain jedis instance, using the specified database index number + * + * @return + * @throws Exception + */ + + public static Jedis borrowJedisInstance(final int dbIndex) throws Exception { + if (jedisPool == null) { + synchronized (JedisUtil.class) { + if (jedisPool == null) { + jedisPool = initialPool(); + } + } + } + Jedis resource = jedisPool.getResource(); + + if (resource == null) { + throw new Exception("fetch from jedis pool failed,null object!"); + } + + resource.select(dbIndex); + return resource; + + } + + /** + * returned to the pool jedis instance + * + * @param jedis + */ + public static void returnJedisInstance(final Jedis jedis) { + if (jedis != null) { + jedis.close(); + } + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/MicroServiceUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/MicroServiceUtil.java new file mode 100644 index 0000000..1081579 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/MicroServiceUtil.java @@ -0,0 +1,78 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper.util; + +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Pattern; + + +public class MicroServiceUtil { + public static final String PREFIX_PATH = "discover:microservices"; + + private static final Pattern SERVICE_KEY_REGEX_PATTERN = + Pattern.compile("discover:microservices:(?[^:]+)(:(?[^:]*))"); + + + public static String getPrefixedKey(String... paths) { + StringBuffer sb = new StringBuffer(); + + sb.append(PREFIX_PATH); + + for (int i = 0; i < paths.length; i++) { + sb.append(":"); + sb.append(paths[i]); + } + return sb.toString(); + } + + + public static String getServiceKey(String serviceName, String version) { + return getPrefixedKey(serviceName, version); + } + + public static Pattern getServiceKeyRegexPattern(){ + return SERVICE_KEY_REGEX_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/onap/msb/apiroute/wrapper/util/RegExpTestUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RegExpTestUtil.java new file mode 100644 index 0000000..0edcfda --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RegExpTestUtil.java @@ -0,0 +1,115 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RegExpTestUtil { + + + private final static String API_KEY_PATTERN ="/api/(?[^/]+)(/(?[^/]*)).*"; + + private final static String IUI_KEY_PATTERN ="/iui/(?[^/]+)/.*"; + + 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 = "^\\/"+ConfigUtil.getInstance().getAPI_ROOT_PATH()+"\\/.*$"; + return Pattern.matches(urlReg, url); + +} + +public static boolean iuiRouteUrlRegExpTest(String url){ + + String urlReg = "^\\/"+ConfigUtil.getInstance().getIUI_ROOT_PATH()+"\\/.*$"; + return Pattern.matches(urlReg, url); + +} + +public static String[] apiServiceNameMatch4URL(String url){ + Pattern redisKeyPattern =Pattern.compile(API_KEY_PATTERN); + Matcher matcher = redisKeyPattern.matcher(url+"/"); + if (matcher.matches()) { + String version; + if(versionRegExpTest(matcher.group("version"))){ + version=matcher.group("version"); + } + else{ + version=""; + } + return new String[]{matcher.group("servicename"),version}; + } + else{ + return null; + } +} + + +public static String iuiServiceNameMatch4URL(String url){ + Pattern redisKeyPattern =Pattern.compile(IUI_KEY_PATTERN); + Matcher matcher = redisKeyPattern.matcher(url+"/"); + if (matcher.matches()) { + return matcher.group("servicename"); + } + else{ + return null; + } +} + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RouteUtil.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RouteUtil.java new file mode 100644 index 0000000..331671f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/RouteUtil.java @@ -0,0 +1,345 @@ +/** + * Copyright 2016 ZTE, Inc. and others. + * + * 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.onap.msb.apiroute.wrapper.util; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.api.Node; +import org.onap.msb.apiroute.api.RouteInfo; +import org.onap.msb.apiroute.api.RouteServer; +import org.onap.msb.apiroute.api.exception.UnprocessableEntityException; + + +public class RouteUtil { + + + public static final int consulDeafultPort=8500; + + public static final String ROUTE_PATH="msb:routing"; + + public static final String ROUTE_PORT_PATH="msb:"; + + public static final String ROUTE_PATH_HOST="msb:host"; + + public static final String APIROUTE="api"; + + public static final String IUIROUTE="iui"; + + public static final String CUSTOMROUTE="custom"; + + public static final String HTTPS_PROTOCOL="https"; + + public static final String CUSTOM_PORTAL="portal"; + + + public static final String PROTOCOL_LIST="REST,HTTP,UI,MQ,FTP,SNMP,TCP,UDP"; + + public static final String MSB_ROUTE_URL = "/api/microservices/v1/services"; + + public static final String MSB_CHECK_URL = "/api/catalog/v1/service/router-all"; + + public static final String visualRangeRange="0,1"; + + public static final String controlRangeMatches="0,1,2"; + + public static final String statusRangeMatches="0,1"; + + public static final String useOwnUpstreamRangeMatches="0,1"; + + public static final String ROUTEWAY_IP="ip"; + + public static final String ROUTEWAY_DOMAIN="domain"; + + public static final String SPLIT_LINE="|"; + + public static final String PROTOCOL_REST="REST"; + + public static final String PROTOCOL_UI="UI"; + + public static final String PROTOCOL_HTTP="HTTP"; + + public static final String FILTER_PROTOCOLS="REST,UI,HTTP"; + + public static final int SERVICE_DATA_QUEUE_NUM=5; + + public static final int SERVICE_QUEUE_CAPACITY=100; + + public static final int SERVICE_LIST_QUEUE_CAPACITY=5; + + public static final int WATCH_SECOND=120; + + public static final String HEALTH_CHECK_PASSING="passing"; + + + + + /** + * @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(ConfigUtil.getInstance().getServerPort())){ + sb.append(ROUTE_PATH); + } + else{ + sb.append(ROUTE_PORT_PATH).append(paths[0]); + } + + for (int i = 1; i < paths.length; i++) { + sb.append(":"); + sb.append(paths[i]); + } + return sb.toString(); + } + + public static String getPrefixedKey4Host(String...paths){ + StringBuffer sb= new StringBuffer(); + + sb.append(ROUTE_PATH_HOST); + + + for (int i = 0; i < paths.length; i++) { + sb.append(":"); + sb.append(paths[i]); + } + return sb.toString(); + } + + + + + public static void checkRouteWay(String routeWay){ + if(!CommonUtil.contain(ConfigUtil.getInstance().getRouteWay(),routeWay)){ + String errInfo = "routeWay does not support,must be ip or domain"; + throw new UnprocessableEntityException(errInfo); + } + } + + public static void checkServiceNameAndVersion(String serviceName,String version){ + if (StringUtils.isBlank(serviceName)) { + throw new UnprocessableEntityException("serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new UnprocessableEntityException("version is not a valid format"); + } + } + } + + public static void checkServiceStatus(String status){ + if (!CommonUtil.contain(statusRangeMatches, status)) { + throw new UnprocessableEntityException( + "save RouteInfo Status FAIL:status is wrong,value range:(" + + RouteUtil.statusRangeMatches + ")"); + } + } + + + + public static void checkRouterInfoFormat(RouteInfo routeInfo) { + + if (StringUtils.isBlank(routeInfo.getServiceName()) || routeInfo.getServers().length == 0) { + throw new UnprocessableEntityException( + "save RouteInfo FAIL: Some required fields are empty"); + } + + if (StringUtils.isNotBlank(routeInfo.getUrl())) { + if (!RegExpTestUtil.urlRegExpTest(routeInfo.getUrl())) { + throw new UnprocessableEntityException( + "save RouteInfo FAIL:url is not a valid format(url must be begin with /)"); + + } + } + + if (!CommonUtil.contain(RouteUtil.visualRangeRange, routeInfo.getVisualRange())) { + throw new UnprocessableEntityException( + "save RouteInfo FAIL:VisualRange is wrong,value range:(" + + RouteUtil.visualRangeRange + ")"); + } + + if (!CommonUtil.contain(RouteUtil.controlRangeMatches, routeInfo.getControl())) { + throw new UnprocessableEntityException( + "save RouteInfo FAIL:control is wrong,value range:(" + + RouteUtil.controlRangeMatches + ")"); + } + + if (!CommonUtil.contain(RouteUtil.statusRangeMatches, routeInfo.getStatus())) { + throw new UnprocessableEntityException( + "save RouteInfo FAIL:status is wrong,value range:(" + + RouteUtil.statusRangeMatches + ")"); + } + + if (!CommonUtil.contain(RouteUtil.useOwnUpstreamRangeMatches, routeInfo.getUseOwnUpstream())) { + throw new UnprocessableEntityException( + "save RouteInfo FAIL:useOwnUpstream is wrong,value range:(" + + RouteUtil.useOwnUpstreamRangeMatches + ")"); + } + + // Check the service instance format + RouteServer[] serverList = routeInfo.getServers(); + for (int i = 0; i < serverList.length; i++) { + RouteServer server = serverList[i]; + if (!RegExpTestUtil.ipRegExpTest(server.getIp())) { + throw new UnprocessableEntityException("save RouteInfo FAIL:IP(" + server.getIp() + + ")is not a valid ip address"); + } + + if (!RegExpTestUtil.portRegExpTest(server.getPort())) { + throw new UnprocessableEntityException("save RouteInfo FAIL:Port(" + server.getPort() + + ")is not a valid Port address"); + } + } + } + + public static void checkMicroServiceInfoFormat(MicroServiceFullInfo microServiceInfo,String requestIP){ + // Check the service instance format + if (StringUtils.isBlank(microServiceInfo.getServiceName()) + || StringUtils.isBlank(microServiceInfo.getProtocol()) + || microServiceInfo.getNodes().size() == 0) { + throw new UnprocessableEntityException( + "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 UnprocessableEntityException("register MicroServiceInfo FAIL:IP(" + node.getIp() + + ")is not a valid ip address"); + } + + if (!RegExpTestUtil.portRegExpTest(node.getPort())) { + throw new UnprocessableEntityException("register MicroServiceInfo FAIL:Port(" + + node.getPort() + ")is not a valid Port address"); + } + } + + if (StringUtils.isNotBlank(microServiceInfo.getVersion())) { + if (!RegExpTestUtil.versionRegExpTest(microServiceInfo.getVersion())) { + throw new UnprocessableEntityException( + "register MicroServiceInfo FAIL:version is not a valid format"); + + } + } + + if (StringUtils.isNotBlank(microServiceInfo.getUrl().trim())) { + if (!RegExpTestUtil.urlRegExpTest(microServiceInfo.getUrl())) { + throw new UnprocessableEntityException( + "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 UnprocessableEntityException( + "register MicroServiceInfo FAIL:Protocol is wrong,value range:(" + + RouteUtil.PROTOCOL_LIST + ")"); + } + + } + + + public static String getAPIRedisPrefixedKey(String routeName, String version, String host,String publish_port,String routeWay){ + String redisPrefixedKey; + if(ROUTEWAY_DOMAIN.equals(routeWay)){ + redisPrefixedKey= RouteUtil.getPrefixedKey4Host(host, APIROUTE, routeName, version); + } + else{ + redisPrefixedKey=RouteUtil.getPrefixedKey(publish_port, APIROUTE, routeName, version); + } + + return redisPrefixedKey; + } + + public static String getRedisPrefixedKey(String routeType,String routeName, String host,String publish_port,String routeWay){ + String redisPrefixedKey; + if(ROUTEWAY_DOMAIN.equals(routeWay)){ + redisPrefixedKey= RouteUtil.getPrefixedKey4Host(host, routeType, routeName); + } + else{ + redisPrefixedKey=RouteUtil.getPrefixedKey(publish_port, routeType, routeName); + } + + return redisPrefixedKey; + } + + public static String getMutiRedisKey(String routeType,String routeWay){ + String redisKey; + if(RouteUtil.ROUTEWAY_DOMAIN.equals(routeWay)){ + redisKey = + RouteUtil.getPrefixedKey4Host("*", routeType, "*"); + + } + else{ + redisKey = + RouteUtil.getPrefixedKey("[^h]*", routeType, "*"); + + } + + return redisKey; + } + + /** + * @Title getRouteNameByns + * @Description TODO(æ ¹æ®æœåŠ¡å和命å空间拆分æœåŠ¡è·¯ç”±å) + * @param serviceName + * @param namespace + * @return + * @return String + */ + public static String getRouteNameByns(String consul_serviceName,String namespace){ + String serviceName=consul_serviceName; + if(StringUtils.isNotBlank(namespace)){ + if(consul_serviceName.endsWith("-"+namespace)){ + serviceName=consul_serviceName.substring(0,consul_serviceName.length()-namespace.length()-1); + } + } + + return serviceName; + } + + public static String getVisualRangeByRouter(String visualRange){ + String[] rangs = StringUtils.split(visualRange, "|"); + if(rangs.length>1){ + String visualRangeMatches=ConfigUtil.getInstance().getVisualRangeMatches(); + if(StringUtils.split(visualRangeMatches, "|").length>1){ + return "0"; + } + else{ + return visualRangeMatches; + } + } + else{ + return visualRange; + } + + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ServiceFilter.java b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ServiceFilter.java new file mode 100644 index 0000000..49b7b49 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/onap/msb/apiroute/wrapper/util/ServiceFilter.java @@ -0,0 +1,515 @@ +package org.onap.msb.apiroute.wrapper.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.onap.msb.apiroute.api.MicroServiceFullInfo; +import org.onap.msb.apiroute.api.Node; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.Service; +import org.onap.msb.apiroute.wrapper.consulextend.model.health.ServiceHealth; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.orbitz.consul.model.health.HealthCheck; + + + +public class ServiceFilter { + private static ServiceFilter instance = new ServiceFilter(); + + private ServiceFilter() {} + + public static ServiceFilter getInstance() { + return instance; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceFilter.class); + + + /** + * Determine whether the service needs to send a notification TODO: filter according to the + * agreement, the only notice of agreement for REST \HTTP\ UI interface MSB - REST + * + * @param protocol + * @return + */ + public boolean isNeedNotifyByProtocol(String protocol) { + return CommonUtil.contain(RouteUtil.FILTER_PROTOCOLS, protocol.trim()); + } + + /** + * Determine whether the service needs to send a notification TODO: according to the visual range + * filter conditions Regular language: all 〠default 〠!default 〠A〠|A 〠A|B〠!A&!B + * + * @param visualRange + * @return + */ + public boolean isNeedNotifyByNameSpace(String nameSpace) { + + String namespaceMatches = ConfigUtil.getInstance().getNamespaceMatches(); + String[] namespaceArray = StringUtils.split(namespaceMatches, "|"); + + if (CommonUtil.contain(namespaceArray, "all")) { + return true; + } + + if (CommonUtil.contain(namespaceArray, "default")) { + if (StringUtils.isEmpty(nameSpace) || "default".equals(nameSpace) ) { + return true; + } else { + return false; + } + } + + if (CommonUtil.contain(namespaceArray, "!default")) { + if (StringUtils.isNotEmpty(nameSpace) && !"default".equals(nameSpace)) { + return true; + } else { + return false; + } + } + try { + String namespaceReg; + if (namespaceMatches.contains("!")) { + namespaceReg = "^" + namespaceMatches.replaceAll("!", "").replaceAll("&", "|") + "$"; + return !Pattern.matches(namespaceReg, nameSpace); + } else { + namespaceReg = "^" + namespaceMatches + "$"; + return Pattern.matches(namespaceReg, nameSpace); + } + + } catch (Exception e) { + LOGGER.error(" Regular " + namespaceMatches + " throw exception:" + e.getMessage()); + return false; + } + } + + public boolean isNeedNotifyByVisualRange(String visualRange) { + + String[] routeVisualRangeArray = + StringUtils.split(ConfigUtil.getInstance().getVisualRangeMatches(), "|"); + + String[] serviceVisualRangeArray = StringUtils.split(visualRange, "|"); + + if (CommonUtil.contain(serviceVisualRangeArray, routeVisualRangeArray)) { + return true; + } + + return false; + + } + + public boolean isNeedNotifyByNetwork_plane_typeMatches(String network_plane_type) { + + String network_plane_typeMatches = ConfigUtil.getInstance().getNetwork_plane_typeMatches(); + if (StringUtils.isBlank(network_plane_typeMatches)) + return true; + + String[] routeNetwork_plane_typeArray = StringUtils.split(network_plane_typeMatches, "|"); + + String[] serviceVisualRangeArray = StringUtils.split(network_plane_type, "|"); + + if (CommonUtil.contain(serviceVisualRangeArray, routeNetwork_plane_typeArray)) { + return true; + } + + return false; + + } + + /** + * Determine whether the service needs to send a notification TODO: according to the visual range + * filter conditions + * + * @param visualRange + * @return + */ + public boolean isNeedNotifyByRouteLabels(Map labelMap) { + + Map labelMapMatches = ConfigUtil.getInstance().getLabelMapMatches(); + + if (labelMapMatches == null || labelMapMatches.isEmpty()) { + return true; + } + + for (Map.Entry entry : labelMapMatches.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + // Multiple values match + + if (StringUtils.isBlank(labelMap.get(key))) { + continue; + } + + String[] routeLalelsArray = StringUtils.split(value, "|"); + + String[] serviceLabelsArray = StringUtils.split(labelMap.get(key), "|"); + + if (CommonUtil.contain(routeLalelsArray, serviceLabelsArray)) { + return true; + } + + } + + return false; + } + + + + /* + * public boolean isNeedNotifyByRoute(String protocol, String namespace, String visualRange, + * String network_plane_type, Map labelMap) { + * + * return isNeedNotifyByProtocol(protocol) && isNeedNotifyByNameSpace(namespace) && + * isNeedNotifyByVisualRange(visualRange) && isNeedNotifyByRouteLabels(labelMap) && + * isNeedNotifyByNetwork_plane_typeMatches(network_plane_type); + * + * } + */ + + public boolean isFilterCheck(ServiceHealth health){ + return isFilterHealthCheck(health.getChecks()) && isFilterService(health.getService().getTags()); + } + + /** + * @Title isFilterHealthCheck + * @Description TODO(判断æœåŠ¡å®žä¾‹çš„å¥åº·æ£€æŸ¥ä¿¡æ¯ï¼Œå…¨éƒ¨ä¸ºpassing表示å¥åº·æ£€æŸ¥æœ‰æ•ˆ) + * @param List + * @return boolean checkList示例——"Checks" : [{ + "Node" : "server", + "CheckID" : "serfHealth", + "Name" : "Serf Health Status", + "Status" : "passing", + "Notes" : "", + "Output" : "Agent alive and reachable", + "ServiceID" : "", + "ServiceName" : "", + "CreateIndex" : 65536, + "ModifyIndex" : 65536 + }, { + "Node" : "server", + "CheckID" : "service:_tcp_roundrobin_1_10.74.151.26_22", + "Name" : "Service 'tcp_roundrobin_1' check", + "Status" : "critical", + "Notes" : "", + "Output" : "dial tcp: missing port in address ok", + "ServiceID" : "_tcp_roundrobin_1_10.74.151.26_22", + "ServiceName" : "tcp_roundrobin_1", + "CreateIndex" : 75988, + "ModifyIndex" : 76173 + } + ] + */ + public boolean isFilterHealthCheck(List checkList){ + if(checkList.isEmpty()){ + return true; + } + + for (HealthCheck check : checkList) { + if (!RouteUtil.HEALTH_CHECK_PASSING.equals(check.getStatus())) { + return false; + } + } + + return true; + } + + + + /** + * @Title isFilterService + * @Description TODO(判断æ¥è‡ªconsulçš„æœåŠ¡ä¿¡æ¯æ˜¯å¦éœ€è¦è¿‡æ»¤) + * @param List + * @return boolean tagList示例—— [ + * "\"base\":{\"protocol\":\"REST\",\"is_manual\":\"true\",\"version\":\"v1\",\"url\":\"/api/msbtest/v1\"}" + * , "\"ns\":{\"namespace\":\"nsName\"}", + * "\"labels\":{\"visualRange\":\"0\",\"network_plane_type\":\"net\",\"customLabel\":\"custom\"}" + * ] + */ + @SuppressWarnings("unchecked") + public boolean isFilterService(List tagList) { + + if (tagList == null || tagList.size() == 0) + return false; + + String visualRange = "", network_plane_type = "", protocol = "", namespace = ""; + + //针对多版本ä¸åŒå±žæ€§çš„tag会有多个,åªè¦å…¶ä¸­ä¸€ä¸ªåŒ¹é…å³é€šè¿‡è¿‡æ»¤,默认ä¸é€šè¿‡ + boolean visualRangeFilter=false,protocolFilter = false, namespaceFilter = false; + boolean hasnamespace=false; + + try { + + for (String tag : tagList) { + + // æå–基础属性tag + if (!protocolFilter && tag.startsWith("\"base\"")) { + String ms_base_json = tag.split("\"base\":")[1]; + + Map baseMap = + (Map) JacksonJsonUtil.jsonToBean(ms_base_json, Map.class); + + if (baseMap.get("protocol") != null) { + protocol = baseMap.get("protocol"); + if ("PORTAL".equalsIgnoreCase(protocol)) { + protocol = "HTTP"; + } + + if (isNeedNotifyByProtocol(protocol)) { + protocolFilter=true; + } + + } + + + + continue; + } + + // æå–命å空间属性tag + if (!namespaceFilter && tag.startsWith("\"ns\"")) { + String ms_ns_json = tag.split("\"ns\":")[1]; + Map nsMap = + (Map) JacksonJsonUtil.jsonToBean(ms_ns_json, Map.class); + + if (nsMap.get("namespace") != null) { + namespace = nsMap.get("namespace"); + hasnamespace=true; + + if (isNeedNotifyByNameSpace(namespace)) { + namespaceFilter=true; + } + } + + + continue; + } + + // æå–Label属性tag + if (tag.startsWith("\"labels\"")) { + String ms_labels_json = "{" + tag.split("\"labels\":\\{")[1]; + // 自定义label标签属性 + Map labelMap = + (Map) JacksonJsonUtil.jsonToBean(ms_labels_json, Map.class); + + + + if (!visualRangeFilter && labelMap.get("visualRange") != null) { + visualRange = labelMap.get("visualRange"); + labelMap.remove("visualRange"); // 自定义标签排除å¯è§èŒƒå›´å’Œç½‘ç»œå¹³é¢ + + if(isNeedNotifyByVisualRange(visualRange)){ + visualRangeFilter=true; + } + } + + + if (labelMap.get("network_plane_type") != null) { + network_plane_type = labelMap.get("network_plane_type"); + labelMap.remove("network_plane_type"); + } + if (!isNeedNotifyByNetwork_plane_typeMatches(network_plane_type)) { + return false; + } + + if (!isNeedNotifyByRouteLabels(labelMap)) { + return false; + } + + continue; + } + + } + + //针对无命å空间的æœåŠ¡åˆ¤æ–­æ˜¯å¦è¿‡æ»¤ + if (!hasnamespace && isNeedNotifyByNameSpace(namespace)) { + namespaceFilter=true; + } + + return visualRangeFilter && protocolFilter && namespaceFilter; + + + } catch (Exception e) { + LOGGER.error(" read tag throw exception", e); + return false; + } + + + } + + + + @SuppressWarnings("unchecked") + public Map transMicroServiceInfoFromConsul( + List serviceNodeList) { + // åŒå多版本æœåŠ¡MAP + Map microServiceInfo4version = + new HashMap(); + + + for (ServiceHealth serviceNode : serviceNodeList) { + + MicroServiceFullInfo microServiceInfo = new MicroServiceFullInfo(); + String url = ""; + String version = "", visualRange = "", protocol = "", lb_policy = "", namespace = + "", host = "", path = "", publish_port = ""; + boolean enable_ssl = false; + + HashSet nodes = new HashSet(); + + Service service = serviceNode.getService(); + String serviceName = service.getService(); + + try { + List tagList = service.getTags(); + + for (String tag : tagList) { + + if (tag.startsWith("\"base\"")) { + String ms_base_json = tag.split("\"base\":")[1]; + + + Map baseMap = + (Map) JacksonJsonUtil.jsonToBean(ms_base_json, Map.class); + if (baseMap.get("url") != null) { + url = baseMap.get("url"); + } + + if (baseMap.get("version") != null) { + version = baseMap.get("version"); + } + + if (baseMap.get("protocol") != null) { + protocol = baseMap.get("protocol"); + } + + if (baseMap.get("host") != null) { + host = baseMap.get("host"); + } + + if (baseMap.get("path") != null) { + path = baseMap.get("path"); + } + + if (baseMap.get("publish_port") != null) { + publish_port = baseMap.get("publish_port"); + } + + + if (baseMap.get("enable_ssl") != null) { + enable_ssl = Boolean.valueOf(baseMap.get("enable_ssl")); + } + + continue; + } + + + + if (tag.startsWith("\"ns\"")) { + String ms_ns_json = tag.split("\"ns\":")[1]; + Map nsMap = + (Map) JacksonJsonUtil.jsonToBean(ms_ns_json, Map.class); + + if (nsMap.get("namespace") != null) { + namespace = nsMap.get("namespace"); + } + + continue; + } + + if (tag.startsWith("\"labels\"")) { + String ms_labels_json = "{" + tag.split("\"labels\":\\{")[1]; + Map labelMap = + (Map) JacksonJsonUtil.jsonToBean(ms_labels_json, Map.class); + + + if (labelMap.get("visualRange") != null) { + visualRange = labelMap.get("visualRange"); + } + + /*if (labelMap.get("network_plane_type") != null) { + network_plane_type = labelMap.get("network_plane_type"); + }*/ + + continue; + } + + if (tag.startsWith("\"lb\"")) { + String ms_lb_json = tag.split("\"lb\":")[1]; + Map lbMap = + (Map) JacksonJsonUtil.jsonToBean(ms_lb_json, Map.class); + + if (lbMap.get("lb_policy") != null) { + lb_policy = lbMap.get("lb_policy"); + } + continue; + } + + } + + + + } catch (Exception e) { + LOGGER.error(serviceName + " read tag throw exception", e); + } + + if (!microServiceInfo4version.containsKey(version)) { + + if ("PORTAL".equalsIgnoreCase(protocol)) { + protocol = "HTTP"; + microServiceInfo.setCustom(RouteUtil.CUSTOM_PORTAL); + } + + microServiceInfo.setProtocol(protocol); + microServiceInfo.setUrl(url); + microServiceInfo.setServiceName(serviceName); + microServiceInfo.setLb_policy(lb_policy); + microServiceInfo.setVisualRange(visualRange); + + microServiceInfo.setEnable_ssl(enable_ssl); + microServiceInfo.setVersion(version); + microServiceInfo.setNamespace(namespace); + microServiceInfo.setHost(host); + microServiceInfo.setPath(path); + //系统间apigateway ä¿å­˜publish_port + if ("0".equals(ConfigUtil.getInstance().getVisualRangeMatches())) { + microServiceInfo.setPublish_port(publish_port); + } + + nodes.add(new Node(service.getAddress(), String.valueOf(service.getPort()))); + microServiceInfo.setNodes(nodes); + + microServiceInfo4version.put(version, microServiceInfo); + } else { + + Set newNodes = microServiceInfo4version.get(version).getNodes(); + // 默认node是注册信æ¯çš„IPå’Œport + newNodes.add(new Node(service.getAddress(), String.valueOf(service.getPort()))); + + // åŒå多版本åŒæ­¥ + microServiceInfo4version.get(version).setNodes(newNodes); + + } + + + /* + * // å¥åº·æ£€æŸ¥ä¿¡æ¯ List checks = value.getChecks(); node.setStatus("passing"); for (Check + * check : checks) { if (!"passing".equals(check.getStatus())) { + * node.setStatus(check.getStatus()); break; } } + */ + + + + } + + return microServiceInfo4version; + + } + +} diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/META-INF/MANIFEST.MF b/apiroute/apiroute-service/src/main/resources/api-doc/META-INF/MANIFEST.MF new file mode 100644 index 0000000..eaf90a9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Created-By: 1.7.0_72 (Oracle Corporation) + diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/WEB-INF/web.xml b/apiroute/apiroute-service/src/main/resources/api-doc/WEB-INF/web.xml new file mode 100644 index 0000000..7d51568 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/WEB-INF/web.xml @@ -0,0 +1,40 @@ + + + + + + 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 0000000..2250b71 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.eot differ 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 0000000..523cb92 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.ttf differ 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 0000000..67e3e25 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff differ 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 0000000..1e726a7 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff2 differ 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 0000000..ac2698e Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.eot differ 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 0000000..76aede2 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.ttf differ 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 0000000..abf1989 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff differ 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 0000000..9f93f74 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff2 differ diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/images/explorer_icons.png b/apiroute/apiroute-service/src/main/resources/api-doc/images/explorer_icons.png new file mode 100644 index 0000000..ed9d2ff Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/images/explorer_icons.png differ diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/images/logo_small.png b/apiroute/apiroute-service/src/main/resources/api-doc/images/logo_small.png new file mode 100644 index 0000000..5496a65 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/images/logo_small.png differ 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 0000000..f9f9cd4 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/images/pet_store_api.png differ diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/images/throbber.gif b/apiroute/apiroute-service/src/main/resources/api-doc/images/throbber.gif new file mode 100644 index 0000000..0639388 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/images/throbber.gif differ diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/images/wordnik_api.png b/apiroute/apiroute-service/src/main/resources/api-doc/images/wordnik_api.png new file mode 100644 index 0000000..dca4f14 Binary files /dev/null and b/apiroute/apiroute-service/src/main/resources/api-doc/images/wordnik_api.png differ diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/index.html b/apiroute/apiroute-service/src/main/resources/api-doc/index.html new file mode 100644 index 0000000..944b1f3 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/index.html @@ -0,0 +1,120 @@ + + + + + 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..f1b0277 --- /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_onap_msb_route_ui_i18n]'); + for(var i=0;i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + +
+ + +
+
+
+ + + +
+ + + + +
+ + + +
+
+ + {{apiGroupByPortArray[$index].length}} + +
+ +
+
+
+ + +
+ +
+
+ + {{routeUtil.showPotocol(route.publish_port,route.publishProtocol)|html}} +
+
+ + {{route.version}} + +
+ + + + + + + + + + + + +
+ +
+
+
+ +
+ + +
+
+
+ + +
+ + + +
+ + + +
+
+ + {{iuiGroupByPortArray[$index].length}} + +
+ +
+
+
+ + +
+ +
+
+ + {{routeUtil.showPotocol(route.publish_port,route.publishProtocol)|html}} +
+
+ + {{route.servers[0].ip}}:{{route.servers[0].port}} +
+ + + + + + + + + +
+ +
+
+
+ +
+ +
+ +
+
+ + +
+ + + +
+ + + + +
+
+ + {{customGroupByPortArray[$index].length}} + +
+ +
+
+
+ + +
+ +
+
+ + {{routeUtil.showPotocol(elem.publish_port,elem.publishProtocol)|html}} +
+
+ + + {{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 + $('