diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..c607b546c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[{package.json,.travis.yml}] +indent_size = 2 \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fae0c624d..ff44f6384 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,5 +18,5 @@ Follow this checklist to help us incorporate your contribution quickly and easil * [ ] Format the pull request title like `[ISSUE #123] Fix UnknownException when host config not exist`. Each commit in the pull request should have a meaningful subject line and body. * [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. * [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in [test module](https://github.com/alibaba/nacos/tree/master/test). -* [ ] Run `mvn -B clean apache-rat:check findbugs:findbugs` to make sure basic checks pass. Run `mvn clean install -DskipITs` to make sure unit-test pass. Run `mvn clean test-compile failsafe:integration-test` to make sure integration-test pass. +* [ ] Run `mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true` to make sure basic checks pass. Run `mvn clean install -DskipITs` to make sure unit-test pass. Run `mvn clean test-compile failsafe:integration-test` to make sure integration-test pass. diff --git a/.gitignore b/.gitignore index dbc93482a..b47f59ded 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,11 @@ target .project .idea +.vscode .DS_Store +.factorypath /logs *.iml node_modules test/derby.log +derby.log diff --git a/.travis.yml b/.travis.yml index f85f66155..b3a9f6497 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ notifications: - xchaos8@126.com - nacos_dev@linux.alibaba.com - dev-nacos@googlegroups.com + - mw_configcenter@list.alibaba-inc.com on_success: change on_failure: always @@ -24,7 +25,9 @@ before_install: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then jdk_switcher use "$CUSTOM_JDK"; fi script: - + - mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true + - mvn -Prelease-nacos clean install -U + - mvn clean package -Pit-test after_success: - - mvn clean install -Pit-test + - mvn clean package -Pit-test - mvn sonar:sonar -Psonar-apache diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bee36e01..5169d21d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,108 @@ +## 0.7.0(Dec, 2018) + +* [ #461 ] Registration failed when instance port is set to 0 +* [ #455 ] The console can't change the change code +* [ #447 ] 集群模式server挂掉一台后,提供方注册失败 +* [ #445 ] 0.6.1控制台创建配置发布提交时,提示信息有问题 +* [ #442 ] Typos in class names and variables. +* [ #413 ] The console has some uncaught exceptions +* [ #395 ] nacos surport mysql in the case of stand-alone mode +* [ #393 ] Support operation of selector on console +* [ #365 ] NodeJs SDK support +* [ #362 ] The metadata will lost when online or offline instance through web ui +* [ #187 ] Provide Label ability for Naming Service into NACOS for complex multi-DC scenario. + +## 0.6.1(Dec, 2018) + +* [#421] NamingService's serivce name can't use colon(:) in Windows +* [#432] When packing nacos-core, ${user.home} is replaced in the logback configuration file (nacos.xml) + +## 0.6.0(Dec, 2018) + +* [#388] Cluster name should be provided in the Instance +* [#377] Clean up messy code in Naming module +* [#369] Support instance list persisted on disk +* [#366] findbugs-maven-plugin version +* [#362] The metadata will lost when online or offline instance through web ui +* [#352] Refactoring internationalization Nacos console +* [#278] Nacos docker img +* [#243] optimize the efficiency of integration testing, it’s taking too long now + +## 0.5.0(Nov, 2018) + +* [#148] Naming write performace. +* [#175] Support deregistering instance automatically. +* [#176] Naming client query instance method should bypass local cache at client start. +* [#177] Console supports registering new empty service and delete empty service. +* [#181] NPE when adding a instance if no leader in the raft cluster. +* [#193] Configure host domain name cause nacos server cluster is unavailable. +* [#209] Disable service and cluster level customization in client registerInstance method. +* [#214] Please support Java 11. +* [#222] print more nacos server start status info in start.log. +* [#231] Refactoring: Parsing the Nacos home directory and the cluster.conf file. +* [#246] "mvn -B clean apache-rat:check findbugs:findbugs" did not work as expected. +* [#251] Console Editor Optimization. +* [#254] DataId and group are required in historical version and listener query. +* [#256] Whether the service discovery data needs to add a newline link symbol. +* [#257] Listening query switching query dimension data is not refreshed. +* [#258] Remove the Balloon of DataId/Group. +* [#259] Listening query paging size problem. +* [#272] "#it is ip" is also parsed into an instance IP. +* [#275] nacos coredns plugin to support DNS. +* [#281] We should lint the console code. +* [#302] Maven build project supports java 11. +* [#316] In stand alone mode, Nacos still checks the cluster.conf. + +## 0.4.0(Nov 7, 2018) + +* [#216] Fix tenant dir problem +* [#197] Service update ignored some properties +* [#190] Client beat lose weight info and metadata info +* [#188] Console delete data cannot be updated in time +* [#179] Listening query fail when namespace is not blank +* [#157] Lack information in readme.md to describe the related project repositories for Nacos echosystem +* [#144] There have a error and something are not clear +* [#106] Snapshot file create error +* [#92] Eliminate warnings, refactor code, show start.log detail + + +## 0.3.0(Oct 26, 2018) + +* [#171] UI debug errors +* [#156] Web UI 404 problem +* [#155] use local resource +* [#145] nacos-example not found :org.apache.logging.log4j.core.Logger +* [#142] UI console show Group +* [#149] Fix naming client beat process failed bug. +* [#150] Fix naming service registration hangs bug. + +## 0.3.0-RC1(Oct 19, 2018) + +* [#33] Support console for config management. +* [#51] Support console for naming service. +* [#121] Fix get instance method hanging bug. +* [#138] Add a flag to indicate if instance is offline. +* [#130] Fix health check disabled if machine has one CPU core bug. +* [#139] Fix still get instance with zero weight bug. +* [#128] Fix console layout bug. + + + +## 0.2.1-release(Sept 28, 2018) + +* FIx deregister last instance failed error. +* Fix url pattern error. +* Fully integrate with and seamlessly support Spring framework, Spring Boot and Spring Cloud +* Separate nacos-api from nacos client implementation +* Support high available cluster mode +* Fix cluster node health check abnormality +* Fix stand-alone mode gets the change history list exception +* Fix Pulling does not exist configuration print io exception +* Optimized log framework +* Service Discovery: Client support getting server status. +* Service Discovery: Client support get all service names of server. +* Service Discovery: Client support get all subscribed services. + ## 0.2.0 (Sept 17, 2018) #### FEATURES: diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..58a6aefc5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at users-nacos@googlegroups.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e75d19700..534b50af3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,31 +1,124 @@ -## How To Contribute +# Contributing to Nacos -We are always very happy to have contributions, whether for trivial cleanups or big new features. -We want to have high quality, well documented codes for each programming language. +Welcome to Nacos! This document is a guideline about how to contribute to Nacos. -Nor is code the only way to contribute to the project. We strongly value documentation, integration with other project, and gladly accept improvements for these aspects. +If you find something incorrect or missing, please leave comments / suggestions. -## Contributing code +## Before you get started -To submit a change for inclusion, please do the following: +### Code of Conduct -#### If the change is non-trivial please include some unit tests that cover the new functionality. -#### If you are introducing a completely new feature or API it is a good idea to start a wiki and get consensus on the basic design first. -#### It is our job to follow up on patches in a timely fashion. Nag us if we aren't doing our job (sometimes we drop things). +Please make sure to read and observe our [Code of Conduct](./CODE_OF_CONDUCT.md). -## Becoming a Committer +## Contributing -We are always interested in adding new contributors. What we look for are series of contributions, good taste and ongoing interest in the project. If you are interested in becoming a committer, please let one of the existing committers know and they can help you walk through the process. +Nacos welcome new participants of any role, including user, contributor, committer and PMC. -Nowadays,we have several important contribution points: -#### Wiki & JavaDoc -#### Nacos Console -#### Nacos SDK(C++\.Net\Php\Python\Go\Node.js) +![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/contributor_definition.png) -##### Prerequisite -If you want to contribute the above listing points, you must abide our some prerequisites: -###### Readability - API must have Javadoc,some very important methods also must have javadoc -###### Testability - 80% above unit test coverage about main process -###### Maintainability - Comply with our [PMD spec](style/codeStyle.xml), and at least 3 month update frequency -###### Deployability - We encourage you to deploy into [maven repository](http://search.maven.org/) +We encourage new comers actively join in Nacos projects and involving from user role to committer role, and even PMC role. In order to accomplish this, new comers needs to actively contribute in Nacos project. The following paragraph introduce how to contribute in Nacos way. + +#### Open / pickup an issue for preparation + +If you find a typo in document, find a bug in code, or want new features, or want to give suggestions, you can [open an issue on GitHub](https://github.com/alibaba/Nacos/issues/new) to report it. + +If you just want to contribute directly you can choose the issue below. + +- [Contribution Welcome](https://github.com/alibaba/nacos/labels/contribution%20welcome): Heavily needed issue, but currently short of hand. + +- [good first issue](https://github.com/alibaba/nacos/labels/good%20first%20issue): Good for newcomers, new comer can pickup one for warm-up. + + +We strongly value documentation and integration with other projects such as Spring Cloud, Kubernetes, Dubbo, etc. We are very glad to work on any issue for these aspects. + +Please note that any PR must be associated with a valid issue. Otherwise the PR will be rejected. + +#### Begin your contribute + +Now if you want to contribute, please create a new pull request. + +We use the `develop` branch as the development branch, which indicates that this is a unstable branch. + +Further more, our branching model complies to [https://nvie.com/posts/a-successful-git-branching-model/](https://nvie.com/posts/a-successful-git-branching-model/). We strongly suggest new comers walk through the above article before creating PR. + +Now, if you are ready to create PR, here is the workflow for contributors: + +1. Fork to your own + +2. Clone fork to local repository + +3. Create a new branch and work on it + +4. Keep your branch in sync + +5. Commit your changes (make sure your commit message concise) + +6. Push your commits to your forked repository + +7. Create a pull request to **develop** branch. + + +When creating pull request: + +1. Please follow [the pull request template](./.github/PULL_REQUEST_TEMPLATE.md). + +2. Please create the request to **develop** branch. + +3. Please make sure the PR has a corresponding issue. + +4. If your PR contains large changes, e.g. component refactor or new components, please write detailed documents about its design and usage. + +5. Note that a single PR should not be too large. If heavy changes are required, it's better to separate the changes to a few individual PRs. + +6. After creating a PR, one or more reviewers will be assigned to the pull request. + +7. Before merging a PR, squash any fix review feedback, typo, merged, and rebased sorts of commits. The final commit message should be clear and concise. + + +If your PR contains large changes, e.g. component refactor or new components, please write detailed documents about its design and usage. + +### Code review guidance + +Committers will rotate reviewing the code to make sure all the PR will be reviewed timely and by at least one committer before merge. If we aren't doing our job (sometimes we drop things). And as always, we welcome volunteers for code review. + +Some principles: + +- Readability - Important code should be well-documented. API should have Javadoc. Code style should be complied with the existing one. + +- Elegance: New functions, classes or components should be well designed. + +- Testability - 80% of the new code should be covered by unit test cases. + +- Maintainability - Comply with our [PMD spec](style/codeStyle.xml), and 3-month-frequency update should be maintained at least. + + +### Now how about try become a committer? + +Generally speaking, contribute 8 non-trivial patches and get at least three different people to review them (you'll need three people to support you). Then ask someone to nominate you. You're demonstrating your: + +- at least 8 PR and the associated issues to the project, + +- ability to collaborate with the team, + +- understanding of the projects' code base and coding style, and + +- ability to write good code (last but certainly not least) + + +A current committer nominates you by slacking the team on the Nacos issue with label "nomination" + +- your first and last name + +- a link to your Git profile + +- an explanation of why you should be a committer, + +- Elaborate the top 3 PR and the associated issues the nominator has worked with you that can demonstrate your ability. + + +Two other committer need to second your nomination. If no one objects in 5 working days (China), you're a committer. If anyone objects or wants more information, the committers discuss and usually come to a consensus (within the 5 working days). If issues cannot be resolved, there's a vote among current committers. + +![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/nomination_process.png) + +In the worst case, this can drag out for two weeks. Keep contributing! Even in the rare cases where a nomination fails, the objection is usually something easy to address like "more patches" or "not enough people are familiar with this person's work." diff --git a/README.md b/README.md index 69222deb6..8b4c18b15 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ -## Nacos + + +# Nacos: Dynamic *Na*ming and *Co*nfiguration *S*ervice [![Gitter](https://badges.gitter.im/alibaba/nacos.svg)](https://gitter.im/alibaba/nacos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) [![Gitter](https://travis-ci.org/alibaba/nacos.svg?branch=master)](https://travis-ci.org/alibaba/nacos) ------- - -Nacos is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily. + +## What does it do + +Nacos (official site: [http://nacos.io](http://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily. Service is a first-class citizen in Nacos. Nacos supports almost all type of services,for example,[Dubbo/gRPC service](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html)、[Spring Cloud RESTFul service](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) or [Kubernetes service](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html). @@ -29,60 +33,70 @@ Nacos provides four major functions. Nacos provides an easy-to-use service dashboard to help you manage your services metadata, configuration, kubernetes DNS, service health and metrics statistics. -### Quick Start +## Quick Start It is super easy to get started with your first project. -1. #### Download run package -[Download](https://github.com/alibaba/nacos/releases/download/0.2.1/nacos-server-0.2.1.zip) +#### Step 1: Download the binary package +You can download the package from the [latest stable release](https://github.com/alibaba/nacos/releases). + +Take release nacos-server-0.6.1.zip for example. ``` -unzip nacos-server-0.2.1.zip +unzip nacos-server-0.6.1.zip cd nacos/bin ``` -2. #### Start Server -* ##### Linux/Unix/Mac +#### Step 2: Start Server +On the **Linux/Unix/Mac** platform, run the following command to start server with standalone mode: ``` -Run the following command to start (standalone means non-cluster mode): - sh startup.sh -m standalone ``` -* ##### Windows +On the **Windows** platform, run the following command to start server with standalone mode. Alternatively, you can also double-click the startup.cmd to run NacosServer. ``` -Run the following command to start: - -cmd startup.cmd - -Or double-click the startup.cmd to run NacosServer. +cmd startup.cmd -m standalone ``` For more details, see [quick-start.](https://nacos.io/en-us/docs/quick-start.html) -3. #### Quick start for other open-source projects: +## Quick start for other open-source projects: +* [Quick start with Nacos command and console](https://nacos.io/en-us/docs/quick-start.html) -* [quick start with spring cloud](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) +* [Quick start with dubbo](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html) -* [quick start with dubbo](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html) +* [Quick start with spring cloud](https://nacos.io/en-us/docs/quick-start-spring-cloud.html) -* [quick start with kubernetes](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html) +* [Quick start with kubernetes](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html) -* [more...](https://nacos.io/) -### Documentation +## Documentation -You can view full documentation on the Nacos website: +You can view the full documentation from the [Nacos website](https://nacos.io/en-us/docs/what-is-nacos.html). -* [nacos.io](https://nacos.io/en-us/docs/what-is-nacos.html) +All the latest and long-term notice can also be found here from [Github notice issue](https://github.com/alibaba/nacos/labels/notice) -### Contact -* #### Gitter-[Nacos Gitter](https://gitter.im/alibaba/nacos) +## Contributing -* #### Weibo-[Nacos Weibo](https://weibo.com/u/6574374908) +Contributors are welcomed to join Nacos project. Please check [CONTRIBUTING](./CONTRIBUTING.md) about how to contribute to this project. -* #### Segmentfault-[Nacos Segmentfault](https://segmentfault.com/t/nacos) +## Other Related Project Repositories -* #### Mailing list-[nacos\_dev@linux.alibaba.com](mailto:nacos_dev@linux.alibaba.com). +* [nacos-spring-project](https://github.com/nacos-group/nacos-spring-project) provides the integration functionality for Spring. +* [nacos-group](https://github.com/nacos-group) is the reposity that hosts the eco tools for Nacos, such as SDK, synchronization tool, etc. +* [spring-cloud-alibaba](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) provides the one-stop solution for application development over Alibaba middleware which includes Nacos. +## Contact + +* [Gitter](https://gitter.im/alibaba/nacos): Nacos's IM tool for community messaging, collaboration and discovery. +* [Twitter](https://twitter.com/nacos2): Follow along for latest nacos news on Twitter. +* [Weibo](https://weibo.com/u/6574374908): Follow along for latest nacos news on Weibo (Twitter of China version). +* [Nacos Segmentfault](https://segmentfault.com/t/nacos): Get latest notice and prompt help from Segmentfault. +* Email Group: + * users-nacos@googlegroups.com: Nacos usage general discussion. + * dev-nacos@googlegroups.com: Nacos developer discussion (APIs, feature design, etc). + * commits-nacos@googlegroups.com: Commits notice, very high frequency. +* Join us from wechat. Welcome words: Nacos. + +![cwex](http://acm-public.oss-cn-hangzhou.aliyuncs.com/xuc.png) diff --git a/REPORTING-BUGS.md b/REPORTING-BUGS.md index df2fe10af..e8e075430 100644 --- a/REPORTING-BUGS.md +++ b/REPORTING-BUGS.md @@ -21,7 +21,7 @@ We might ask for further information to locate a bug. A duplicated bug report wi # 如何提交错误报告 -如果Nacos项目的任何部分存在问题或文档问题,请通过[opening an issue][Nacos-issue]告诉我们。我们非常认真地对待错误和错误,在产品面前没有不重要的问题。不过在创建错误报告之前,请检查是否存在报告相同问题的issues。 +如果Nacos项目的任何部分存在问题或文档问题,请通过[opening an issue][Nacos-issue]告诉我们。我们非常认真地对待错误和缺陷,在产品面前没有不重要的问题。不过在创建错误报告之前,请检查是否存在报告相同问题的issues。 为了使错误报告准确且易于理解,请尝试创建以下错误报告: @@ -36,4 +36,4 @@ We might ask for further information to locate a bug. A duplicated bug report wi 我们可能会要求您提供更多信息以查找错误。将关闭重复的错误报告。 [etcd-issue]:https://github.com/etcd-io/etcd/issues/new -[filing-good-bugs]:http://fantasai.inkedblade.net/style/talks/filing-good-bugs/ \ No newline at end of file +[filing-good-bugs]:http://fantasai.inkedblade.net/style/talks/filing-good-bugs/ diff --git a/api/pom.xml b/api/pom.xml index 6bec84582..05dfc4dce 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -12,30 +12,55 @@ limitations under the License. --> - - com.alibaba.nacos - nacos-all - 0.2.1 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + com.alibaba.nacos + nacos-all + 0.7.0 + - 4.0.0 + 4.0.0 - nacos-api - jar + nacos-api + jar - nacos-api ${project.version} - http://maven.apache.org + nacos-api ${project.version} + http://maven.apache.org + + + + org.apache.maven.plugins + maven-compiler-plugin + + 7 + 7 + + + + - - UTF-8 - - - - - com.alibaba - fastjson - - + + UTF-8 + + + + com.alibaba + fastjson + + + org.apache.commons + commons-lang3 + + + junit + junit + test + + + org.springframework + spring-test + test + + diff --git a/api/src/main/java/com/alibaba/nacos/api/NacosFactory.java b/api/src/main/java/com/alibaba/nacos/api/NacosFactory.java index ae8e75e00..6020468cd 100644 --- a/api/src/main/java/com/alibaba/nacos/api/NacosFactory.java +++ b/api/src/main/java/com/alibaba/nacos/api/NacosFactory.java @@ -31,7 +31,7 @@ import com.alibaba.nacos.api.naming.NamingService; public class NacosFactory { /** - * create config service + * Create config service * * @param properties init param * @return config @@ -42,7 +42,7 @@ public class NacosFactory { } /** - * create config service + * Create config service * * @param serverAddr server list * @return config @@ -53,7 +53,7 @@ public class NacosFactory { } /** - * create naming service + * Create naming service * * @param serverAddr server list * @return Naming @@ -64,7 +64,7 @@ public class NacosFactory { } /** - * create naming service + * Create naming service * * @param properties init param * @return Naming diff --git a/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java b/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java index 91c97707b..03941b0c9 100644 --- a/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java +++ b/api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java @@ -38,4 +38,6 @@ public class PropertyKeyConst { public final static String ENCODE = "encode"; + public final static String NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart"; + } diff --git a/api/src/main/java/com/alibaba/nacos/api/annotation/NacosInjected.java b/api/src/main/java/com/alibaba/nacos/api/annotation/NacosInjected.java index ac8ec7765..ba9d636bc 100644 --- a/api/src/main/java/com/alibaba/nacos/api/annotation/NacosInjected.java +++ b/api/src/main/java/com/alibaba/nacos/api/annotation/NacosInjected.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/api/src/main/java/com/alibaba/nacos/api/annotation/NacosProperties.java b/api/src/main/java/com/alibaba/nacos/api/annotation/NacosProperties.java index fad6d66aa..a6bd2ad6d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/annotation/NacosProperties.java +++ b/api/src/main/java/com/alibaba/nacos/api/annotation/NacosProperties.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/core/src/test/java/com/alibaba/nacos/core/AppTest.java b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Entity.java similarity index 50% rename from core/src/test/java/com/alibaba/nacos/core/AppTest.java rename to api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Entity.java index 60c39f223..6124839b2 100644 --- a/core/src/test/java/com/alibaba/nacos/core/AppTest.java +++ b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Entity.java @@ -13,41 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alibaba.nacos.core; +package com.alibaba.nacos.api.cmdb.pojo; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; +import java.util.Map; /** - * Unit test for simple App. + * @author nkorange */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); +public class Entity { + + private String type; + private String name; + private Map labels; + + public String getType() { + return type; } - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); + public void setType(String type) { + this.type = type; } - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getLabels() { + return labels; + } + + public void setLabels(Map labels) { + this.labels = labels; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEvent.java b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEvent.java new file mode 100644 index 000000000..a51314ba4 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEvent.java @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.cmdb.pojo; + +/** + * @author nkorange + */ +public class EntityEvent { + + private EntityEventType type; + private String entityName; + private String entityType; + + public EntityEventType getType() { + return type; + } + + public void setType(EntityEventType type) { + this.type = type; + } + + public String getEntityName() { + return entityName; + } + + public void setEntityName(String entityName) { + this.entityName = entityName; + } + + public String getEntityType() { + return entityType; + } + + public void setEntityType(String entityType) { + this.entityType = entityType; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEventType.java b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEventType.java new file mode 100644 index 000000000..5ad6dfac5 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEventType.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.cmdb.pojo; + +/** + * @author nkorange + */ +public enum EntityEventType { + /** + * + */ + ENTITY_ADD_OR_UPDATE, + /** + * + */ + ENTITY_REMOVE +} diff --git a/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Label.java b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Label.java new file mode 100644 index 000000000..45883e6c3 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Label.java @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.cmdb.pojo; + +import java.util.Set; + +/** + * @author nkorange + */ +public class Label { + + private String name; + private Set values; + private String description; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getValues() { + return values; + } + + public void setValues(Set values) { + this.values = values; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/PreservedEntityTypes.java b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/PreservedEntityTypes.java new file mode 100644 index 000000000..b48729cdc --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/PreservedEntityTypes.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.cmdb.pojo; + +/** + * @author nkorange + */ +public enum PreservedEntityTypes { + /** + * Ip + */ + ip, + /** + * Service + */ + service +} diff --git a/api/src/main/java/com/alibaba/nacos/api/cmdb/spi/CmdbService.java b/api/src/main/java/com/alibaba/nacos/api/cmdb/spi/CmdbService.java new file mode 100644 index 000000000..eadee776f --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/cmdb/spi/CmdbService.java @@ -0,0 +1,97 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.cmdb.spi; + +import com.alibaba.nacos.api.cmdb.pojo.Entity; +import com.alibaba.nacos.api.cmdb.pojo.EntityEvent; +import com.alibaba.nacos.api.cmdb.pojo.Label; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Service to visit CMDB store + * + * @author nkorange + */ +public interface CmdbService { + + /** + * Get all label names stored in CMDB + * + * @return label name set + */ + Set getLabelNames(); + + /** + * Get all possible entity types in CMDB + * + * @return all entity types + */ + Set getEntityTypes(); + + /** + * Get label info + * + * @param labelName label name + * @return label info + */ + Label getLabel(String labelName); + + /** + * Get label value of label name of ip + * + * @param entityName entity name + * @param entityType entity type + * @param labelName target label name + * @return label value + */ + String getLabelValue(String entityName, String entityType, String labelName); + + /** + * Get all label value of ip + * + * @param entityName entity name + * @param entityType entity type + * @return all label values + */ + Map getLabelValues(String entityName, String entityType); + + /** + * Dump all entities in CMDB + * + * @return all entities + */ + Map> getAllEntities(); + + /** + * get label change events + * + * @param timestamp start time of generated events + * @return label events + */ + List getEntityEvents(long timestamp); + + /** + * Get single entity + * + * @param entityName name of entity + * @param entityType type of entity + * @return + */ + Entity getEntity(String entityName, String entityType); +} diff --git a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java index ba40dbf74..130fe7d5d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java +++ b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java @@ -85,11 +85,6 @@ public class Constants { */ public static final int ONCE_TIMEOUT = 2000; - /** - * millisecond - */ - public static final int CONN_TIMEOUT = 2000; - /** * millisecond */ @@ -110,9 +105,9 @@ public class Constants { public static final int FLOW_CONTROL_INTERVAL = 1000; - public static final String LINE_SEPARATOR = Character.toString((char) 1); + public static final String LINE_SEPARATOR = Character.toString((char)1); - public static final String WORD_SEPARATOR = Character.toString((char) 2); + public static final String WORD_SEPARATOR = Character.toString((char)2); public static final String LONGPULLING_LINE_SEPARATOR = "\r\n"; @@ -124,4 +119,7 @@ public class Constants { public static final String NAMING_INSTANCE_ID_SPLITTER = "#"; public static final int NAMING_INSTANCE_ID_SEG_COUNT = 4; + public static final String NAMING_HTTP_HEADER_SPILIER = "\\|"; + + public static final String NAMING_DEFAULT_CLUSTER_NAME = "DEFAULT"; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigFactory.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigFactory.java index 298705c70..52179b112 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigFactory.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigFactory.java @@ -29,7 +29,7 @@ import com.alibaba.nacos.api.exception.NacosException; public class ConfigFactory { /** - * create config service + * Create Config * * @param properties init param * @return ConfigService @@ -47,11 +47,11 @@ public class ConfigFactory { } /** - * create config service + * Create Config * - * @param serverAddr serverlist - * @return ConfigService - * @throws NacosException Exception + * @param serverAddr serverList + * @return Config + * @throws ConfigService Exception */ public static ConfigService createConfigService(String serverAddr) throws NacosException { Properties properties = new Properties(); diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index 07743b0bf..3e79f950c 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -26,7 +26,7 @@ import com.alibaba.nacos.api.exception.NacosException; public interface ConfigService { /** - * get config + * Get config * * @param dataId dataId * @param group group @@ -52,7 +52,7 @@ public interface ConfigService { void addListener(String dataId, String group, Listener listener) throws NacosException; /** - * publish config. + * Publish config. * * @param dataId dataId * @param group group @@ -63,7 +63,7 @@ public interface ConfigService { boolean publishConfig(String dataId, String group, String content) throws NacosException; /** - * remove config + * Remove config * * @param dataId dataId * @param group group @@ -73,7 +73,7 @@ public interface ConfigService { boolean removeConfig(String dataId, String group) throws NacosException; /** - * remove listener + * Remove listener * * @param dataId dataId * @param group group @@ -82,7 +82,7 @@ public interface ConfigService { void removeListener(String dataId, String group, Listener listener); /** - * get server status + * Get server status * * @return whether health */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigListener.java b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigListener.java index aad64b87a..dfc490385 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigListener.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -57,8 +56,7 @@ public @interface NacosConfigListener { Class converter() default NacosConfigConverter.class; /** - * The {@link NacosProperties} attribute, If not specified, it will use - * global Nacos Properties. + * The {@link NacosProperties} attribute, If not specified, it will use global Nacos Properties. * * @return the default value is {@link NacosProperties} */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java index 2cd57f19b..2eedfeb93 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -24,7 +23,6 @@ import java.lang.annotation.*; import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; - /** * An annotation for Nacos configuration Properties for binding POJO as Properties Object. * @@ -52,51 +50,46 @@ public @interface NacosConfigurationProperties { String dataId(); /** - * It indicates the properties of current doBind bean is auto-refreshed - * when Nacos configuration is changed. + * It indicates the properties of current doBind bean is auto-refreshed when Nacos configuration is changed. * * @return default value is false */ boolean autoRefreshed() default false; - /** - * Flag to indicate that when binding to this object invalid fields should be ignored. - * Invalid means invalid according to the binder that is used, and usually this means - * fields of the wrong type (or that cannot be coerced into the correct type). + * Flag to indicate that when binding to this object invalid fields should be ignored. Invalid means invalid + * according to the binder that is used, and usually this means fields of the wrong type (or that cannot be coerced + * into the correct type). * * @return the flag value (default false) */ boolean ignoreInvalidFields() default false; /** - * Flag to indicate that when binding to this object fields with periods in their - * names should be ignored. + * Flag to indicate that when binding to this object fields with periods in their names should be ignored. * * @return the flag value (default false) */ boolean ignoreNestedProperties() default false; /** - * Flag to indicate that when binding to this object unknown fields should be ignored. - * An unknown field could be a sign of a mistake in the Properties. + * Flag to indicate that when binding to this object unknown fields should be ignored. An unknown field could be a + * sign of a mistake in the Properties. * * @return the flag value (default true) */ boolean ignoreUnknownFields() default true; /** - * Flag to indicate that an exception should be raised if a Validator is available and - * validation fails. If it is set to false, validation errors will be swallowed. They - * will be logged, but not propagated to the caller. + * Flag to indicate that an exception should be raised if a Validator is available and validation fails. If it is + * set to false, validation errors will be swallowed. They will be logged, but not propagated to the caller. * * @return the flag value (default true) */ boolean exceptionIfInvalid() default true; /** - * The {@link NacosProperties} attribute, If not specified, it will use - * global Nacos Properties. + * The {@link NacosProperties} attribute, If not specified, it will use global Nacos Properties. * * @return the default value is {@link NacosProperties} */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosIgnore.java b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosIgnore.java index 99d65c98b..6d3ce0399 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosIgnore.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosIgnore.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -19,8 +18,7 @@ package com.alibaba.nacos.api.config.annotation; import java.lang.annotation.*; /** - * An annotation for ignore field from annotated - * {@link NacosConfigurationProperties} Properties Object. + * An annotation for ignore field from annotated {@link NacosConfigurationProperties} Properties Object. * * @author Mercy * @see NacosConfigurationProperties diff --git a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosProperty.java b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosProperty.java index 2be36cbd8..80aa3c661 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosProperty.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosProperty.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -19,9 +18,8 @@ package com.alibaba.nacos.api.config.annotation; import java.lang.annotation.*; /** - * An annotation for Nacos Property name of Nacos Configuration to - * bind a field from annotated {@link NacosConfigurationProperties} - * Properties Object. + * An annotation for Nacos Property name of Nacos Configuration to bind a field from annotated {@link + * NacosConfigurationProperties} Properties Object. * * @author Mercy * @see NacosConfigurationProperties diff --git a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosValue.java b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosValue.java index b9dbdbd6f..a77ac669e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosValue.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosValue.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/api/src/main/java/com/alibaba/nacos/api/config/convert/NacosConfigConverter.java b/api/src/main/java/com/alibaba/nacos/api/config/convert/NacosConfigConverter.java index fc0cb216e..d3e3702b2 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/convert/NacosConfigConverter.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/convert/NacosConfigConverter.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -41,5 +40,4 @@ public interface NacosConfigConverter { */ T convert(String config); - } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigContext.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigContext.java index 63ab4d2ff..5b9d638a2 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigContext.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigContext.java @@ -22,7 +22,7 @@ package com.alibaba.nacos.api.config.filter; */ public interface IConfigContext { /** - * get context param by key + * Get context param by key * * @param key * @return context @@ -30,7 +30,7 @@ public interface IConfigContext { Object getParameter(String key); /** - * set context param + * Set context param * * @param key key * @param value value diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java index c4217418a..8e7b2ba2e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java @@ -24,7 +24,7 @@ import com.alibaba.nacos.api.exception.NacosException; */ public interface IConfigFilter { /** - * init + * Init * * @param filterConfig Filter Config */ @@ -47,14 +47,14 @@ public interface IConfigFilter { void deploy(); /** - * get order + * Get order * * @return order number */ int getOrder(); /** - * get filterName + * Get filterName * * @return filter name */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilterChain.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilterChain.java index 8f7a764da..fa97dcfab 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilterChain.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilterChain.java @@ -24,7 +24,7 @@ import com.alibaba.nacos.api.exception.NacosException; */ public interface IConfigFilterChain { /** - * filter aciton + * Filter aciton * * @param request request * @param response response diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigResponse.java index b08e9946c..c792f8db8 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigResponse.java @@ -30,7 +30,7 @@ public interface IConfigResponse { Object getParameter(String key); /** - * get config context + * Get config context * * @return configContext */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java b/api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java index 26ba4508e..bea4f9a4c 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/filter/IFilterConfig.java @@ -30,7 +30,7 @@ public interface IFilterConfig { String getFilterName(); /** - * get init param + * Get init param * * @param name * @return param diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java index c9d8af5e6..f0f874a2b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java @@ -26,7 +26,7 @@ import java.util.concurrent.Executor; public abstract class AbstractListener implements Listener { /** - * use default executor + * Use default executor */ @Override public Executor getExecutor() { diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/Listener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/Listener.java index 5e8ca457a..501168a3f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/Listener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/Listener.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.api.config.listener; import java.util.concurrent.Executor; - /** * Listener for watch config * @@ -26,15 +25,14 @@ import java.util.concurrent.Executor; public interface Listener { /** - * get executor for execute this receive + * Get executor for execute this receive * * @return Executor */ Executor getExecutor(); - /** - * receive config info + * Receive config info * * @param configInfo config info */ diff --git a/api/src/main/java/com/alibaba/nacos/api/exception/NacosException.java b/api/src/main/java/com/alibaba/nacos/api/exception/NacosException.java index 1d10dd753..7806e9635 100644 --- a/api/src/main/java/com/alibaba/nacos/api/exception/NacosException.java +++ b/api/src/main/java/com/alibaba/nacos/api/exception/NacosException.java @@ -75,7 +75,6 @@ public class NacosException extends Exception { */ public static final int CLIENT_OVER_THRESHOLD = -503; - /** * server error code * 400 403 throw exception to user @@ -107,5 +106,4 @@ public class NacosException extends Exception { */ public static final int OVER_THRESHOLD = 503; - } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingFactory.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingFactory.java index d554bbd06..d018eca02 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingFactory.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingFactory.java @@ -27,25 +27,25 @@ import com.alibaba.nacos.api.exception.NacosException; */ public class NamingFactory { - public static NamingService createNamingService(String serverList) throws NacosException { - try { - Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); - Constructor constructor = driverImplClass.getConstructor(String.class); - NamingService vendorImpl = (NamingService) constructor.newInstance(serverList); - return vendorImpl; - } catch (Throwable e) { - throw new NacosException(-400, e.getMessage()); - } - } + public static NamingService createNamingService(String serverList) throws NacosException { + try { + Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); + Constructor constructor = driverImplClass.getConstructor(String.class); + NamingService vendorImpl = (NamingService)constructor.newInstance(serverList); + return vendorImpl; + } catch (Throwable e) { + throw new NacosException(-400, e.getMessage()); + } + } - public static NamingService createNamingService(Properties properties) throws NacosException { - try { - Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); - Constructor constructor = driverImplClass.getConstructor(Properties.class); - NamingService vendorImpl = (NamingService) constructor.newInstance(properties); - return vendorImpl; - } catch (Throwable e) { - throw new NacosException(-400, e.getMessage()); - } - } + public static NamingService createNamingService(Properties properties) throws NacosException { + try { + Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); + Constructor constructor = driverImplClass.getConstructor(Properties.class); + NamingService vendorImpl = (NamingService)constructor.newInstance(properties); + return vendorImpl; + } catch (Throwable e) { + throw new NacosException(-400, e.getMessage()); + } + } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index 1ece5c4ad..02622d8df 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.selector.AbstractSelector; import java.util.List; @@ -189,7 +190,22 @@ public interface NamingService { ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException; /** +<<<<<<< HEAD * get all subscribed services of current client +======= + * Get all service names from server + * + * @param pageNo page index + * @param pageSize page size + * @param selector selector to filter the resource + * @return list of service names + * @throws NacosException + */ + ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException; + + /** + * Get all subscribed services of current client +>>>>>>> upstream/develop * * @return subscribed services * @throws NacosException diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/AbstractHealthChecker.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/AbstractHealthChecker.java index 583a5b2f0..af71be548 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/AbstractHealthChecker.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/AbstractHealthChecker.java @@ -15,10 +15,17 @@ */ package com.alibaba.nacos.api.naming.pojo; +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.nacos.api.common.Constants; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; /** - * @author dungu.zpf + * @author nkorange */ public abstract class AbstractHealthChecker implements Cloneable { @@ -32,6 +39,14 @@ public abstract class AbstractHealthChecker implements Cloneable { this.type = type; } + /** + * Clone all fields of this instance to another one + * + * @return Another instance with exactly the same fields. + * @throws CloneNotSupportedException + */ + public abstract AbstractHealthChecker clone() throws CloneNotSupportedException; + public static class Http extends AbstractHealthChecker { public static final String TYPE = "HTTP"; @@ -68,6 +83,25 @@ public abstract class AbstractHealthChecker implements Cloneable { this.headers = headers; } + @JSONField(serialize = false) + public Map getCustomHeaders() { + if (StringUtils.isBlank(headers)) { + return Collections.emptyMap(); + } + + Map headerMap = new HashMap(16); + for (String s : headers.split(Constants.NAMING_HTTP_HEADER_SPILIER)) { + String[] splits = s.split(":"); + if (splits.length != 2) { + continue; + } + + headerMap.put(StringUtils.trim(splits[0]), StringUtils.trim(splits[1])); + } + + return headerMap; + } + @Override public int hashCode() { return Objects.hash(path, headers, expectedResponseCode); @@ -79,7 +113,7 @@ public abstract class AbstractHealthChecker implements Cloneable { return false; } - Http other = (Http) obj; + Http other = (Http)obj; if (!strEquals(type, other.getType())) { return false; @@ -93,6 +127,18 @@ public abstract class AbstractHealthChecker implements Cloneable { } return expectedResponseCode == other.getExpectedResponseCode(); } + + @Override + public Http clone() throws CloneNotSupportedException { + Http config = new Http(); + + config.setPath(this.getPath()); + config.setHeaders(this.getHeaders()); + config.setType(this.getType()); + config.setExpectedResponseCode(this.getExpectedResponseCode()); + + return config; + } } public static class Tcp extends AbstractHealthChecker { @@ -112,6 +158,12 @@ public abstract class AbstractHealthChecker implements Cloneable { return obj instanceof Tcp; } + + public Tcp clone() throws CloneNotSupportedException { + Tcp config = new Tcp(); + config.setType(this.type); + return config; + } } public static class Mysql extends AbstractHealthChecker { @@ -160,7 +212,7 @@ public abstract class AbstractHealthChecker implements Cloneable { return false; } - Mysql other = (Mysql) obj; + Mysql other = (Mysql)obj; if (!strEquals(user, other.getUser())) { return false; @@ -173,6 +225,17 @@ public abstract class AbstractHealthChecker implements Cloneable { return strEquals(cmd, other.getCmd()); } + + @Override + public Mysql clone() throws CloneNotSupportedException { + Mysql config = new Mysql(); + config.setUser(this.getUser()); + config.setPwd(this.getPwd()); + config.setCmd(this.getCmd()); + config.setType(this.getType()); + + return config; + } } private static boolean strEquals(String str1, String str2) { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Cluster.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Cluster.java index 287975fab..e4ec09ccd 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Cluster.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Cluster.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Map; /** +<<<<<<< HEAD * * * @author dungu.zpf @@ -33,7 +34,7 @@ public class Cluster { /** * Name of cluster */ - private String name = ""; + private String name; /** * Health check config of this cluster @@ -55,7 +56,6 @@ public class Cluster { */ private boolean useIPPort4Check = true; - private Map metadata = new HashMap(); public Cluster() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Instance.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Instance.java index 129ffd8d9..fcc840ade 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Instance.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Instance.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.api.naming.pojo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.annotation.JSONField; -import com.alibaba.nacos.api.common.Constants; import java.util.HashMap; import java.util.Map; @@ -55,17 +54,17 @@ public class Instance { @JSONField(name = "valid") private boolean healthy = true; + private boolean enabled = true; + /** * cluster information of instance */ - @JSONField(serialize = false) - private Cluster cluster = new Cluster(); + private String clusterName; /** - * service information of instance + * Service information of instance */ - @JSONField(serialize = false) - private Service service; + private String serviceName; /** * user extended attributes @@ -80,14 +79,6 @@ public class Instance { this.instanceId = instanceId; } - public String serviceName() { - String[] infos = instanceId.split(Constants.NAMING_INSTANCE_ID_SPLITTER); - if (infos.length < Constants.NAMING_INSTANCE_ID_SEG_COUNT) { - return null; - } - return infos[Constants.NAMING_INSTANCE_ID_SEG_COUNT - 1]; - } - public String getIp() { return ip; } @@ -120,20 +111,20 @@ public class Instance { this.healthy = healthy; } - public Cluster getCluster() { - return cluster; + public String getClusterName() { + return clusterName; } - public void setCluster(Cluster cluster) { - this.cluster = cluster; + public void setClusterName(String clusterName) { + this.clusterName = clusterName; } - public Service getService() { - return service; + public String getServiceName() { + return serviceName; } - public void setService(Service service) { - this.service = service; + public void setServiceName(String serviceName) { + this.serviceName = serviceName; } public Map getMetadata() { @@ -148,6 +139,14 @@ public class Instance { this.metadata.put(key, value); } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @Override public String toString() { return JSON.toJSONString(this); @@ -163,7 +162,7 @@ public class Instance { return false; } - Instance host = (Instance) obj; + Instance host = (Instance)obj; return strEquals(toString(), host.toString()); } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java index 630a038ab..098270d8c 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java @@ -15,6 +15,8 @@ */ package com.alibaba.nacos.api.naming.pojo; +import com.alibaba.nacos.api.selector.AbstractSelector; + import java.util.HashMap; import java.util.Map; @@ -41,21 +43,26 @@ public class Service { private String app; /** - * service group which is meant to classify services into different sets. + * Service group which is meant to classify services into different sets. */ private String group; /** - * health check mode. + * Health check mode. */ private String healthCheckMode; + /** + * Selector name of this service + */ + private AbstractSelector selector; + + private Map metadata = new HashMap(); + public Service(String name) { this.name = name; } - private Map metadata = new HashMap(); - public String getName() { return name; } @@ -107,4 +114,12 @@ public class Service { public void addMetadata(String key, String value) { this.metadata.put(key, value); } + + public AbstractSelector getSelector() { + return selector; + } + + public void setSelector(AbstractSelector selector) { + this.selector = selector; + } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/ServiceInfo.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/ServiceInfo.java index 6d60c0aae..7b0e369ea 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/ServiceInfo.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/ServiceInfo.java @@ -17,6 +17,8 @@ package com.alibaba.nacos.api.naming.pojo; import com.alibaba.fastjson.annotation.JSONField; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -193,6 +195,15 @@ public class ServiceInfo { return getKey(name, clusters, env, isAllIPs()); } + @JSONField(serialize = false) + public String getKeyEncoded() { + try { + return getKey(URLEncoder.encode(name, "UTF-8"), clusters, env, isAllIPs()); + } catch (UnsupportedEncodingException e) { + return getKey(); + } + } + @JSONField(serialize = false) public static String getKey(String name, String clusters, String unit) { return getKey(name, clusters, unit, false); @@ -207,7 +218,7 @@ public class ServiceInfo { if (!isEmpty(clusters) && !isEmpty(unit)) { return isAllIPs ? name + SPLITER + clusters + SPLITER + unit + SPLITER + ALL_IPS - : name + SPLITER + clusters + SPLITER + unit; + : name + SPLITER + clusters + SPLITER + unit; } if (!isEmpty(clusters)) { @@ -216,7 +227,7 @@ public class ServiceInfo { if (!isEmpty(unit)) { return isAllIPs ? name + SPLITER + EMPTY + SPLITER + unit + SPLITER + ALL_IPS : - name + SPLITER + EMPTY + SPLITER + unit; + name + SPLITER + EMPTY + SPLITER + unit; } return isAllIPs ? name + SPLITER + ALL_IPS : name; diff --git a/api/src/main/java/com/alibaba/nacos/api/selector/AbstractSelector.java b/api/src/main/java/com/alibaba/nacos/api/selector/AbstractSelector.java new file mode 100644 index 000000000..650608c27 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/selector/AbstractSelector.java @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.selector; + +/** + * Abstract selector that only contains a type + * + * @author nkorange + */ +public abstract class AbstractSelector { + + /** + * The type of this selector, each child class should announce its own unique type. + */ + private String type; + + public String getType() { + return type; + } + + protected void setType(String type) { + this.type = type; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/selector/ExpressionSelector.java b/api/src/main/java/com/alibaba/nacos/api/selector/ExpressionSelector.java new file mode 100644 index 000000000..35b8f0d43 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/selector/ExpressionSelector.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.selector; + +/** + * The selector to filter resource with flexible expression. + * + * @author nkorange + */ +public class ExpressionSelector extends AbstractSelector { + + /** + * Label expression of this selector. + */ + private String expression; + + public ExpressionSelector() { + this.setType(SelectorType.label.name()); + } + + public String getExpression() { + return expression; + } + + public void setExpression(String expression) { + this.expression = expression; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/selector/SelectorType.java b/api/src/main/java/com/alibaba/nacos/api/selector/SelectorType.java new file mode 100644 index 000000000..9ec9ab25f --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/selector/SelectorType.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.api.selector; + +/** + * The types of selector accepted by Nacos + * + * @author nkorange + */ +public enum SelectorType { + /** + * not match any type + */ + unknown, + /** + * not filter out any entity + */ + none, + /** + * select by label + */ + label +} diff --git a/api/src/test/java/com/alibaba/nacos/api/annotation/NacosPropertiesTest.java b/api/src/test/java/com/alibaba/nacos/api/annotation/NacosPropertiesTest.java index 1bc18fc55..53ed76539 100644 --- a/api/src/test/java/com/alibaba/nacos/api/annotation/NacosPropertiesTest.java +++ b/api/src/test/java/com/alibaba/nacos/api/annotation/NacosPropertiesTest.java @@ -1,12 +1,11 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Copyright 1999-2018 Alibaba Group Holding Ltd. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, diff --git a/client/pom.xml b/client/pom.xml index dcf7bcc87..13e8fa403 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -12,113 +12,100 @@ limitations under the License. --> - - com.alibaba.nacos - nacos-all - 0.2.1 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + com.alibaba.nacos + nacos-all + 0.7.0 + ../pom.xml + - 4.0.0 + 4.0.0 - nacos-client - jar + nacos-client + jar - nacos-client ${project.version} - http://maven.apache.org + nacos-client ${project.version} + http://maven.apache.org - - UTF-8 - + + UTF-8 + - - - org.slf4j - slf4j-api - provided - - - log4j - log4j - provided - - - org.apache.logging.log4j - log4j-core - 2.8.2 - provided - - - commons-logging - commons-logging - provided - - - org.slf4j - slf4j-log4j12 - provided - - - org.apache.logging.log4j - log4j-slf4j-impl - provided - - - org.slf4j - jcl-over-slf4j - provided - - - junit - junit - test - - - ${project.groupId} - nacos-common - - - ${project.groupId} - nacos-api - - - com.alibaba - fastjson - + - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - + + org.slf4j + slf4j-api + true + - - com.google.guava - guava - + + org.apache.logging.log4j + log4j-core + 2.8.2 + true + - - commons-codec - commons-codec - + + org.apache.logging.log4j + log4j-slf4j-impl + true + - - org.codehaus.jackson - jackson-mapper-lgpl - - - net.jcip - jcip-annotations - true - - - com.github.spotbugs - spotbugs-annotations - true - - + + ${project.groupId} + nacos-common + + + ${project.groupId} + nacos-api + + + + ch.qos.logback + logback-classic + true + + + + ch.qos.logback + logback-core + true + + + + com.google.guava + guava + + + + commons-codec + commons-codec + + + + org.codehaus.jackson + jackson-mapper-lgpl + + + net.jcip + jcip-annotations + true + + + + com.github.spotbugs + spotbugs-annotations + true + + + + junit + junit + test + + + diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 8f8b0c51d..3b7680422 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -44,233 +44,233 @@ import java.util.Properties; /** * Config Impl - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") public class NacosConfigService implements ConfigService { - final static public Logger log = LogUtils.logger(NacosConfigService.class); - public final long POST_TIMEOUT = 3000L; - /** - * http agent - */ - private ServerHttpAgent agent; - /** - * longpulling - */ - private ClientWorker worker; - private String namespace; - private String encode; - private ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(); + final static public Logger log = LogUtils.logger(NacosConfigService.class); + public final long POST_TIMEOUT = 3000L; + /** + * http agent + */ + private ServerHttpAgent agent; + /** + * longpulling + */ + private ClientWorker worker; + private String namespace; + private String encode; + private ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(); - public NacosConfigService(Properties properties) throws NacosException { - String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE); - if (StringUtils.isBlank(encodeTmp)) { - encode = Constants.ENCODE; - } else { - encode = encodeTmp.trim(); - } - String namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE); - if (StringUtils.isBlank(namespaceTmp)) { - namespace = TenantUtil.getUserTenant(); - properties.put(PropertyKeyConst.NAMESPACE, namespace); - } else { - namespace = namespaceTmp; - properties.put(PropertyKeyConst.NAMESPACE, namespace); - } - agent = new ServerHttpAgent(properties); - agent.start(); - worker = new ClientWorker(agent, configFilterChainManager); - } + public NacosConfigService(Properties properties) throws NacosException { + String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE); + if (StringUtils.isBlank(encodeTmp)) { + encode = Constants.ENCODE; + } else { + encode = encodeTmp.trim(); + } + String namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE); + if (StringUtils.isBlank(namespaceTmp)) { + namespace = TenantUtil.getUserTenant(); + properties.put(PropertyKeyConst.NAMESPACE, namespace); + } else { + namespace = namespaceTmp; + properties.put(PropertyKeyConst.NAMESPACE, namespace); + } + agent = new ServerHttpAgent(properties); + agent.start(); + worker = new ClientWorker(agent, configFilterChainManager); + } - @Override - public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { - return getConfigInner(namespace, dataId, group, timeoutMs); - } + @Override + public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { + return getConfigInner(namespace, dataId, group, timeoutMs); + } - @Override - public void addListener(String dataId, String group, Listener listener) throws NacosException { - worker.addTenantListeners(dataId, group, Arrays.asList(listener)); - } + @Override + public void addListener(String dataId, String group, Listener listener) throws NacosException { + worker.addTenantListeners(dataId, group, Arrays.asList(listener)); + } - @Override - public boolean publishConfig(String dataId, String group, String content) throws NacosException { - return publishConfigInner(namespace, dataId, group, null, null, null, content); - } + @Override + public boolean publishConfig(String dataId, String group, String content) throws NacosException { + return publishConfigInner(namespace, dataId, group, null, null, null, content); + } - @Override - public boolean removeConfig(String dataId, String group) throws NacosException { - return removeConfigInner(namespace, dataId, group, null); - } + @Override + public boolean removeConfig(String dataId, String group) throws NacosException { + return removeConfigInner(namespace, dataId, group, null); + } - @Override - public void removeListener(String dataId, String group, Listener listener) { - worker.removeTenantListener(dataId, group, listener); - } + @Override + public void removeListener(String dataId, String group, Listener listener) { + worker.removeTenantListener(dataId, group, listener); + } - private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { - group = null2defaultGroup(group); - ParamUtils.checkKeyParam(dataId, group); - ConfigResponse cr = new ConfigResponse(); + private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { + group = null2defaultGroup(group); + ParamUtils.checkKeyParam(dataId, group); + ConfigResponse cr = new ConfigResponse(); - cr.setDataId(dataId); - cr.setTenant(tenant); - cr.setGroup(group); + cr.setDataId(dataId); + cr.setTenant(tenant); + cr.setGroup(group); - // 优先使用本地配置 - String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); - if (content != null) { - log.warn(agent.getName(), "[get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", dataId, - group, tenant, ContentUtils.truncateContent(content)); - cr.setContent(content); - configFilterChainManager.doFilter(null, cr); - content = cr.getContent(); - return content; - } + // 优先使用本地配置 + String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); + if (content != null) { + log.warn(agent.getName(), "[get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", dataId, + group, tenant, ContentUtils.truncateContent(content)); + cr.setContent(content); + configFilterChainManager.doFilter(null, cr); + content = cr.getContent(); + return content; + } - try { - content = worker.getServerConfig(dataId, group, tenant, timeoutMs); - cr.setContent(content); - configFilterChainManager.doFilter(null, cr); - content = cr.getContent(); - return content; - } catch (NacosException ioe) { - if (NacosException.NO_RIGHT == ioe.getErrCode()) { - throw ioe; - } - log.warn("NACOS-0003", - LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0003", "环境问题", "get from server error")); - log.warn(agent.getName(), "[get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", - dataId, group, tenant, ioe.toString()); - } + try { + content = worker.getServerConfig(dataId, group, tenant, timeoutMs); + cr.setContent(content); + configFilterChainManager.doFilter(null, cr); + content = cr.getContent(); + return content; + } catch (NacosException ioe) { + if (NacosException.NO_RIGHT == ioe.getErrCode()) { + throw ioe; + } + log.warn("NACOS-0003", + LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0003", "环境问题", "get from server error")); + log.warn(agent.getName(), "[get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", + dataId, group, tenant, ioe.toString()); + } - log.warn(agent.getName(), "[get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", dataId, - group, tenant, ContentUtils.truncateContent(content)); - content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant); - cr.setContent(content); - configFilterChainManager.doFilter(null, cr); - content = cr.getContent(); - return content; - } + log.warn(agent.getName(), "[get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", dataId, + group, tenant, ContentUtils.truncateContent(content)); + content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant); + cr.setContent(content); + configFilterChainManager.doFilter(null, cr); + content = cr.getContent(); + return content; + } - private String null2defaultGroup(String group) { - return (null == group) ? Constants.DEFAULT_GROUP : group.trim(); - } + private String null2defaultGroup(String group) { + return (null == group) ? Constants.DEFAULT_GROUP : group.trim(); + } - private boolean removeConfigInner(String tenant, String dataId, String group, String tag) throws NacosException { - group = null2defaultGroup(group); - ParamUtils.checkKeyParam(dataId, group); - String url = Constants.CONFIG_CONTROLLER_PATH; - List params = new ArrayList(); - params.add("dataId"); - params.add(dataId); - params.add("group"); - params.add(group); - if (StringUtils.isNotEmpty(tenant)) { - params.add("tenant"); - params.add(tenant); - } - if (StringUtils.isNotEmpty(tag)) { - params.add("tag"); - params.add(tag); - } - HttpResult result = null; - try { - result = agent.httpDelete(url, null, params, encode, POST_TIMEOUT); - } catch (IOException ioe) { - log.warn("[remove] error, " + dataId + ", " + group + ", " + tenant + ", msg: " + ioe.toString()); - return false; - } + private boolean removeConfigInner(String tenant, String dataId, String group, String tag) throws NacosException { + group = null2defaultGroup(group); + ParamUtils.checkKeyParam(dataId, group); + String url = Constants.CONFIG_CONTROLLER_PATH; + List params = new ArrayList(); + params.add("dataId"); + params.add(dataId); + params.add("group"); + params.add(group); + if (StringUtils.isNotEmpty(tenant)) { + params.add("tenant"); + params.add(tenant); + } + if (StringUtils.isNotEmpty(tag)) { + params.add("tag"); + params.add(tag); + } + HttpResult result = null; + try { + result = agent.httpDelete(url, null, params, encode, POST_TIMEOUT); + } catch (IOException ioe) { + log.warn("[remove] error, " + dataId + ", " + group + ", " + tenant + ", msg: " + ioe.toString()); + return false; + } - if (HttpURLConnection.HTTP_OK == result.code) { - log.info(agent.getName(), "[remove] ok, dataId={}, group={}, tenant={}", dataId, group, tenant); - return true; - } else if (HttpURLConnection.HTTP_FORBIDDEN == result.code) { - log.warn(agent.getName(), "[remove] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, group, - tenant, result.code, result.content); - throw new NacosException(result.code, result.content); - } else { - log.warn(agent.getName(), "[remove] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, group, - tenant, result.code, result.content); - return false; - } - } + if (HttpURLConnection.HTTP_OK == result.code) { + log.info(agent.getName(), "[remove] ok, dataId={}, group={}, tenant={}", dataId, group, tenant); + return true; + } else if (HttpURLConnection.HTTP_FORBIDDEN == result.code) { + log.warn(agent.getName(), "[remove] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, group, + tenant, result.code, result.content); + throw new NacosException(result.code, result.content); + } else { + log.warn(agent.getName(), "[remove] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, group, + tenant, result.code, result.content); + return false; + } + } - private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName, - String betaIps, String content) throws NacosException { - group = null2defaultGroup(group); - ParamUtils.checkParam(dataId, group, content); + private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName, + String betaIps, String content) throws NacosException { + group = null2defaultGroup(group); + ParamUtils.checkParam(dataId, group, content); - ConfigRequest cr = new ConfigRequest(); - cr.setDataId(dataId); - cr.setTenant(tenant); - cr.setGroup(group); - cr.setContent(content); - configFilterChainManager.doFilter(cr, null); - content = cr.getContent(); + ConfigRequest cr = new ConfigRequest(); + cr.setDataId(dataId); + cr.setTenant(tenant); + cr.setGroup(group); + cr.setContent(content); + configFilterChainManager.doFilter(cr, null); + content = cr.getContent(); - String url = Constants.CONFIG_CONTROLLER_PATH; - List params = new ArrayList(); - params.add("dataId"); - params.add(dataId); - params.add("group"); - params.add(group); - params.add("content"); - params.add(content); - if (StringUtils.isNotEmpty(tenant)) { - params.add("tenant"); - params.add(tenant); - } - if (StringUtils.isNotEmpty(appName)) { - params.add("appName"); - params.add(appName); - } - if (StringUtils.isNotEmpty(tag)) { - params.add("tag"); - params.add(tag); - } + String url = Constants.CONFIG_CONTROLLER_PATH; + List params = new ArrayList(); + params.add("dataId"); + params.add(dataId); + params.add("group"); + params.add(group); + params.add("content"); + params.add(content); + if (StringUtils.isNotEmpty(tenant)) { + params.add("tenant"); + params.add(tenant); + } + if (StringUtils.isNotEmpty(appName)) { + params.add("appName"); + params.add(appName); + } + if (StringUtils.isNotEmpty(tag)) { + params.add("tag"); + params.add(tag); + } - List headers = new ArrayList(); - if (StringUtils.isNotEmpty(betaIps)) { - headers.add("betaIps"); - headers.add(betaIps); - } + List headers = new ArrayList(); + if (StringUtils.isNotEmpty(betaIps)) { + headers.add("betaIps"); + headers.add(betaIps); + } - HttpResult result = null; - try { - result = agent.httpPost(url, headers, params, encode, POST_TIMEOUT); - } catch (IOException ioe) { - log.warn("NACOS-0006", - LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0006", "环境问题", "[publish-single] exception")); - log.warn(agent.getName(), "[publish-single] exception, dataId={}, group={}, msg={}", dataId, group, - ioe.toString()); - return false; - } + HttpResult result = null; + try { + result = agent.httpPost(url, headers, params, encode, POST_TIMEOUT); + } catch (IOException ioe) { + log.warn("NACOS-0006", + LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0006", "环境问题", "[publish-single] exception")); + log.warn(agent.getName(), "[publish-single] exception, dataId={}, group={}, msg={}", dataId, group, + ioe.toString()); + return false; + } - if (HttpURLConnection.HTTP_OK == result.code) { - log.info(agent.getName(), "[publish-single] ok, dataId={}, group={}, tenant={}, config={}", dataId, group, - tenant, ContentUtils.truncateContent(content)); - return true; - } else if (HttpURLConnection.HTTP_FORBIDDEN == result.code) { - log.warn(agent.getName(), "[publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, - group, tenant, result.code, result.content); - throw new NacosException(result.code, result.content); - } else { - log.warn(agent.getName(), "[publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, - group, tenant, result.code, result.content); - return false; - } + if (HttpURLConnection.HTTP_OK == result.code) { + log.info(agent.getName(), "[publish-single] ok, dataId={}, group={}, tenant={}, config={}", dataId, group, + tenant, ContentUtils.truncateContent(content)); + return true; + } else if (HttpURLConnection.HTTP_FORBIDDEN == result.code) { + log.warn(agent.getName(), "[publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, + group, tenant, result.code, result.content); + throw new NacosException(result.code, result.content); + } else { + log.warn(agent.getName(), "[publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}", dataId, + group, tenant, result.code, result.content); + return false; + } - } + } - @Override - public String getServerStatus() { - if (worker.isHealthServer()) { - return "UP"; - } else { - return "DOWN"; - } - } + @Override + public String getServerStatus() { + if (worker.isHealthServer()) { + return "UP"; + } else { + return "DOWN"; + } + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/common/GroupKey.java b/client/src/main/java/com/alibaba/nacos/client/config/common/GroupKey.java index fce61eddc..5b2465162 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/common/GroupKey.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/common/GroupKey.java @@ -18,9 +18,8 @@ package com.alibaba.nacos.client.config.common; import com.alibaba.nacos.client.utils.StringUtils; /** - * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId - * and groupId. - * + * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId and groupId. + * * @author Nacos */ public class GroupKey { @@ -33,28 +32,28 @@ public class GroupKey { return sb.toString(); } - static public String getKeyTenant(String dataId, String group, String tenant) { - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append('+'); - urlEncode(group, sb); - if (StringUtils.isNotEmpty(tenant)) { - sb.append('+'); - urlEncode(tenant, sb); - } - return sb.toString(); - } - - static public String getKey(String dataId, String group, String datumStr) { - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append('+'); - urlEncode(group, sb); - sb.append('+'); - urlEncode(datumStr, sb); - return sb.toString(); - } - + static public String getKeyTenant(String dataId, String group, String tenant) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + if (StringUtils.isNotEmpty(tenant)) { + sb.append('+'); + urlEncode(tenant, sb); + } + return sb.toString(); + } + + static public String getKey(String dataId, String group, String datumStr) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + sb.append('+'); + urlEncode(datumStr, sb); + return sb.toString(); + } + static public String[] parseKey(String groupKey) { StringBuilder sb = new StringBuilder(); String dataId = null; @@ -64,15 +63,15 @@ public class GroupKey { for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if ('+' == c) { - if (null == dataId) { - dataId = sb.toString(); - sb.setLength(0); - } else if (null == group) { - group = sb.toString(); - sb.setLength(0); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } + if (null == dataId) { + dataId = sb.toString(); + sb.setLength(0); + } else if (null == group) { + group = sb.toString(); + sb.setLength(0); + } else { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } } else if ('%' == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); @@ -87,25 +86,24 @@ public class GroupKey { sb.append(c); } } - - if (StringUtils.isBlank(group)) { - group = sb.toString(); - if (group.length() == 0) { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else { - tenant = sb.toString(); - if (group.length() == 0) { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } - - return new String[] { dataId, group, tenant }; + + if (StringUtils.isBlank(group)) { + group = sb.toString(); + if (group.length() == 0) { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } else { + tenant = sb.toString(); + if (group.length() == 0) { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } + + return new String[] {dataId, group, tenant}; } - + /** - * + -> %2B - * % -> %25 + * + -> %2B % -> %25 */ static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { diff --git a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigContext.java b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigContext.java index 23c65f6ec..834a6d5dc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigContext.java @@ -22,22 +22,21 @@ import com.alibaba.nacos.api.config.filter.IConfigContext; /** * Config Context - * - * @author Nacos * + * @author Nacos */ public class ConfigContext implements IConfigContext { - private Map param = new HashMap(); + private Map param = new HashMap(); - @Override - public Object getParameter(String key) { - return param.get(key); - } + @Override + public Object getParameter(String key) { + return param.get(key); + } - @Override - public void setParameter(String key, Object value) { - param.put(key, value); - } + @Override + public void setParameter(String key, Object value) { + param.put(key, value); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java index 1566113ed..f06fde03f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java @@ -26,62 +26,60 @@ import com.google.common.collect.Lists; /** * Config Filter Chain Management - * - * @author Nacos * + * @author Nacos */ public class ConfigFilterChainManager implements IConfigFilterChain { - private List filters = Lists.newArrayList(); + private List filters = Lists.newArrayList(); - public synchronized ConfigFilterChainManager addFilter(IConfigFilter filter) { - // 根据order大小顺序插入 - int i = 0; - while (i < this.filters.size()) { - IConfigFilter currentValue = this.filters.get(i); - if (currentValue.getFilterName().equals(filter.getFilterName())) { - break; - } - if (filter.getOrder() >= currentValue.getOrder() && i < this.filters.size()) { - i++; - } else { - this.filters.add(i, filter); - break; - } - } + public synchronized ConfigFilterChainManager addFilter(IConfigFilter filter) { + // 根据order大小顺序插入 + int i = 0; + while (i < this.filters.size()) { + IConfigFilter currentValue = this.filters.get(i); + if (currentValue.getFilterName().equals(filter.getFilterName())) { + break; + } + if (filter.getOrder() >= currentValue.getOrder() && i < this.filters.size()) { + i++; + } else { + this.filters.add(i, filter); + break; + } + } - if (i == this.filters.size()) { - this.filters.add(i, filter); - } - return this; - } - + if (i == this.filters.size()) { + this.filters.add(i, filter); + } + return this; + } - @Override - public void doFilter(IConfigRequest request, IConfigResponse response) throws NacosException { - new VirtualFilterChain(this.filters).doFilter(request, response); - } + @Override + public void doFilter(IConfigRequest request, IConfigResponse response) throws NacosException { + new VirtualFilterChain(this.filters).doFilter(request, response); + } - private static class VirtualFilterChain implements IConfigFilterChain { + private static class VirtualFilterChain implements IConfigFilterChain { - private final List additionalFilters; + private final List additionalFilters; - private int currentPosition = 0; + private int currentPosition = 0; - public VirtualFilterChain(List additionalFilters) { - this.additionalFilters = additionalFilters; - } + public VirtualFilterChain(List additionalFilters) { + this.additionalFilters = additionalFilters; + } - @Override - public void doFilter(final IConfigRequest request, final IConfigResponse response) throws NacosException { - if (this.currentPosition == this.additionalFilters.size()) { - return; - } else { - this.currentPosition++; - IConfigFilter nextFilter = this.additionalFilters.get(this.currentPosition - 1); - nextFilter.doFilter(request, response, this); - } - } - } + @Override + public void doFilter(final IConfigRequest request, final IConfigResponse response) throws NacosException { + if (this.currentPosition == this.additionalFilters.size()) { + return; + } else { + this.currentPosition++; + IConfigFilter nextFilter = this.additionalFilters.get(this.currentPosition - 1); + nextFilter.doFilter(request, response, this); + } + } + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigRequest.java b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigRequest.java index 100b10d39..76294724f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigRequest.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigRequest.java @@ -23,56 +23,55 @@ import com.alibaba.nacos.api.config.filter.IConfigRequest; /** * Config Request - * - * @author Nacos * + * @author Nacos */ public class ConfigRequest implements IConfigRequest { - private Map param = new HashMap(); + private Map param = new HashMap(); - private IConfigContext configContext = new ConfigContext(); + private IConfigContext configContext = new ConfigContext(); - public String getTenant() { - return (String) param.get("tenant"); - } + public String getTenant() { + return (String)param.get("tenant"); + } - public void setTenant(String tenant) { - param.put("tenant", tenant); - } + public void setTenant(String tenant) { + param.put("tenant", tenant); + } - public String getDataId() { - return (String) param.get("dataId"); - } + public String getDataId() { + return (String)param.get("dataId"); + } - public void setDataId(String dataId) { - param.put("dataId", dataId); - } + public void setDataId(String dataId) { + param.put("dataId", dataId); + } - public String getGroup() { - return (String) param.get("group"); - } + public String getGroup() { + return (String)param.get("group"); + } - public void setGroup(String group) { - param.put("group", group); - } + public void setGroup(String group) { + param.put("group", group); + } - public String getContent() { - return (String) param.get("content"); - } + public String getContent() { + return (String)param.get("content"); + } - public void setContent(String content) { - param.put("content", content); - } + public void setContent(String content) { + param.put("content", content); + } - @Override - public Object getParameter(String key) { - return param.get(key); - } + @Override + public Object getParameter(String key) { + return param.get(key); + } - @Override - public IConfigContext getConfigContext() { - return configContext; - } + @Override + public IConfigContext getConfigContext() { + return configContext; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigResponse.java b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigResponse.java index c864331cc..c009fc289 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigResponse.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigResponse.java @@ -23,56 +23,55 @@ import com.alibaba.nacos.api.config.filter.IConfigResponse; /** * Config Response - * - * @author Nacos * + * @author Nacos */ public class ConfigResponse implements IConfigResponse { - private Map param = new HashMap(); + private Map param = new HashMap(); - private IConfigContext configContext = new ConfigContext(); + private IConfigContext configContext = new ConfigContext(); - public String getTenant() { - return (String) param.get("tenant"); - } + public String getTenant() { + return (String)param.get("tenant"); + } - public void setTenant(String tenant) { - param.put("tenant", tenant); - } + public void setTenant(String tenant) { + param.put("tenant", tenant); + } - public String getDataId() { - return (String) param.get("dataId"); - } + public String getDataId() { + return (String)param.get("dataId"); + } - public void setDataId(String dataId) { - param.put("dataId", dataId); - } + public void setDataId(String dataId) { + param.put("dataId", dataId); + } - public String getGroup() { - return (String) param.get("group"); - } + public String getGroup() { + return (String)param.get("group"); + } - public void setGroup(String group) { - param.put("group", group); - } + public void setGroup(String group) { + param.put("group", group); + } - public String getContent() { - return (String) param.get("content"); - } + public String getContent() { + return (String)param.get("content"); + } - public void setContent(String content) { - param.put("content", content); - } + public void setContent(String content) { + param.put("content", content); + } - @Override - public Object getParameter(String key) { - return param.get(key); - } + @Override + public Object getParameter(String key) { + return param.get(key); + } - @Override - public IConfigContext getConfigContext() { - return configContext; - } + @Override + public IConfigContext getConfigContext() { + return configContext; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java index 9ca7822e9..0b3dfa147 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java @@ -32,56 +32,54 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * Listner Management - * - * @author Nacos * + * @author Nacos */ public class CacheData { - final static public Logger log = LogUtils.logger(CacheData.class); - - public boolean isInitializing() { - return isInitializing; - } + final static public Logger log = LogUtils.logger(CacheData.class); - public void setInitializing(boolean isInitializing) { - this.isInitializing = isInitializing; - } + public boolean isInitializing() { + return isInitializing; + } - public String getMd5() { + public void setInitializing(boolean isInitializing) { + this.isInitializing = isInitializing; + } + + public String getMd5() { return md5; } - - public String getTenant() { - return tenant; - } + + public String getTenant() { + return tenant; + } public String getContent() { return content; } - + public void setContent(String newContent) { this.content = newContent; this.md5 = getMd5String(content); } - - /** - * Add listener - * - * @param listener - * listener - */ - public void addListener(Listener listener) { - if (null == listener) { - throw new IllegalArgumentException("listener is null"); - } - ManagerListenerWrap wrap = new ManagerListenerWrap(listener); - if (listeners.addIfAbsent(wrap)) { - log.info(name, "[add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", tenant, dataId, group, - listeners.size()); - } - } - + + /** + * Add listener + * + * @param listener listener + */ + public void addListener(Listener listener) { + if (null == listener) { + throw new IllegalArgumentException("listener is null"); + } + ManagerListenerWrap wrap = new ManagerListenerWrap(listener); + if (listeners.addIfAbsent(wrap)) { + log.info(name, "[add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", tenant, dataId, group, + listeners.size()); + } + } + public void removeListener(Listener listener) { if (null == listener) { throw new IllegalArgumentException("listener is null"); @@ -91,7 +89,7 @@ public class CacheData { log.info(name, "[remove-listener] ok, dataId={}, group={}, cnt={}", dataId, group, listeners.size()); } } - + /** * 返回监听器列表上的迭代器,只读。保证不返回NULL。 */ @@ -103,32 +101,32 @@ public class CacheData { return result; } - public long getLocalConfigInfoVersion() { return localConfigLastModified; } + public void setLocalConfigInfoVersion(long localConfigLastModified) { this.localConfigLastModified = localConfigLastModified; } - public boolean isUseLocalConfigInfo() { return isUseLocalConfig; } + public void setUseLocalConfigInfo(boolean useLocalConfigInfo) { this.isUseLocalConfig = useLocalConfigInfo; if (!useLocalConfigInfo) { localConfigLastModified = -1; } } - - public int getTaskId() { - return taskId; - } - public void setTaskId(int taskId) { - this.taskId = taskId; - } + public int getTaskId() { + return taskId; + } + + public void setTaskId(int taskId) { + this.taskId = taskId; + } @Override public int hashCode() { @@ -147,7 +145,7 @@ public class CacheData { if (this == obj) { return true; } - CacheData other = (CacheData) obj; + CacheData other = (CacheData)obj; return dataId.equals(other.dataId) && group.equals(other.group); } @@ -155,7 +153,7 @@ public class CacheData { public String toString() { return "CacheData [" + dataId + ", " + group + "]"; } - + void checkListenerMd5() { for (ManagerListenerWrap wrap : listeners) { if (!md5.equals(wrap.lastCallMd5)) { @@ -163,48 +161,46 @@ public class CacheData { } } } - + private void safeNotifyListener(final String dataId, final String group, final String content, - final String md5, final ManagerListenerWrap listenerWrap) { + final String md5, final ManagerListenerWrap listenerWrap) { final Listener listener = listenerWrap.listener; - + Runnable job = new Runnable() { public void run() { - ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader(); - ClassLoader appClassLoader= listener.getClass().getClassLoader(); + ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader appClassLoader = listener.getClass().getClassLoader(); try { - if(listener instanceof AbstractSharedListener){ - AbstractSharedListener adapter = (AbstractSharedListener) listener; + if (listener instanceof AbstractSharedListener) { + AbstractSharedListener adapter = (AbstractSharedListener)listener; adapter.fillContext(dataId, group); log.info(name, "[notify-context] dataId={}, group={}, md5={}", dataId, group, md5); } // 执行回调之前先将线程classloader设置为具体webapp的classloader,以免回调方法中调用spi接口是出现异常或错用(多应用部署才会有该问题)。 Thread.currentThread().setContextClassLoader(appClassLoader); - - ConfigResponse cr = new ConfigResponse(); - cr.setDataId(dataId); - cr.setGroup(group); - cr.setContent(content); - configFilterChainManager.doFilter(null, cr); - String contentTmp = cr.getContent(); - listener.receiveConfigInfo(contentTmp); - listenerWrap.lastCallMd5 = md5; - log.info( - name, - "[notify-ok] dataId={}, group={}, md5={}, listener={} ", - dataId, group, md5, listener); - } catch (NacosException de) { - log.error(name, "NACOS-XXXX", - "[notify-error] dataId={}, group={}, md5={}, listener={} errCode={} errMsg={}", dataId, - group, md5, listener, de.getErrCode(), de.getErrMsg()); - } catch (Throwable t) { - log.error(name, "NACOS-XXXX", - "[notify-error] dataId={}, group={}, md5={}, listener={} tx={}", dataId, group, md5, - listener, t.getCause()); - } - finally - { - Thread.currentThread().setContextClassLoader(myClassLoader); + + ConfigResponse cr = new ConfigResponse(); + cr.setDataId(dataId); + cr.setGroup(group); + cr.setContent(content); + configFilterChainManager.doFilter(null, cr); + String contentTmp = cr.getContent(); + listener.receiveConfigInfo(contentTmp); + listenerWrap.lastCallMd5 = md5; + log.info( + name, + "[notify-ok] dataId={}, group={}, md5={}, listener={} ", + dataId, group, md5, listener); + } catch (NacosException de) { + log.error(name, "NACOS-XXXX", + "[notify-error] dataId={}, group={}, md5={}, listener={} errCode={} errMsg={}", dataId, + group, md5, listener, de.getErrCode(), de.getErrMsg()); + } catch (Throwable t) { + log.error(name, "NACOS-XXXX", + "[notify-error] dataId={}, group={}, md5={}, listener={} tx={}", dataId, group, md5, + listener, t.getCause()); + } finally { + Thread.currentThread().setContextClassLoader(myClassLoader); } } }; @@ -217,57 +213,59 @@ public class CacheData { job.run(); } } catch (Throwable t) { - log.error( - name, - "NACOS-XXXX", - "[notify-error] dataId={}, group={}, md5={}, listener={} throwable={}", - dataId, group, md5, listener, t.getCause()); + log.error( + name, + "NACOS-XXXX", + "[notify-error] dataId={}, group={}, md5={}, listener={} throwable={}", + dataId, group, md5, listener, t.getCause()); } final long finishNotify = System.currentTimeMillis(); - log.info(name, "[notify-listener] time cost={}ms in ClientWorker, dataId={}, group={}, md5={}, listener={} ",(finishNotify - startNotify), dataId ,group, md5, listener); + log.info(name, "[notify-listener] time cost={}ms in ClientWorker, dataId={}, group={}, md5={}, listener={} ", + (finishNotify - startNotify), dataId, group, md5, listener); } - + static public String getMd5String(String config) { return (null == config) ? Constants.NULL : MD5.getInstance().getMD5String(config); } private String loadCacheContentFromDiskLocal(String name, String dataId, String group, String tenant) { - String content = LocalConfigInfoProcessor.getFailover(name, dataId, group, tenant); - content = (null != content) ? content - : LocalConfigInfoProcessor.getSnapshot(name, dataId, group, tenant); - return content; + String content = LocalConfigInfoProcessor.getFailover(name, dataId, group, tenant); + content = (null != content) ? content + : LocalConfigInfoProcessor.getSnapshot(name, dataId, group, tenant); + return content; } - - public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group) { - if (null == dataId || null == group) { - throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group); - } - this.name = name; - this.configFilterChainManager = configFilterChainManager; - this.dataId = dataId; - this.group = group; - this.tenant = TenantUtil.getUserTenant(); - listeners = new CopyOnWriteArrayList(); - this.isInitializing = true; - this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant); - this.md5 = getMd5String(content); + + public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group) { + if (null == dataId || null == group) { + throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group); + } + this.name = name; + this.configFilterChainManager = configFilterChainManager; + this.dataId = dataId; + this.group = group; + this.tenant = TenantUtil.getUserTenant(); + listeners = new CopyOnWriteArrayList(); + this.isInitializing = true; + this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant); + this.md5 = getMd5String(content); } - - public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group, String tenant) { - if (null == dataId || null == group) { - throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group); - } - this.name = name; - this.configFilterChainManager = configFilterChainManager; - this.dataId = dataId; - this.group = group; - this.tenant = tenant; - listeners = new CopyOnWriteArrayList(); - this.isInitializing = true; - this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant); - this.md5 = getMd5String(content); + + public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group, + String tenant) { + if (null == dataId || null == group) { + throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group); + } + this.name = name; + this.configFilterChainManager = configFilterChainManager; + this.dataId = dataId; + this.group = group; + this.tenant = tenant; + listeners = new CopyOnWriteArrayList(); + this.isInitializing = true; + this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant); + this.md5 = getMd5String(content); } - + // ================== private final String name; @@ -277,16 +275,16 @@ public class CacheData { public final String tenant; private final CopyOnWriteArrayList listeners; - private volatile String md5; - /** - * whether use local config - */ - private volatile boolean isUseLocalConfig = false; - /** - * last motify time - */ - private volatile long localConfigLastModified; - private volatile String content; + private volatile String md5; + /** + * whether use local config + */ + private volatile boolean isUseLocalConfig = false; + /** + * last motify time + */ + private volatile long localConfigLastModified; + private volatile String content; private int taskId; private volatile boolean isInitializing = true; } @@ -307,13 +305,13 @@ class ManagerListenerWrap { if (obj == this) { return true; } - ManagerListenerWrap other = (ManagerListenerWrap) obj; + ManagerListenerWrap other = (ManagerListenerWrap)obj; return listener.equals(other.listener); } - @Override - public int hashCode() { - return super.hashCode(); - } + @Override + public int hashCode() { + return super.hashCode(); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java index 201723460..ba86785d4 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java @@ -44,24 +44,23 @@ import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR; /** * Longpulling - * - * @author Nacos * + * @author Nacos */ public class ClientWorker { - - final static public Logger log = LogUtils.logger(ClientWorker.class); - public void addListeners(String dataId, String group, List listeners) { + final static public Logger log = LogUtils.logger(ClientWorker.class); + + public void addListeners(String dataId, String group, List listeners) { group = null2defaultGroup(group); CacheData cache = addCacheDataIfAbsent(dataId, group); for (Listener listener : listeners) { cache.addListener(listener); } - } - - public void removeListener(String dataId, String group, Listener listener) { - group = null2defaultGroup(group); + } + + public void removeListener(String dataId, String group, Listener listener) { + group = null2defaultGroup(group); CacheData cache = getCache(dataId, group); if (null != cache) { cache.removeListener(listener); @@ -69,30 +68,30 @@ public class ClientWorker { removeCache(dataId, group); } } - } - - public void addTenantListeners(String dataId, String group, List listeners) { - group = null2defaultGroup(group); - String tenant = agent.getTenant(); - CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); - for (Listener listener : listeners) { - cache.addListener(listener); - } - } - - public void removeTenantListener(String dataId, String group, Listener listener) { - group = null2defaultGroup(group); - String tenant = agent.getTenant(); - CacheData cache = getCache(dataId, group, tenant); - if (null != cache) { - cache.removeListener(listener); - if (cache.getListeners().isEmpty()) { - removeCache(dataId, group, tenant); - } - } - } - - @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") + } + + public void addTenantListeners(String dataId, String group, List listeners) { + group = null2defaultGroup(group); + String tenant = agent.getTenant(); + CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); + for (Listener listener : listeners) { + cache.addListener(listener); + } + } + + public void removeTenantListener(String dataId, String group, Listener listener) { + group = null2defaultGroup(group); + String tenant = agent.getTenant(); + CacheData cache = getCache(dataId, group, tenant); + if (null != cache) { + cache.removeListener(listener); + if (cache.getListeners().isEmpty()) { + removeCache(dataId, group, tenant); + } + } + } + + @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") void removeCache(String dataId, String group) { String groupKey = GroupKey.getKey(dataId, group); synchronized (cacheMap) { @@ -102,20 +101,20 @@ public class ClientWorker { } log.info(agent.getName(), "[unsubscribe] {}", groupKey); } - - @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") + + @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") void removeCache(String dataId, String group, String tenant) { - String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); - synchronized (cacheMap) { - Map copy = new HashMap(cacheMap.get()); - copy.remove(groupKey); - cacheMap.set(copy); - } - log.info(agent.getName(), "[unsubscribe] {}", groupKey); + String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); + synchronized (cacheMap) { + Map copy = new HashMap(cacheMap.get()); + copy.remove(groupKey); + cacheMap.set(copy); + } + log.info(agent.getName(), "[unsubscribe] {}", groupKey); } - - @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") - public CacheData addCacheDataIfAbsent(String dataId, String group) { + + @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") + public CacheData addCacheDataIfAbsent(String dataId, String group) { CacheData cache = getCache(dataId, group); if (null != cache) { return cache; @@ -123,408 +122,409 @@ public class ClientWorker { String key = GroupKey.getKey(dataId, group); cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group); - + synchronized (cacheMap) { - CacheData cacheFromMap = getCache(dataId, group); - // multiple listeners on the same dataid+group and race condition,so double check again - //other listener thread beat me to set to cacheMap - if(null != cacheFromMap) { - cache = cacheFromMap; - //reset so that server not hang this check - cache.setInitializing(true); - } else { - int taskId = cacheMap.get().size() / (int) ParamUtil.getPerTaskConfigSize(); - cache.setTaskId(taskId); - } - + CacheData cacheFromMap = getCache(dataId, group); + // multiple listeners on the same dataid+group and race condition,so double check again + //other listener thread beat me to set to cacheMap + if (null != cacheFromMap) { + cache = cacheFromMap; + //reset so that server not hang this check + cache.setInitializing(true); + } else { + int taskId = cacheMap.get().size() / (int)ParamUtil.getPerTaskConfigSize(); + cache.setTaskId(taskId); + } + Map copy = new HashMap(cacheMap.get()); copy.put(key, cache); cacheMap.set(copy); } - + log.info(agent.getName(), "[subscribe] {}", key); - + return cache; } - @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") - public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant) { - CacheData cache = getCache(dataId, group, tenant); - if (null != cache) { - return cache; - } - String key = GroupKey.getKeyTenant(dataId, group, tenant); - cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant); - synchronized (cacheMap) { - CacheData cacheFromMap = getCache(dataId, group, tenant); - // multiple listeners on the same dataid+group and race condition,so - // double check again - // other listener thread beat me to set to cacheMap - if (null != cacheFromMap) { - cache = cacheFromMap; - // reset so that server not hang this check - cache.setInitializing(true); - } - Map copy = new HashMap(cacheMap.get()); - copy.put(key, cache); - cacheMap.set(copy); - } - log.info(agent.getName(), "[subscribe] {}", key); - return cache; - } - - public CacheData getCache(String dataId, String group) { - return getCache(dataId, group, TenantUtil.getUserTenant()); - } - - public CacheData getCache(String dataId, String group, String tenant) { - if (null == dataId || null == group) { - throw new IllegalArgumentException(); - } - return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); - } - + @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") + public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant) { + CacheData cache = getCache(dataId, group, tenant); + if (null != cache) { + return cache; + } + String key = GroupKey.getKeyTenant(dataId, group, tenant); + cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant); + synchronized (cacheMap) { + CacheData cacheFromMap = getCache(dataId, group, tenant); + // multiple listeners on the same dataid+group and race condition,so + // double check again + // other listener thread beat me to set to cacheMap + if (null != cacheFromMap) { + cache = cacheFromMap; + // reset so that server not hang this check + cache.setInitializing(true); + } - public String getServerConfig(String dataId, String group, String tenant, long readTimeout) - throws NacosException { - if (StringUtils.isBlank(group)) { - group = Constants.DEFAULT_GROUP; - } + Map copy = new HashMap(cacheMap.get()); + copy.put(key, cache); + cacheMap.set(copy); + } + log.info(agent.getName(), "[subscribe] {}", key); + return cache; + } - HttpResult result = null; - try { - List params = null; - if (StringUtils.isBlank(tenant)) { - params = Arrays.asList("dataId", dataId, "group", group); - } else { - params = Arrays.asList("dataId", dataId, "group", group, "tenant", tenant); - } - result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout); - } catch (IOException e) { - log.error(agent.getName(), "NACOS-XXXX", - "[sub-server] get server config exception, dataId={}, group={}, tenant={}, msg={}", dataId, group, - tenant, e.toString()); - throw new NacosException(NacosException.SERVER_ERROR, e.getMessage()); - } + public CacheData getCache(String dataId, String group) { + return getCache(dataId, group, TenantUtil.getUserTenant()); + } - switch (result.code) { - case HttpURLConnection.HTTP_OK: - LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content); - return result.content; - case HttpURLConnection.HTTP_NOT_FOUND: - LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null); - return null; - case HttpURLConnection.HTTP_CONFLICT: { - log.error(agent.getName(), "NACOS-XXXX", - "[sub-server-error] get server config being modified concurrently, dataId={}, group={}, tenant={}", - dataId, group, tenant); - throw new NacosException(NacosException.CONFLICT, - "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); - } - case HttpURLConnection.HTTP_FORBIDDEN: { - log.error(agent.getName(), "NACOS-XXXX", "[sub-server-error] no right, dataId={}, group={}, tenant={}", - dataId, group, tenant); - throw new NacosException(result.code, result.content); - } - default: { - log.error(agent.getName(), "NACOS-XXXX", "[sub-server-error] dataId={}, group={}, tenant={}, code={}", - dataId, group, tenant, result.code); - throw new NacosException(result.code, - "http error, code=" + result.code + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); - } - } - } + public CacheData getCache(String dataId, String group, String tenant) { + if (null == dataId || null == group) { + throw new IllegalArgumentException(); + } + return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); + } - private void checkLocalConfig(CacheData cacheData) { - final String dataId = cacheData.dataId; - final String group = cacheData.group; - final String tenant = cacheData.tenant; - File path = LocalConfigInfoProcessor.getFailoverFile(agent.getName(), dataId, group, tenant); + public String getServerConfig(String dataId, String group, String tenant, long readTimeout) + throws NacosException { + if (StringUtils.isBlank(group)) { + group = Constants.DEFAULT_GROUP; + } - // 没有 -> 有 - if (!cacheData.isUseLocalConfigInfo() && path.exists()) { - String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); - String md5 = MD5.getInstance().getMD5String(content); - cacheData.setUseLocalConfigInfo(true); - cacheData.setLocalConfigInfoVersion(path.lastModified()); - cacheData.setContent(content); + HttpResult result = null; + try { + List params = null; + if (StringUtils.isBlank(tenant)) { + params = Arrays.asList("dataId", dataId, "group", group); + } else { + params = Arrays.asList("dataId", dataId, "group", group, "tenant", tenant); + } + result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout); + } catch (IOException e) { + log.error(agent.getName(), "NACOS-XXXX", + "[sub-server] get server config exception, dataId={}, group={}, tenant={}, msg={}", dataId, group, + tenant, e.toString()); + throw new NacosException(NacosException.SERVER_ERROR, e.getMessage()); + } - log.warn(agent.getName(), - "[failover-change] failover file created. dataId={}, group={}, tenant={}, md5={}, content={}", - dataId, group, tenant, md5, ContentUtils.truncateContent(content)); - return; - } + switch (result.code) { + case HttpURLConnection.HTTP_OK: + LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content); + return result.content; + case HttpURLConnection.HTTP_NOT_FOUND: + LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null); + return null; + case HttpURLConnection.HTTP_CONFLICT: { + log.error(agent.getName(), "NACOS-XXXX", + "[sub-server-error] get server config being modified concurrently, dataId={}, group={}, tenant={}", + dataId, group, tenant); + throw new NacosException(NacosException.CONFLICT, + "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); + } + case HttpURLConnection.HTTP_FORBIDDEN: { + log.error(agent.getName(), "NACOS-XXXX", "[sub-server-error] no right, dataId={}, group={}, tenant={}", + dataId, group, tenant); + throw new NacosException(result.code, result.content); + } + default: { + log.error(agent.getName(), "NACOS-XXXX", "[sub-server-error] dataId={}, group={}, tenant={}, code={}", + dataId, group, tenant, result.code); + throw new NacosException(result.code, + "http error, code=" + result.code + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); + } + } + } - // 有 -> 没有。不通知业务监听器,从server拿到配置后通知。 - if (cacheData.isUseLocalConfigInfo() && !path.exists()) { - cacheData.setUseLocalConfigInfo(false); - log.warn(agent.getName(), "[failover-change] failover file deleted. dataId={}, group={}, tenant={}", dataId, - group, tenant); - return; - } + private void checkLocalConfig(CacheData cacheData) { + final String dataId = cacheData.dataId; + final String group = cacheData.group; + final String tenant = cacheData.tenant; + File path = LocalConfigInfoProcessor.getFailoverFile(agent.getName(), dataId, group, tenant); + + // 没有 -> 有 + if (!cacheData.isUseLocalConfigInfo() && path.exists()) { + String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); + String md5 = MD5.getInstance().getMD5String(content); + cacheData.setUseLocalConfigInfo(true); + cacheData.setLocalConfigInfoVersion(path.lastModified()); + cacheData.setContent(content); + + log.warn(agent.getName(), + "[failover-change] failover file created. dataId={}, group={}, tenant={}, md5={}, content={}", + dataId, group, tenant, md5, ContentUtils.truncateContent(content)); + return; + } + + // 有 -> 没有。不通知业务监听器,从server拿到配置后通知。 + if (cacheData.isUseLocalConfigInfo() && !path.exists()) { + cacheData.setUseLocalConfigInfo(false); + log.warn(agent.getName(), "[failover-change] failover file deleted. dataId={}, group={}, tenant={}", dataId, + group, tenant); + return; + } + + // 有变更 + if (cacheData.isUseLocalConfigInfo() && path.exists() + && cacheData.getLocalConfigInfoVersion() != path.lastModified()) { + String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); + String md5 = MD5.getInstance().getMD5String(content); + cacheData.setUseLocalConfigInfo(true); + cacheData.setLocalConfigInfoVersion(path.lastModified()); + cacheData.setContent(content); + log.warn(agent.getName(), + "[failover-change] failover file changed. dataId={}, group={}, tenant={}, md5={}, content={}", + dataId, group, tenant, md5, ContentUtils.truncateContent(content)); + return; + } + } - // 有变更 - if (cacheData.isUseLocalConfigInfo() && path.exists() - && cacheData.getLocalConfigInfoVersion() != path.lastModified()) { - String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant); - String md5 = MD5.getInstance().getMD5String(content); - cacheData.setUseLocalConfigInfo(true); - cacheData.setLocalConfigInfoVersion(path.lastModified()); - cacheData.setContent(content); - log.warn(agent.getName(), - "[failover-change] failover file changed. dataId={}, group={}, tenant={}, md5={}, content={}", - dataId, group, tenant, md5, ContentUtils.truncateContent(content)); - return; - } - } - private String null2defaultGroup(String group) { return (null == group) ? Constants.DEFAULT_GROUP : group.trim(); } - - public void checkConfigInfo() { - // 分任务 - int listenerSize = cacheMap.get().size(); - // 向上取整为批数 - int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize()); - if (longingTaskCount > currentLongingTaskCount) { - for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) { - // 要判断任务是否在执行 这块需要好好想想。 任务列表现在是无序的。变化过程可能有问题 - executorService.execute(new LongPullingRunnable(i)); - } - currentLongingTaskCount = longingTaskCount; - } - } - /** - * 从Server获取值变化了的DataID列表。返回的对象里只有dataId和group是有效的。 保证不返回NULL。 - */ - List checkUpdateDataIds(List cacheDatas, List inInitializingCacheList) { - StringBuilder sb = new StringBuilder(); - for (CacheData cacheData : cacheDatas) { - if (!cacheData.isUseLocalConfigInfo()) { - sb.append(cacheData.dataId).append(WORD_SEPARATOR); - sb.append(cacheData.group).append(WORD_SEPARATOR); - if (StringUtils.isBlank(cacheData.tenant)) { - sb.append(cacheData.getMd5()).append(LINE_SEPARATOR); - } else { - sb.append(cacheData.getMd5()).append(WORD_SEPARATOR); - sb.append(cacheData.getTenant()).append(LINE_SEPARATOR); - } - if (cacheData.isInitializing()) { - // cacheData 首次出现在cacheMap中&首次check更新 - inInitializingCacheList - .add(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant)); - } - } - } - boolean isInitializingCacheList = !inInitializingCacheList.isEmpty(); - return checkUpdateConfigStr(sb.toString(), isInitializingCacheList); - } + public void checkConfigInfo() { + // 分任务 + int listenerSize = cacheMap.get().size(); + // 向上取整为批数 + int longingTaskCount = (int)Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize()); + if (longingTaskCount > currentLongingTaskCount) { + for (int i = (int)currentLongingTaskCount; i < longingTaskCount; i++) { + // 要判断任务是否在执行 这块需要好好想想。 任务列表现在是无序的。变化过程可能有问题 + executorService.execute(new LongPullingRunnable(i)); + } + currentLongingTaskCount = longingTaskCount; + } + } - /** - * 从Server获取值变化了的DataID列表。返回的对象里只有dataId和group是有效的。 保证不返回NULL。 - */ - List checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) { + /** + * 从Server获取值变化了的DataID列表。返回的对象里只有dataId和group是有效的。 保证不返回NULL。 + */ + List checkUpdateDataIds(List cacheDatas, List inInitializingCacheList) { + StringBuilder sb = new StringBuilder(); + for (CacheData cacheData : cacheDatas) { + if (!cacheData.isUseLocalConfigInfo()) { + sb.append(cacheData.dataId).append(WORD_SEPARATOR); + sb.append(cacheData.group).append(WORD_SEPARATOR); + if (StringUtils.isBlank(cacheData.tenant)) { + sb.append(cacheData.getMd5()).append(LINE_SEPARATOR); + } else { + sb.append(cacheData.getMd5()).append(WORD_SEPARATOR); + sb.append(cacheData.getTenant()).append(LINE_SEPARATOR); + } + if (cacheData.isInitializing()) { + // cacheData 首次出现在cacheMap中&首次check更新 + inInitializingCacheList + .add(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant)); + } + } + } + boolean isInitializingCacheList = !inInitializingCacheList.isEmpty(); + return checkUpdateConfigStr(sb.toString(), isInitializingCacheList); + } - List params = Arrays.asList(Constants.PROBE_MODIFY_REQUEST, probeUpdateString); - long timeout = TimeUnit.SECONDS.toMillis(30L); + /** + * 从Server获取值变化了的DataID列表。返回的对象里只有dataId和group是有效的。 保证不返回NULL。 + */ + List checkUpdateConfigStr(String probeUpdateString, boolean isInitializingCacheList) { - List headers = new ArrayList(2); - headers.add("Long-Pulling-Timeout"); - headers.add("" + timeout); + List params = Arrays.asList(Constants.PROBE_MODIFY_REQUEST, probeUpdateString); + long timeout = TimeUnit.SECONDS.toMillis(30L); - // told server do not hang me up if new initializing cacheData added in - if (isInitializingCacheList) { - headers.add("Long-Pulling-Timeout-No-Hangup"); - headers.add("true"); - } + List headers = new ArrayList(2); + headers.add("Long-Pulling-Timeout"); + headers.add("" + timeout); - if (StringUtils.isBlank(probeUpdateString)) { - return Collections.emptyList(); - } + // told server do not hang me up if new initializing cacheData added in + if (isInitializingCacheList) { + headers.add("Long-Pulling-Timeout-No-Hangup"); + headers.add("true"); + } - try { - HttpResult result = agent.httpPost(Constants.CONFIG_CONTROLLER_PATH + "/listener", headers, params, - agent.getEncode(), timeout); + if (StringUtils.isBlank(probeUpdateString)) { + return Collections.emptyList(); + } - if (HttpURLConnection.HTTP_OK == result.code) { - setHealthServer(true); - return parseUpdateDataIdResponse(result.content); - } else { - setHealthServer(false); - if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR) { - log.error("NACOS-0007", LoggerHelper.getErrorCodeStr("Nacos", "Nacos-0007", "环境问题", - "[check-update] get changed dataId error")); - } - log.error(agent.getName(), "NACOS-XXXX", "[check-update] get changed dataId error, code={}", - result.code); - } - } catch (IOException e) { - setHealthServer(false); - log.error(agent.getName(), "NACOS-XXXX", "[check-update] get changed dataId exception, msg={}", - e.toString()); - } - return Collections.emptyList(); - } + try { + HttpResult result = agent.httpPost(Constants.CONFIG_CONTROLLER_PATH + "/listener", headers, params, + agent.getEncode(), timeout); - /** - * 从HTTP响应拿到变化的groupKey。保证不返回NULL。 - */ - private List parseUpdateDataIdResponse(String response) { - if (StringUtils.isBlank(response)) { - return Collections.emptyList(); - } + if (HttpURLConnection.HTTP_OK == result.code) { + setHealthServer(true); + return parseUpdateDataIdResponse(result.content); + } else { + setHealthServer(false); + if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR) { + log.error("NACOS-0007", LoggerHelper.getErrorCodeStr("Nacos", "Nacos-0007", "环境问题", + "[check-update] get changed dataId error")); + } + log.error(agent.getName(), "NACOS-XXXX", "[check-update] get changed dataId error, code={}", + result.code); + } + } catch (IOException e) { + setHealthServer(false); + log.error(agent.getName(), "NACOS-XXXX", "[check-update] get changed dataId exception, msg={}", + e.toString()); + } + return Collections.emptyList(); + } - try { - response = URLDecoder.decode(response, "UTF-8"); - } catch (Exception e) { - log.error(agent.getName(), "NACOS-XXXX", "[polling-resp] decode modifiedDataIdsString error", e); - } + /** + * 从HTTP响应拿到变化的groupKey。保证不返回NULL。 + */ + private List parseUpdateDataIdResponse(String response) { + if (StringUtils.isBlank(response)) { + return Collections.emptyList(); + } - List updateList = new LinkedList(); + try { + response = URLDecoder.decode(response, "UTF-8"); + } catch (Exception e) { + log.error(agent.getName(), "NACOS-XXXX", "[polling-resp] decode modifiedDataIdsString error", e); + } - for (String dataIdAndGroup : response.split(LINE_SEPARATOR)) { - if (!StringUtils.isBlank(dataIdAndGroup)) { - String[] keyArr = dataIdAndGroup.split(WORD_SEPARATOR); - String dataId = keyArr[0]; - String group = keyArr[1]; - if (keyArr.length == 2) { - updateList.add(GroupKey.getKey(dataId, group)); - log.info(agent.getName(), "[polling-resp] config changed. dataId={}, group={}", dataId, group); - } else if (keyArr.length == 3) { - String tenant = keyArr[2]; - updateList.add(GroupKey.getKeyTenant(dataId, group, tenant)); - log.info(agent.getName(), "[polling-resp] config changed. dataId={}, group={}, tenant={}", dataId, - group, tenant); - } else { - log.error(agent.getName(), "NACOS-XXXX", "[polling-resp] invalid dataIdAndGroup error", - dataIdAndGroup); - } - } - } - return updateList; - } + List updateList = new LinkedList(); - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ServerHttpAgent agent, final ConfigFilterChainManager configFilterChainManager) { - this.agent = agent; - this.configFilterChainManager = configFilterChainManager; - executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setName("com.alibaba.nacos.client.Worker." + agent.getName()); - t.setDaemon(true); - return t; - } - }); + for (String dataIdAndGroup : response.split(LINE_SEPARATOR)) { + if (!StringUtils.isBlank(dataIdAndGroup)) { + String[] keyArr = dataIdAndGroup.split(WORD_SEPARATOR); + String dataId = keyArr[0]; + String group = keyArr[1]; + if (keyArr.length == 2) { + updateList.add(GroupKey.getKey(dataId, group)); + log.info(agent.getName(), "[polling-resp] config changed. dataId={}, group={}", dataId, group); + } else if (keyArr.length == 3) { + String tenant = keyArr[2]; + updateList.add(GroupKey.getKeyTenant(dataId, group, tenant)); + log.info(agent.getName(), "[polling-resp] config changed. dataId={}, group={}, tenant={}", dataId, + group, tenant); + } else { + log.error(agent.getName(), "NACOS-XXXX", "[polling-resp] invalid dataIdAndGroup error", + dataIdAndGroup); + } + } + } + return updateList; + } - executorService = Executors.newCachedThreadPool(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setName("com.alibaba.nacos.client.Worker.longPulling" + agent.getName()); - t.setDaemon(true); - return t; - } - }); + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public ClientWorker(final ServerHttpAgent agent, final ConfigFilterChainManager configFilterChainManager) { + this.agent = agent; + this.configFilterChainManager = configFilterChainManager; + executor = Executors.newScheduledThreadPool(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Worker." + agent.getName()); + t.setDaemon(true); + return t; + } + }); - executor.scheduleWithFixedDelay(new Runnable() { - public void run() { - try { - checkConfigInfo(); - } catch (Throwable e) { - log.error(agent.getName(), "NACOS-XXXX", "[sub-check] rotate check error", e); - } - } - }, 1L, 10L, TimeUnit.MILLISECONDS); - } + executorService = Executors.newCachedThreadPool(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Worker.longPulling" + agent.getName()); + t.setDaemon(true); + return t; + } + }); - class LongPullingRunnable implements Runnable { - private int taskId; + executor.scheduleWithFixedDelay(new Runnable() { + public void run() { + try { + checkConfigInfo(); + } catch (Throwable e) { + log.error(agent.getName(), "NACOS-XXXX", "[sub-check] rotate check error", e); + } + } + }, 1L, 10L, TimeUnit.MILLISECONDS); + } - public LongPullingRunnable(int taskId) { - this.taskId = taskId; - } + class LongPullingRunnable implements Runnable { + private int taskId; - public void run() { - try { - List cacheDatas = new ArrayList(); - // check failover config - for (CacheData cacheData : cacheMap.get().values()) { - if (cacheData.getTaskId() == taskId) { - cacheDatas.add(cacheData); - try { - checkLocalConfig(cacheData); - if (cacheData.isUseLocalConfigInfo()) { - cacheData.checkListenerMd5(); - } - } catch (Exception e) { - log.error("NACOS-CLIENT", "get local config info error", e); - } - } - } + public LongPullingRunnable(int taskId) { + this.taskId = taskId; + } - List inInitializingCacheList = new ArrayList(); - // check server config - List changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList); + public void run() { + try { + List cacheDatas = new ArrayList(); + // check failover config + for (CacheData cacheData : cacheMap.get().values()) { + if (cacheData.getTaskId() == taskId) { + cacheDatas.add(cacheData); + try { + checkLocalConfig(cacheData); + if (cacheData.isUseLocalConfigInfo()) { + cacheData.checkListenerMd5(); + } + } catch (Exception e) { + log.error("NACOS-CLIENT", "get local config info error", e); + } + } + } - for (String groupKey : changedGroupKeys) { - String[] key = GroupKey.parseKey(groupKey); - String dataId = key[0]; - String group = key[1]; - String tenant = null; - if (key.length == 3) { - tenant = key[2]; - } - try { - String content = getServerConfig(dataId, group, tenant, 3000L); - CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); - cache.setContent(content); - log.info(agent.getName(), "[data-received] dataId={}, group={}, tenant={}, md5={}, content={}", - dataId, group, tenant, cache.getMd5(), ContentUtils.truncateContent(content)); - } catch (NacosException ioe) { - log.error(agent.getName(), "NACOS-XXXX", - "[get-update] get changed config exception. dataId={}, group={}, tenant={}, msg={}", - dataId, group, tenant, ioe.toString()); - } - } - for (CacheData cacheData : cacheDatas) { - if (!cacheData.isInitializing() || inInitializingCacheList - .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) { - cacheData.checkListenerMd5(); - cacheData.setInitializing(false); - } - } - inInitializingCacheList.clear(); - } catch (Throwable e) { - log.error("500", "longPulling error", e); - } finally { - executorService.execute(this); - } - } - } + List inInitializingCacheList = new ArrayList(); + // check server config + List changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList); - // ================= + for (String groupKey : changedGroupKeys) { + String[] key = GroupKey.parseKey(groupKey); + String dataId = key[0]; + String group = key[1]; + String tenant = null; + if (key.length == 3) { + tenant = key[2]; + } + try { + String content = getServerConfig(dataId, group, tenant, 3000L); + CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); + cache.setContent(content); + log.info(agent.getName(), "[data-received] dataId={}, group={}, tenant={}, md5={}, content={}", + dataId, group, tenant, cache.getMd5(), ContentUtils.truncateContent(content)); + } catch (NacosException ioe) { + log.error(agent.getName(), "NACOS-XXXX", + "[get-update] get changed config exception. dataId={}, group={}, tenant={}, msg={}", + dataId, group, tenant, ioe.toString()); + } + } + for (CacheData cacheData : cacheDatas) { + if (!cacheData.isInitializing() || inInitializingCacheList + .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) { + cacheData.checkListenerMd5(); + cacheData.setInitializing(false); + } + } + inInitializingCacheList.clear(); + } catch (Throwable e) { + log.error("500", "longPulling error", e); + } finally { + executorService.execute(this); + } + } + } - public boolean isHealthServer() { - return isHealthServer; - } + // ================= - private void setHealthServer(boolean isHealthServer) { - this.isHealthServer = isHealthServer; - } + public boolean isHealthServer() { + return isHealthServer; + } + + private void setHealthServer(boolean isHealthServer) { + this.isHealthServer = isHealthServer; + } + + final ScheduledExecutorService executor; + final ExecutorService executorService; + /** + * groupKey -> cacheData + */ + AtomicReference> cacheMap = new AtomicReference>( + new HashMap()); + ServerHttpAgent agent; + ConfigFilterChainManager configFilterChainManager; + private boolean isHealthServer = true; + private double currentLongingTaskCount = 0; - final ScheduledExecutorService executor; - final ExecutorService executorService; - /** - * groupKey -> cacheData - */ - AtomicReference> cacheMap = new AtomicReference>(new HashMap()); - ServerHttpAgent agent; - ConfigFilterChainManager configFilterChainManager; - private boolean isHealthServer = true; - private double currentLongingTaskCount = 0; - } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/EventDispatcher.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/EventDispatcher.java index 72412baa5..886f07d7f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/EventDispatcher.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/EventDispatcher.java @@ -24,16 +24,14 @@ import java.util.concurrent.CopyOnWriteArrayList; import com.alibaba.nacos.client.config.utils.LogUtils; import com.alibaba.nacos.client.logger.Logger; - /** * Event subscription and publishing tools. - * - * @author Nacos * + * @author Nacos */ public class EventDispatcher { - - final static public Logger log = LogUtils.logger(EventDispatcher.class); + + final static public Logger log = LogUtils.logger(EventDispatcher.class); /** * 添加事件监听器 @@ -55,7 +53,7 @@ public class EventDispatcher { // 发布该事件暗示的其他事件 for (AbstractEvent implyEvent : abstractEvent.implyEvents()) { try { - // 避免死循环 + // 避免死循环 if (abstractEvent != implyEvent) { fireEvent(implyEvent); } @@ -74,7 +72,7 @@ public class EventDispatcher { } static synchronized CopyOnWriteArrayList getListenerList( - Class eventType) { + Class eventType) { CopyOnWriteArrayList listeners = LISTENER_MAP.get(eventType); if (null == listeners) { listeners = new CopyOnWriteArrayList(); @@ -84,8 +82,9 @@ public class EventDispatcher { } // ======================== - - static final Map, CopyOnWriteArrayList> LISTENER_MAP = new HashMap, CopyOnWriteArrayList>(); + + static final Map, CopyOnWriteArrayList> LISTENER_MAP + = new HashMap, CopyOnWriteArrayList>(); // ======================== @@ -105,29 +104,30 @@ public class EventDispatcher { */ static public abstract class AbstractEventListener { public AbstractEventListener() { - /** - * 自动注册给EventDispatcher - */ - EventDispatcher.addEventListener(this); + /** + * 自动注册给EventDispatcher + */ + EventDispatcher.addEventListener(this); } - - /** - * 感兴趣的事件列表 - * - * @return event list - */ + + /** + * 感兴趣的事件列表 + * + * @return event list + */ abstract public List> interest(); /** * 处理事件 + * * @param abstractEvent event to do */ abstract public void onEvent(AbstractEvent abstractEvent); } - /** - * serverList has changed - */ - static public class ServerlistChangeEvent extends AbstractEvent { - } + /** + * serverList has changed + */ + static public class ServerlistChangeEvent extends AbstractEvent { + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/HttpSimpleClient.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/HttpSimpleClient.java index b11a2f08b..d2f016e2d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/HttpSimpleClient.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/HttpSimpleClient.java @@ -20,7 +20,7 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.utils.IOUtils; import com.alibaba.nacos.client.config.utils.MD5; import com.alibaba.nacos.client.utils.ParamUtil; -import com.alibaba.nacos.common.util.UuidUtil; +import com.alibaba.nacos.common.util.UuidUtils; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -34,95 +34,88 @@ import java.util.Map; /** * Http tool - * + * * @author Nacos * */ public class HttpSimpleClient { - static public HttpResult httpGet(String url, List headers, List paramValues, - String encoding, long readTimeoutMs, boolean isSSL) throws IOException{ - String encodedContent = encodingParams(paramValues, encoding); + String encoding, long readTimeoutMs, boolean isSSL) throws IOException { + String encodedContent = encodingParams(paramValues, encoding); url += (null == encodedContent) ? "" : ("?" + encodedContent); - if (Limiter.isLimit(MD5.getInstance().getMD5String( - new StringBuilder(url).append(encodedContent).toString()))) { - return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD, - "More than client-side current limit threshold"); - } - + if (Limiter.isLimit(MD5.getInstance().getMD5String( + new StringBuilder(url).append(encodedContent).toString()))) { + return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD, + "More than client-side current limit threshold"); + } + HttpURLConnection conn = null; - - try { - conn = (HttpURLConnection) new URL(url).openConnection(); - conn.setRequestMethod("GET"); - conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100); - conn.setReadTimeout((int) readTimeoutMs); - List newHeaders = getHeaders(url, headers, paramValues); - setHeaders(conn, newHeaders, encoding); - conn.connect(); - - int respCode = conn.getResponseCode(); - String resp = null; + try { + conn = (HttpURLConnection)new URL(url).openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100); + conn.setReadTimeout((int)readTimeoutMs); + List newHeaders = getHeaders(url, headers, paramValues); + setHeaders(conn, newHeaders, encoding); - if (HttpURLConnection.HTTP_OK == respCode) { - resp = IOUtils.toString(conn.getInputStream(), encoding); - } else { - resp = IOUtils.toString(conn.getErrorStream(), encoding); - } - return new HttpResult(respCode, conn.getHeaderFields(), resp); - } finally { + conn.connect(); + + int respCode = conn.getResponseCode(); + String resp = null; + + if (HttpURLConnection.HTTP_OK == respCode) { + resp = IOUtils.toString(conn.getInputStream(), encoding); + } else { + resp = IOUtils.toString(conn.getErrorStream(), encoding); + } + return new HttpResult(respCode, conn.getHeaderFields(), resp); + } finally { if (conn != null) { conn.disconnect(); } } } - - /** - * 发送GET请求。 - */ - static public HttpResult httpGet(String url, List headers, List paramValues, String encoding, - long readTimeoutMs) throws IOException { - return httpGet(url, headers, paramValues, encoding, readTimeoutMs, false); - } + /** + * 发送GET请求。 + */ + static public HttpResult httpGet(String url, List headers, List paramValues, String encoding, + long readTimeoutMs) throws IOException { + return httpGet(url, headers, paramValues, encoding, readTimeoutMs, false); + } /** * 发送POST请求。 - * + * * @param url - * @param headers - * 请求Header,可以为null - * @param paramValues - * 参数,可以为null - * @param encoding - * URL编码使用的字符集 - * @param readTimeoutMs - * 响应超时 - * @param isSSL - * 是否https + * @param headers 请求Header,可以为null + * @param paramValues 参数,可以为null + * @param encoding URL编码使用的字符集 + * @param readTimeoutMs 响应超时 + * @param isSSL 是否https * @return * @throws IOException */ static public HttpResult httpPost(String url, List headers, List paramValues, - String encoding, long readTimeoutMs, boolean isSSL) throws IOException { - String encodedContent = encodingParams(paramValues, encoding); - if (Limiter.isLimit(MD5.getInstance().getMD5String( - new StringBuilder(url).append(encodedContent).toString()))) { - return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD, - "More than client-side current limit threshold"); - } + String encoding, long readTimeoutMs, boolean isSSL) throws IOException { + String encodedContent = encodingParams(paramValues, encoding); + if (Limiter.isLimit(MD5.getInstance().getMD5String( + new StringBuilder(url).append(encodedContent).toString()))) { + return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD, + "More than client-side current limit threshold"); + } HttpURLConnection conn = null; try { - conn = (HttpURLConnection) new URL(url).openConnection(); + conn = (HttpURLConnection)new URL(url).openConnection(); conn.setRequestMethod("POST"); conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 3000 ? ParamUtil.getConnectTimeout() : 3000); - conn.setReadTimeout((int) readTimeoutMs); + conn.setReadTimeout((int)readTimeoutMs); conn.setDoOutput(true); conn.setDoInput(true); - List newHeaders = getHeaders(url, headers, paramValues); - setHeaders(conn, newHeaders, encoding); + List newHeaders = getHeaders(url, headers, paramValues); + setHeaders(conn, newHeaders, encoding); conn.getOutputStream().write(encodedContent.getBytes(encoding)); @@ -141,81 +134,75 @@ public class HttpSimpleClient { } } } - - /** - * 发送POST请求。 - * - * @param url - * @param headers - * 请求Header,可以为null - * @param paramValues - * 参数,可以为null - * @param encoding - * URL编码使用的字符集 - * @param readTimeoutMs - * 响应超时 - * @return - * @throws IOException - */ - static public HttpResult httpPost(String url, List headers, List paramValues, String encoding, - long readTimeoutMs) throws IOException { - return httpPost(url, headers, paramValues, encoding, readTimeoutMs, false); - } - + /** + * 发送POST请求。 + * + * @param url + * @param headers 请求Header,可以为null + * @param paramValues 参数,可以为null + * @param encoding URL编码使用的字符集 + * @param readTimeoutMs 响应超时 + * @return + * @throws IOException + */ + static public HttpResult httpPost(String url, List headers, List paramValues, String encoding, + long readTimeoutMs) throws IOException { + return httpPost(url, headers, paramValues, encoding, readTimeoutMs, false); + } + static public HttpResult httpDelete(String url, List headers, List paramValues, - String encoding, long readTimeoutMs, boolean isSSL) throws IOException{ - String encodedContent = encodingParams(paramValues, encoding); + String encoding, long readTimeoutMs, boolean isSSL) throws IOException { + String encodedContent = encodingParams(paramValues, encoding); url += (null == encodedContent) ? "" : ("?" + encodedContent); - if (Limiter.isLimit(MD5.getInstance().getMD5String( - new StringBuilder(url).append(encodedContent).toString()))) { - return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD, - "More than client-side current limit threshold"); - } - + if (Limiter.isLimit(MD5.getInstance().getMD5String( + new StringBuilder(url).append(encodedContent).toString()))) { + return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD, + "More than client-side current limit threshold"); + } + HttpURLConnection conn = null; - - try { - conn = (HttpURLConnection) new URL(url).openConnection(); - conn.setRequestMethod("DELETE"); - conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100); - conn.setReadTimeout((int) readTimeoutMs); - List newHeaders = getHeaders(url, headers, paramValues); - setHeaders(conn, newHeaders, encoding); - conn.connect(); - - int respCode = conn.getResponseCode(); - String resp = null; + try { + conn = (HttpURLConnection)new URL(url).openConnection(); + conn.setRequestMethod("DELETE"); + conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100); + conn.setReadTimeout((int)readTimeoutMs); + List newHeaders = getHeaders(url, headers, paramValues); + setHeaders(conn, newHeaders, encoding); - if (HttpURLConnection.HTTP_OK == respCode) { - resp = IOUtils.toString(conn.getInputStream(), encoding); - } else { - resp = IOUtils.toString(conn.getErrorStream(), encoding); - } - return new HttpResult(respCode, conn.getHeaderFields(), resp); - } finally { + conn.connect(); + + int respCode = conn.getResponseCode(); + String resp = null; + + if (HttpURLConnection.HTTP_OK == respCode) { + resp = IOUtils.toString(conn.getInputStream(), encoding); + } else { + resp = IOUtils.toString(conn.getErrorStream(), encoding); + } + return new HttpResult(respCode, conn.getHeaderFields(), resp); + } finally { if (conn != null) { conn.disconnect(); } } } + static public HttpResult httpDelete(String url, List headers, List paramValues, String encoding, + long readTimeoutMs) throws IOException { + return httpGet(url, headers, paramValues, encoding, readTimeoutMs, false); + } - static public HttpResult httpDelete(String url, List headers, List paramValues, String encoding, - long readTimeoutMs) throws IOException { - return httpGet(url, headers, paramValues, encoding, readTimeoutMs, false); - } - static private void setHeaders(HttpURLConnection conn, List headers, String encoding) { if (null != headers) { - for (Iterator iter = headers.iterator(); iter.hasNext();) { + for (Iterator iter = headers.iterator(); iter.hasNext(); ) { conn.addRequestProperty(iter.next(), iter.next()); } } conn.addRequestProperty("Client-Version", ParamUtil.getClientVersion()); conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + encoding); - + String ts = String.valueOf(System.currentTimeMillis()); String token = MD5.getInstance().getMD5String(ts + ParamUtil.getAppKey()); @@ -224,27 +211,27 @@ public class HttpSimpleClient { conn.addRequestProperty(Constants.CLIENT_REQUEST_TOKEN_HEADER, token); } - private static List getHeaders(String url, List headers, List paramValues) - throws IOException { - List newHeaders = new ArrayList(); - newHeaders.add("exConfigInfo"); - newHeaders.add("true"); - newHeaders.add("RequestId"); - newHeaders.add(UuidUtil.generateUuid()); - if (headers!=null) { - newHeaders.addAll(headers); - } - return newHeaders; - } - + private static List getHeaders(String url, List headers, List paramValues) + throws IOException { + List newHeaders = new ArrayList(); + newHeaders.add("exConfigInfo"); + newHeaders.add("true"); + newHeaders.add("RequestId"); + newHeaders.add(UuidUtils.generateUuid()); + if (headers != null) { + newHeaders.addAll(headers); + } + return newHeaders; + } + static private String encodingParams(List paramValues, String encoding) - throws UnsupportedEncodingException { + throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); if (null == paramValues) { return null; } - - for (Iterator iter = paramValues.iterator(); iter.hasNext();) { + + for (Iterator iter = paramValues.iterator(); iter.hasNext(); ) { sb.append(iter.next()).append("="); sb.append(URLEncoder.encode(iter.next(), encoding)); if (iter.hasNext()) { @@ -253,23 +240,23 @@ public class HttpSimpleClient { } return sb.toString(); } - + static public class HttpResult { final public int code; - final public Map> headers; + final public Map> headers; final public String content; - public HttpResult(int code, String content) { - this.code = code; - this.headers = null; - this.content = content; - } - - public HttpResult(int code, Map> headers, String content) { - this.code = code; - this.headers = headers; - this.content = content; - } + public HttpResult(int code, String content) { + this.code = code; + this.headers = null; + this.content = content; + } + + public HttpResult(int code, Map> headers, String content) { + this.code = code; + this.headers = headers; + this.content = content; + } } - + } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/Limiter.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/Limiter.java index 7dbec0f10..4271b84a6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/Limiter.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/Limiter.java @@ -27,53 +27,52 @@ import com.google.common.util.concurrent.RateLimiter; /** * Limiter - * - * @author Nacos * + * @author Nacos */ public class Limiter { - static final public Logger log = LogUtils.logger(Limiter.class); - private static int CAPACITY_SIZE = 1000; - private static int LIMIT_TIME = 1000; - private static Cache cache = CacheBuilder.newBuilder() - .initialCapacity(CAPACITY_SIZE).expireAfterAccess(1, TimeUnit.MINUTES) - .build(); + static final public Logger log = LogUtils.logger(Limiter.class); + private static int CAPACITY_SIZE = 1000; + private static int LIMIT_TIME = 1000; + private static Cache cache = CacheBuilder.newBuilder() + .initialCapacity(CAPACITY_SIZE).expireAfterAccess(1, TimeUnit.MINUTES) + .build(); - /** - * qps 5 - */ - private static final String DEFAULT_LIMIT = "5"; - private static double limit = 5; + /** + * qps 5 + */ + private static final String DEFAULT_LIMIT = "5"; + private static double limit = 5; - static { - try { - String limitTimeStr = System - .getProperty("limitTime", DEFAULT_LIMIT); - limit = Double.parseDouble(limitTimeStr); - log.info("limitTime:{}", limit); - } catch (Exception e) { - log.error("Nacos-xxx", "init limitTime fail", e); - } - } + static { + try { + String limitTimeStr = System + .getProperty("limitTime", DEFAULT_LIMIT); + limit = Double.parseDouble(limitTimeStr); + log.info("limitTime:{}", limit); + } catch (Exception e) { + log.error("Nacos-xxx", "init limitTime fail", e); + } + } - public static boolean isLimit(String accessKeyID) { - RateLimiter rateLimiter = null; - try { - rateLimiter = cache.get(accessKeyID, new Callable() { - @Override - public RateLimiter call() throws Exception { - return RateLimiter.create(limit); - } - }); - } catch (ExecutionException e) { - log.error("Nacos-XXX", "create limit fail", e); - } - if (rateLimiter != null && !rateLimiter.tryAcquire(LIMIT_TIME, TimeUnit.MILLISECONDS)) { - log.error("Nacos-XXX", "access_key_id:{} limited", accessKeyID); - return true; - } - return false; - } + public static boolean isLimit(String accessKeyID) { + RateLimiter rateLimiter = null; + try { + rateLimiter = cache.get(accessKeyID, new Callable() { + @Override + public RateLimiter call() throws Exception { + return RateLimiter.create(limit); + } + }); + } catch (ExecutionException e) { + log.error("Nacos-XXX", "create limit fail", e); + } + if (rateLimiter != null && !rateLimiter.tryAcquire(LIMIT_TIME, TimeUnit.MILLISECONDS)) { + log.error("Nacos-XXX", "access_key_id:{} limited", accessKeyID); + return true; + } + return false; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java index b9b559214..24bdc60ac 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java @@ -25,169 +25,170 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; - /** * Local Disaster Recovery Directory Tool - * + * * @author Nacos */ public class LocalConfigInfoProcessor { - - final static public Logger log = LogUtils.logger(LocalConfigInfoProcessor.class); + + final static public Logger log = LogUtils.logger(LocalConfigInfoProcessor.class); static public String getFailover(String serverName, String dataId, String group, String tenant) { - File localPath = getFailoverFile(serverName, dataId, group, tenant); - if (!localPath.exists() || !localPath.isFile()) { - return null; - } - - try { - return readFile(localPath); - } catch (IOException ioe) { - log.error(serverName, "NACOS-XXXX","get failover error, " + localPath + ioe.toString()); - return null; - } + File localPath = getFailoverFile(serverName, dataId, group, tenant); + if (!localPath.exists() || !localPath.isFile()) { + return null; + } + + try { + return readFile(localPath); + } catch (IOException ioe) { + log.error(serverName, "NACOS-XXXX", "get failover error, " + localPath + ioe.toString()); + return null; + } } - + /** * 获取本地缓存文件内容。NULL表示没有本地文件或抛出异常。 */ static public String getSnapshot(String name, String dataId, String group, String tenant) { - if (!SnapShotSwitch.getIsSnapShot()) { - return null; - } - File file = getSnapshotFile(name, dataId, group, tenant); - if (!file.exists() || !file.isFile()) { - return null; - } - - try { - return readFile(file); - } catch (IOException ioe) { - log.error(name, "NACOS-XXXX","get snapshot error, " + file + ", " + ioe.toString()); - return null; - } + if (!SnapShotSwitch.getIsSnapShot()) { + return null; + } + File file = getSnapshotFile(name, dataId, group, tenant); + if (!file.exists() || !file.isFile()) { + return null; + } + + try { + return readFile(file); + } catch (IOException ioe) { + log.error(name, "NACOS-XXXX", "get snapshot error, " + file + ", " + ioe.toString()); + return null; + } } - - static private String readFile(File file) throws IOException { - if (!file.exists() || !file.isFile()) { - return null; - } - if (JVMUtil.isMultiInstance()) { - return ConcurrentDiskUtil.getFileContent(file, Constants.ENCODE); - } else { - InputStream is = null; - try { - is = new FileInputStream(file); - return IOUtils.toString(is, Constants.ENCODE); - } finally { - try { - if (null != is) { - is.close(); - } - } catch (IOException ioe) { - } - } - } - } + static private String readFile(File file) throws IOException { + if (!file.exists() || !file.isFile()) { + return null; + } + + if (JVMUtil.isMultiInstance()) { + return ConcurrentDiskUtil.getFileContent(file, Constants.ENCODE); + } else { + InputStream is = null; + try { + is = new FileInputStream(file); + return IOUtils.toString(is, Constants.ENCODE); + } finally { + try { + if (null != is) { + is.close(); + } + } catch (IOException ioe) { + } + } + } + } - static public void saveSnapshot(String envName, String dataId, String group, String tenant, String config) { - if (!SnapShotSwitch.getIsSnapShot()) { - return; - } + if (!SnapShotSwitch.getIsSnapShot()) { + return; + } File file = getSnapshotFile(envName, dataId, group, tenant); if (null == config) { try { IOUtils.delete(file); } catch (IOException ioe) { - log.error(envName, "NACOS-XXXX","delete snapshot error, " + file + ", " + ioe.toString()); + log.error(envName, "NACOS-XXXX", "delete snapshot error, " + file + ", " + ioe.toString()); } } else { try { - boolean isMdOk = file.getParentFile().mkdirs(); - if (!isMdOk) { - log.error(envName, "NACOS-XXXX", "save snapshot error"); - } - if (JVMUtil.isMultiInstance()) { - ConcurrentDiskUtil.writeFileContent(file, config, - Constants.ENCODE); - } else { - IOUtils.writeStringToFile(file, config, Constants.ENCODE); - } + File parentFile = file.getParentFile(); + if (!parentFile.exists()) { + boolean isMdOk = parentFile.mkdirs(); + if (!isMdOk) { + log.error(envName, "NACOS-XXXX", "save snapshot error"); + } + } + + if (JVMUtil.isMultiInstance()) { + ConcurrentDiskUtil.writeFileContent(file, config, + Constants.ENCODE); + } else { + IOUtils.writeStringToFile(file, config, Constants.ENCODE); + } } catch (IOException ioe) { - log.error(envName, "NACOS-XXXX","save snapshot error, " + file + ", " + ioe.toString()); + log.error(envName, "NACOS-XXXX", "save snapshot error, " + file + ", " + ioe.toString()); } } } - + /** * 清除snapshot目录下所有缓存文件。 */ static public void cleanAllSnapshot() { try { - File rootFile = new File(LOCAL_SNAPSHOT_PATH); - File[] files = rootFile.listFiles(); - if (files == null || files.length == 0) { - return; - } - for(File file : files){ - if(file.getName().endsWith("_nacos")){ - IOUtils.cleanDirectory(file); - } - } + File rootFile = new File(LOCAL_SNAPSHOT_PATH); + File[] files = rootFile.listFiles(); + if (files == null || files.length == 0) { + return; + } + for (File file : files) { + if (file.getName().endsWith("_nacos")) { + IOUtils.cleanDirectory(file); + } + } } catch (IOException ioe) { - log.error("NACOS-XXXX","clean all snapshot error, " + ioe.toString(), ioe); + log.error("NACOS-XXXX", "clean all snapshot error, " + ioe.toString(), ioe); } } - - static public void cleanEnvSnapshot(String envName){ - File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos"); - tmp = new File(tmp, "snapshot"); - try { - IOUtils.cleanDirectory(tmp); - log.info("success dlelet " + envName + "-snapshot"); - } catch (IOException e) { - log.info("fail dlelet " + envName + "-snapshot, " + e.toString()); - e.printStackTrace(); - } + + static public void cleanEnvSnapshot(String envName) { + File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos"); + tmp = new File(tmp, "snapshot"); + try { + IOUtils.cleanDirectory(tmp); + log.info("success delete " + envName + "-snapshot"); + } catch (IOException e) { + log.info("fail delete " + envName + "-snapshot, " + e.toString()); + e.printStackTrace(); + } } - static File getFailoverFile(String serverName, String dataId, String group, String tenant) { - File tmp = new File(LOCAL_SNAPSHOT_PATH, serverName + "_nacos"); - tmp = new File(tmp, "data"); - if (StringUtils.isBlank(tenant)) { - tmp = new File(tmp, "config-data"); - } else - { - tmp = new File(tmp, "config-data-tenant"); - tmp = new File(tmp, tenant); - } - return new File(new File(tmp, group), dataId); - } - - static File getSnapshotFile(String envName, String dataId, String group, String tenant) { - File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos"); - if (StringUtils.isBlank(tenant)) { - tmp = new File(tmp, "snapshot"); - } else { - tmp = new File(tmp, "snapshot-tenant"); - tmp = new File(tmp, tenant); - } - + File tmp = new File(LOCAL_SNAPSHOT_PATH, serverName + "_nacos"); + tmp = new File(tmp, "data"); + if (StringUtils.isBlank(tenant)) { + tmp = new File(tmp, "config-data"); + } else { + tmp = new File(tmp, "config-data-tenant"); + tmp = new File(tmp, tenant); + } return new File(new File(tmp, group), dataId); } - + + static File getSnapshotFile(String envName, String dataId, String group, String tenant) { + File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos"); + if (StringUtils.isBlank(tenant)) { + tmp = new File(tmp, "snapshot"); + } else { + tmp = new File(tmp, "snapshot-tenant"); + tmp = new File(tmp, tenant); + } + + return new File(new File(tmp, group), dataId); + } + public static final String LOCAL_FILEROOT_PATH; public static final String LOCAL_SNAPSHOT_PATH; - static { - LOCAL_FILEROOT_PATH = System.getProperty("JM.LOG.PATH", System.getProperty("user.home")) + File.separator - + "nacos" + File.separator + "config"; - LOCAL_SNAPSHOT_PATH = System.getProperty("JM.SNAPSHOT.PATH", System.getProperty("user.home")) + File.separator - + "nacos" + File.separator + "config"; - log.warn("LOCAL_SNAPSHOT_PATH:{}", LOCAL_SNAPSHOT_PATH); - } + + static { + LOCAL_FILEROOT_PATH = System.getProperty("JM.LOG.PATH", System.getProperty("user.home")) + File.separator + + "nacos" + File.separator + "config"; + LOCAL_SNAPSHOT_PATH = System.getProperty("JM.SNAPSHOT.PATH", System.getProperty("user.home")) + File.separator + + "nacos" + File.separator + "config"; + log.warn("LOCAL_SNAPSHOT_PATH:{}", LOCAL_SNAPSHOT_PATH); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java index 8bb2cb6da..26a26fd79 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerHttpAgent.java @@ -41,351 +41,352 @@ import java.util.List; import java.util.Properties; /** - * Server Agent - * - * @author water.lyl + * Server Agent * + * @author water.lyl */ public class ServerHttpAgent { - final static public Logger log = LogUtils.logger(ServerHttpAgent.class); - /** - * @param path - * 相对于web应用根,以/开头 - * @param headers - * @param paramValues - * @param encoding - * @param readTimeoutMs - * @return - * @throws IOException - */ - public HttpResult httpGet(String path, List headers, List paramValues, String encoding, - long readTimeoutMs) throws IOException { - final long endTime = System.currentTimeMillis() + readTimeoutMs; + final static public Logger log = LogUtils.logger(ServerHttpAgent.class); - boolean isSSL = false; - - do { - try { - List newHeaders = getSpasHeaders(paramValues); - if (headers != null) { - newHeaders.addAll(headers); - } - HttpResult result = HttpSimpleClient.httpGet( - getUrl(serverListMgr.getCurrentServerAddr(), path, isSSL), newHeaders, paramValues, encoding, - readTimeoutMs, isSSL); - if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR - || result.code == HttpURLConnection.HTTP_BAD_GATEWAY - || result.code == HttpURLConnection.HTTP_UNAVAILABLE) { - log.error("NACOS ConnectException", "currentServerAddr:{}. httpCode:", - new Object[] { serverListMgr.getCurrentServerAddr(), result.code }); - } else { - return result; - } - } catch (ConnectException ce) { - log.error("NACOS ConnectException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr() }); - serverListMgr.refreshCurrentServerAddr(); - } catch (SocketTimeoutException stoe) { - log.error("NACOS SocketTimeoutException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - serverListMgr.refreshCurrentServerAddr(); - } catch (IOException ioe) { - log.error("NACOS IOException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - throw ioe; - } - } while (System.currentTimeMillis() <= endTime); + /** + * @param path 相对于web应用根,以/开头 + * @param headers + * @param paramValues + * @param encoding + * @param readTimeoutMs + * @return + * @throws IOException + */ + public HttpResult httpGet(String path, List headers, List paramValues, String encoding, + long readTimeoutMs) throws IOException { + final long endTime = System.currentTimeMillis() + readTimeoutMs; - log.error("NACOS-0002", - LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0002", "环境问题", "no available server")); - throw new ConnectException("no available server"); - } + boolean isSSL = false; - public HttpResult httpPost(String path, List headers, List paramValues, String encoding, - long readTimeoutMs) throws IOException { - final long endTime = System.currentTimeMillis() + readTimeoutMs; - boolean isSSL = false; - do { - try { - List newHeaders = getSpasHeaders(paramValues); - if (headers != null) { - newHeaders.addAll(headers); - } - HttpResult result = HttpSimpleClient.httpPost( - getUrl(serverListMgr.getCurrentServerAddr(), path, isSSL), newHeaders, paramValues, encoding, - readTimeoutMs, isSSL); - if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR - || result.code == HttpURLConnection.HTTP_BAD_GATEWAY - || result.code == HttpURLConnection.HTTP_UNAVAILABLE) { - log.error("NACOS ConnectException", "currentServerAddr:{}. httpCode:", - new Object[] { serverListMgr.getCurrentServerAddr(), result.code }); - } else { - return result; - } - } catch (ConnectException ce) { - log.error("NACOS ConnectException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - serverListMgr.refreshCurrentServerAddr(); - } catch (SocketTimeoutException stoe) { - log.error("NACOS SocketTimeoutException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - serverListMgr.refreshCurrentServerAddr(); - } catch (IOException ioe) { - log.error("NACOS IOException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - throw ioe; - } + do { + try { + List newHeaders = getSpasHeaders(paramValues); + if (headers != null) { + newHeaders.addAll(headers); + } + HttpResult result = HttpSimpleClient.httpGet( + getUrl(serverListMgr.getCurrentServerAddr(), path, isSSL), newHeaders, paramValues, encoding, + readTimeoutMs, isSSL); + if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR + || result.code == HttpURLConnection.HTTP_BAD_GATEWAY + || result.code == HttpURLConnection.HTTP_UNAVAILABLE) { + log.error("NACOS ConnectException", "currentServerAddr:{}. httpCode:", + new Object[] {serverListMgr.getCurrentServerAddr(), result.code}); + } else { + return result; + } + } catch (ConnectException ce) { + log.error("NACOS ConnectException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + serverListMgr.refreshCurrentServerAddr(); + } catch (SocketTimeoutException stoe) { + log.error("NACOS SocketTimeoutException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + serverListMgr.refreshCurrentServerAddr(); + } catch (IOException ioe) { + log.error("NACOS IOException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + throw ioe; + } + } while (System.currentTimeMillis() <= endTime); - } while (System.currentTimeMillis() <= endTime); + log.error("NACOS-0002", + LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0002", "环境问题", "no available server")); + throw new ConnectException("no available server"); + } - log.error("NACOS-0002", - LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0002", "环境问题", "no available server")); - throw new ConnectException("no available server"); - } - - public HttpResult httpDelete(String path, List headers, List paramValues, String encoding, - long readTimeoutMs) throws IOException { - final long endTime = System.currentTimeMillis() + readTimeoutMs; - boolean isSSL = false; - do { - try { - List newHeaders = getSpasHeaders(paramValues); - if (headers != null) { - newHeaders.addAll(headers); - } - HttpResult result = HttpSimpleClient.httpDelete( - getUrl(serverListMgr.getCurrentServerAddr(), path, isSSL), newHeaders, paramValues, encoding, - readTimeoutMs, isSSL); - if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR - || result.code == HttpURLConnection.HTTP_BAD_GATEWAY - || result.code == HttpURLConnection.HTTP_UNAVAILABLE) { - log.error("NACOS ConnectException", "currentServerAddr:{}. httpCode:", - new Object[] { serverListMgr.getCurrentServerAddr(), result.code }); - } else { - return result; - } - } catch (ConnectException ce) { - log.error("NACOS ConnectException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - serverListMgr.refreshCurrentServerAddr(); - } catch (SocketTimeoutException stoe) { - log.error("NACOS SocketTimeoutException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - serverListMgr.refreshCurrentServerAddr(); - } catch (IOException ioe) { - log.error("NACOS IOException", "currentServerAddr:{}", - new Object[] { serverListMgr.getCurrentServerAddr()}); - throw ioe; - } - - } while (System.currentTimeMillis() <= endTime); - - log.error("NACOS-0002", - LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0002", "环境问题", "no available server")); - throw new ConnectException("no available server"); - } + public HttpResult httpPost(String path, List headers, List paramValues, String encoding, + long readTimeoutMs) throws IOException { + final long endTime = System.currentTimeMillis() + readTimeoutMs; + boolean isSSL = false; + do { + try { + List newHeaders = getSpasHeaders(paramValues); + if (headers != null) { + newHeaders.addAll(headers); + } + HttpResult result = HttpSimpleClient.httpPost( + getUrl(serverListMgr.getCurrentServerAddr(), path, isSSL), newHeaders, paramValues, encoding, + readTimeoutMs, isSSL); + if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR + || result.code == HttpURLConnection.HTTP_BAD_GATEWAY + || result.code == HttpURLConnection.HTTP_UNAVAILABLE) { + log.error("NACOS ConnectException", "currentServerAddr:{}. httpCode:", + new Object[] {serverListMgr.getCurrentServerAddr(), result.code}); + } else { + return result; + } + } catch (ConnectException ce) { + log.error("NACOS ConnectException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + serverListMgr.refreshCurrentServerAddr(); + } catch (SocketTimeoutException stoe) { + log.error("NACOS SocketTimeoutException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + serverListMgr.refreshCurrentServerAddr(); + } catch (IOException ioe) { + log.error("NACOS IOException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + throw ioe; + } - private String getUrl(String serverAddr, String relativePath, boolean isSSL) { - String httpPrefix = "http://"; - if (isSSL) { - httpPrefix = "https://"; - } - return httpPrefix + serverAddr + "/" + serverListMgr.getContentPath() + relativePath; - } + } while (System.currentTimeMillis() <= endTime); - public static String getAppname() { - return ParamUtil.getAppName(); - } + log.error("NACOS-0002", + LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0002", "环境问题", "no available server")); + throw new ConnectException("no available server"); + } - public ServerHttpAgent(ServerListManager mgr) { - serverListMgr = mgr; - } - - public ServerHttpAgent(ServerListManager mgr, Properties properties) { - serverListMgr = mgr; - String ak = properties.getProperty(PropertyKeyConst.ACCESS_KEY); - if (StringUtils.isBlank(ak)) { - accessKey = SpasAdapter.getAk(); - } else { - accessKey = ak; - } + public HttpResult httpDelete(String path, List headers, List paramValues, String encoding, + long readTimeoutMs) throws IOException { + final long endTime = System.currentTimeMillis() + readTimeoutMs; + boolean isSSL = false; + do { + try { + List newHeaders = getSpasHeaders(paramValues); + if (headers != null) { + newHeaders.addAll(headers); + } + HttpResult result = HttpSimpleClient.httpDelete( + getUrl(serverListMgr.getCurrentServerAddr(), path, isSSL), newHeaders, paramValues, encoding, + readTimeoutMs, isSSL); + if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR + || result.code == HttpURLConnection.HTTP_BAD_GATEWAY + || result.code == HttpURLConnection.HTTP_UNAVAILABLE) { + log.error("NACOS ConnectException", "currentServerAddr:{}. httpCode:", + new Object[] {serverListMgr.getCurrentServerAddr(), result.code}); + } else { + return result; + } + } catch (ConnectException ce) { + log.error("NACOS ConnectException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + serverListMgr.refreshCurrentServerAddr(); + } catch (SocketTimeoutException stoe) { + log.error("NACOS SocketTimeoutException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + serverListMgr.refreshCurrentServerAddr(); + } catch (IOException ioe) { + log.error("NACOS IOException", "currentServerAddr:{}", + new Object[] {serverListMgr.getCurrentServerAddr()}); + throw ioe; + } - String sk = properties.getProperty(PropertyKeyConst.SECRET_KEY); - if (StringUtils.isBlank(sk)) { - secretKey = SpasAdapter.getSk(); - } else { - secretKey = sk; - } - } - - public ServerHttpAgent(Properties properties) throws NacosException { - String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE); - if (StringUtils.isBlank(encodeTmp)) { - encode = Constants.ENCODE; - } else { - encode = encodeTmp.trim(); - } - serverListMgr = new ServerListManager(properties); - String ak = properties.getProperty(PropertyKeyConst.ACCESS_KEY); - if (StringUtils.isBlank(ak)) { - accessKey = SpasAdapter.getAk(); - } else { - accessKey = ak; - } - - String sk = properties.getProperty(PropertyKeyConst.SECRET_KEY); - if (StringUtils.isBlank(sk)) { - secretKey = SpasAdapter.getSk(); - } else { - secretKey = sk; - } - } + } while (System.currentTimeMillis() <= endTime); - public synchronized void start() throws NacosException { - serverListMgr.start(); - } - - private List getSpasHeaders(List paramValues) throws IOException { - List newHeaders = new ArrayList(); - // STS 临时凭证鉴权的优先级高于 AK/SK 鉴权 - if (STSConfig.getInstance().isSTSOn()) { - STSCredential sTSCredential = getSTSCredential(); - accessKey = sTSCredential.accessKeyId; - secretKey = sTSCredential.accessKeySecret; - newHeaders.add("Spas-SecurityToken"); - newHeaders.add(sTSCredential.securityToken); - } + log.error("NACOS-0002", + LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0002", "环境问题", "no available server")); + throw new ConnectException("no available server"); + } - if (StringUtils.isNotEmpty(accessKey) && StringUtils.isNotEmpty(secretKey)) { - newHeaders.add("Spas-AccessKey"); - newHeaders.add(accessKey); - List signHeaders = SpasAdapter.getSignHeaders(paramValues, secretKey); - if (signHeaders != null) { - newHeaders.addAll(signHeaders); - } - } - return newHeaders; - } + private String getUrl(String serverAddr, String relativePath, boolean isSSL) { + String httpPrefix = "http://"; + if (isSSL) { + httpPrefix = "https://"; + } + return httpPrefix + serverAddr + "/" + serverListMgr.getContentPath() + relativePath; + } - private STSCredential getSTSCredential() throws IOException { - boolean cacheSecurityCredentials = STSConfig.getInstance().isCacheSecurityCredentials(); - if (cacheSecurityCredentials && sTSCredential != null) { - long currentTime = System.currentTimeMillis(); - long expirationTime = sTSCredential.expiration.getTime(); - int timeToRefreshInMillisecond = STSConfig.getInstance().getTimeToRefreshInMillisecond(); - if (expirationTime - currentTime > timeToRefreshInMillisecond) { - return sTSCredential; - } - } - String stsResponse = getSTSResponse(); - STSCredential stsCredentialTmp = (STSCredential)JSONUtils.deserializeObject(stsResponse, - new TypeReference() {}); - sTSCredential = stsCredentialTmp; - log.info("getSTSCredential", "code:{}, accessKeyId:{}, lastUpdated:{}, expiration:{}", sTSCredential.getCode(), - sTSCredential.getAccessKeyId(), sTSCredential.getLastUpdated(), sTSCredential.getExpiration()); - return sTSCredential; - } + public static String getAppname() { + return ParamUtil.getAppName(); + } - private static String getSTSResponse() throws IOException { - String securityCredentials = STSConfig.getInstance().getSecurityCredentials(); - if (securityCredentials != null) { - return securityCredentials; - } - String securityCredentialsUrl = STSConfig.getInstance().getSecurityCredentialsUrl(); - HttpURLConnection conn = null; - int respCode; - String response; - try { - conn = (HttpURLConnection)new URL(securityCredentialsUrl).openConnection(); - conn.setRequestMethod("GET"); - conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100); - conn.setReadTimeout(1000); - conn.connect(); - respCode = conn.getResponseCode(); - if (HttpURLConnection.HTTP_OK == respCode) { - response = IOUtils.toString(conn.getInputStream(), Constants.ENCODE); - } else { - response = IOUtils.toString(conn.getErrorStream(), Constants.ENCODE); - } - } catch (IOException e) { - log.error("500", "can not get security credentials", e); - throw e; - } finally { - if (null != conn) { - conn.disconnect(); - } - } - if (HttpURLConnection.HTTP_OK == respCode) { - return response; - } - log.error(respCode + "", "can not get security credentials, securityCredentialsUrl:{}, response:{}", - new Object[] {securityCredentialsUrl, response}); - throw new IOException("can not get security credentials, responseCode: " + respCode + ", response: " + response); - } - - public String getName() { - return serverListMgr.getName(); - } + public ServerHttpAgent(ServerListManager mgr) { + serverListMgr = mgr; + } - public String getNamespace() { - return serverListMgr.getNamespace(); - } - public String getTenant() { - return serverListMgr.getTenant(); - } + public ServerHttpAgent(ServerListManager mgr, Properties properties) { + serverListMgr = mgr; + String ak = properties.getProperty(PropertyKeyConst.ACCESS_KEY); + if (StringUtils.isBlank(ak)) { + accessKey = SpasAdapter.getAk(); + } else { + accessKey = ak; + } - public String getEncode() { - return encode; - } + String sk = properties.getProperty(PropertyKeyConst.SECRET_KEY); + if (StringUtils.isBlank(sk)) { + secretKey = SpasAdapter.getSk(); + } else { + secretKey = sk; + } + } - @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") - private static class STSCredential { - @JsonProperty(value = "AccessKeyId") - private String accessKeyId; - @JsonProperty(value = "AccessKeySecret") - private String accessKeySecret; - @JsonProperty(value = "Expiration") - private Date expiration; - @JsonProperty(value = "SecurityToken") - private String securityToken; - @JsonProperty(value = "LastUpdated") - private Date lastUpdated; - @JsonProperty(value = "Code") - private String code; + public ServerHttpAgent(Properties properties) throws NacosException { + String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE); + if (StringUtils.isBlank(encodeTmp)) { + encode = Constants.ENCODE; + } else { + encode = encodeTmp.trim(); + } + serverListMgr = new ServerListManager(properties); + String ak = properties.getProperty(PropertyKeyConst.ACCESS_KEY); + if (StringUtils.isBlank(ak)) { + accessKey = SpasAdapter.getAk(); + } else { + accessKey = ak; + } - public String getAccessKeyId() { - return accessKeyId; - } + String sk = properties.getProperty(PropertyKeyConst.SECRET_KEY); + if (StringUtils.isBlank(sk)) { + secretKey = SpasAdapter.getSk(); + } else { + secretKey = sk; + } + } - public Date getExpiration() { - return expiration; - } + public synchronized void start() throws NacosException { + serverListMgr.start(); + } - public Date getLastUpdated() { - return lastUpdated; - } + private List getSpasHeaders(List paramValues) throws IOException { + List newHeaders = new ArrayList(); + // STS 临时凭证鉴权的优先级高于 AK/SK 鉴权 + if (STSConfig.getInstance().isSTSOn()) { + STSCredential sTSCredential = getSTSCredential(); + accessKey = sTSCredential.accessKeyId; + secretKey = sTSCredential.accessKeySecret; + newHeaders.add("Spas-SecurityToken"); + newHeaders.add(sTSCredential.securityToken); + } - public String getCode() { - return code; - } + if (StringUtils.isNotEmpty(accessKey) && StringUtils.isNotEmpty(secretKey)) { + newHeaders.add("Spas-AccessKey"); + newHeaders.add(accessKey); + List signHeaders = SpasAdapter.getSignHeaders(paramValues, secretKey); + if (signHeaders != null) { + newHeaders.addAll(signHeaders); + } + } + return newHeaders; + } - public String toString() { - return "STSCredential{" + - "accessKeyId='" + accessKeyId + '\'' + - ", accessKeySecret='" + accessKeySecret + '\'' + - ", expiration=" + expiration + - ", securityToken='" + securityToken + '\'' + - ", lastUpdated=" + lastUpdated + - ", code='" + code + '\'' + - '}'; - } - } - - private String accessKey; - private String secretKey; - private String encode; - private volatile STSCredential sTSCredential; - final ServerListManager serverListMgr; + private STSCredential getSTSCredential() throws IOException { + boolean cacheSecurityCredentials = STSConfig.getInstance().isCacheSecurityCredentials(); + if (cacheSecurityCredentials && sTSCredential != null) { + long currentTime = System.currentTimeMillis(); + long expirationTime = sTSCredential.expiration.getTime(); + int timeToRefreshInMillisecond = STSConfig.getInstance().getTimeToRefreshInMillisecond(); + if (expirationTime - currentTime > timeToRefreshInMillisecond) { + return sTSCredential; + } + } + String stsResponse = getSTSResponse(); + STSCredential stsCredentialTmp = (STSCredential)JSONUtils.deserializeObject(stsResponse, + new TypeReference() {}); + sTSCredential = stsCredentialTmp; + log.info("getSTSCredential", "code:{}, accessKeyId:{}, lastUpdated:{}, expiration:{}", sTSCredential.getCode(), + sTSCredential.getAccessKeyId(), sTSCredential.getLastUpdated(), sTSCredential.getExpiration()); + return sTSCredential; + } + + private static String getSTSResponse() throws IOException { + String securityCredentials = STSConfig.getInstance().getSecurityCredentials(); + if (securityCredentials != null) { + return securityCredentials; + } + String securityCredentialsUrl = STSConfig.getInstance().getSecurityCredentialsUrl(); + HttpURLConnection conn = null; + int respCode; + String response; + try { + conn = (HttpURLConnection)new URL(securityCredentialsUrl).openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100); + conn.setReadTimeout(1000); + conn.connect(); + respCode = conn.getResponseCode(); + if (HttpURLConnection.HTTP_OK == respCode) { + response = IOUtils.toString(conn.getInputStream(), Constants.ENCODE); + } else { + response = IOUtils.toString(conn.getErrorStream(), Constants.ENCODE); + } + } catch (IOException e) { + log.error("500", "can not get security credentials", e); + throw e; + } finally { + if (null != conn) { + conn.disconnect(); + } + } + if (HttpURLConnection.HTTP_OK == respCode) { + return response; + } + log.error(respCode + "", "can not get security credentials, securityCredentialsUrl:{}, response:{}", + new Object[] {securityCredentialsUrl, response}); + throw new IOException( + "can not get security credentials, responseCode: " + respCode + ", response: " + response); + } + + public String getName() { + return serverListMgr.getName(); + } + + public String getNamespace() { + return serverListMgr.getNamespace(); + } + + public String getTenant() { + return serverListMgr.getTenant(); + } + + public String getEncode() { + return encode; + } + + @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") + private static class STSCredential { + @JsonProperty(value = "AccessKeyId") + private String accessKeyId; + @JsonProperty(value = "AccessKeySecret") + private String accessKeySecret; + @JsonProperty(value = "Expiration") + private Date expiration; + @JsonProperty(value = "SecurityToken") + private String securityToken; + @JsonProperty(value = "LastUpdated") + private Date lastUpdated; + @JsonProperty(value = "Code") + private String code; + + public String getAccessKeyId() { + return accessKeyId; + } + + public Date getExpiration() { + return expiration; + } + + public Date getLastUpdated() { + return lastUpdated; + } + + public String getCode() { + return code; + } + + public String toString() { + return "STSCredential{" + + "accessKeyId='" + accessKeyId + '\'' + + ", accessKeySecret='" + accessKeySecret + '\'' + + ", expiration=" + expiration + + ", securityToken='" + securityToken + '\'' + + ", lastUpdated=" + lastUpdated + + ", code='" + code + '\'' + + '}'; + } + } + + private String accessKey; + private String secretKey; + private String encode; + private volatile STSCredential sTSCredential; + final ServerListManager serverListMgr; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerListManager.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerListManager.java index ad94d5963..efb24add7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerListManager.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ServerListManager.java @@ -40,212 +40,213 @@ import com.alibaba.nacos.client.utils.StringUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - /** * Serverlist Manager - * + * * @author Nacos */ public class ServerListManager { - - final static public Logger log = LogUtils.logger(ServerListManager.class); - public ServerListManager() { - isFixed = false; - isStarted = false; - name = DEFAULT_NAME; - } - public ServerListManager(List fixed) { - this(fixed, null); - } - - public ServerListManager(List fixed, String namespace) { - isFixed = true; - isStarted = true; - List serverAddrs = new ArrayList(); - for (String serverAddr : fixed) { - String[] serverAddrArr = serverAddr.split(":"); - if (serverAddrArr.length == 1) { - serverAddrs.add(serverAddrArr[0] + ":" + ParamUtil.getDefaultServerPort()); - } else { - serverAddrs.add(serverAddr); - } - } - serverUrls = new ArrayList(serverAddrs); - if (StringUtils.isBlank(namespace)) { - name = FIXED_NAME + "-" + getFixedNameSuffix(serverAddrs.toArray(new String[serverAddrs.size()])); - } else { - this.namespace = namespace; - name = FIXED_NAME + "-" + getFixedNameSuffix(serverAddrs.toArray(new String[serverAddrs.size()])) + "-" - + namespace; - } - } + final static public Logger log = LogUtils.logger(ServerListManager.class); - public ServerListManager(String host, int port) { - isFixed = false; - isStarted = false; - name = CUSTOM_NAME + "-" + host + "-" + port; - addressServerUrl = String.format("http://%s:%d/%s/%s", host, port, contentPath, serverListName); - } + public ServerListManager() { + isFixed = false; + isStarted = false; + name = DEFAULT_NAME; + } - public ServerListManager(String endpoint) throws NacosException { - this(endpoint, null); - } + public ServerListManager(List fixed) { + this(fixed, null); + } + + public ServerListManager(List fixed, String namespace) { + isFixed = true; + isStarted = true; + List serverAddrs = new ArrayList(); + for (String serverAddr : fixed) { + String[] serverAddrArr = serverAddr.split(":"); + if (serverAddrArr.length == 1) { + serverAddrs.add(serverAddrArr[0] + ":" + ParamUtil.getDefaultServerPort()); + } else { + serverAddrs.add(serverAddr); + } + } + serverUrls = new ArrayList(serverAddrs); + if (StringUtils.isBlank(namespace)) { + name = FIXED_NAME + "-" + getFixedNameSuffix(serverAddrs.toArray(new String[serverAddrs.size()])); + } else { + this.namespace = namespace; + name = FIXED_NAME + "-" + getFixedNameSuffix(serverAddrs.toArray(new String[serverAddrs.size()])) + "-" + + namespace; + } + } + + public ServerListManager(String host, int port) { + isFixed = false; + isStarted = false; + name = CUSTOM_NAME + "-" + host + "-" + port; + addressServerUrl = String.format("http://%s:%d/%s/%s", host, port, contentPath, serverListName); + } + + public ServerListManager(String endpoint) throws NacosException { + this(endpoint, null); + } + + public ServerListManager(String endpoint, String namespace) throws NacosException { + isFixed = false; + isStarted = false; + if (StringUtils.isBlank(endpoint)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "endpoint is blank"); + } + if (StringUtils.isBlank(namespace)) { + name = endpoint; + addressServerUrl = String.format("http://%s:%d/%s/%s", endpoint, endpointPort, contentPath, + serverListName); + } else { + if (StringUtils.isBlank(endpoint)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "endpoint is blank"); + } + name = endpoint + "-" + namespace; + this.namespace = namespace; + this.tenant = namespace; + addressServerUrl = String.format("http://%s:%d/%s/%s?namespace=%s", endpoint, endpointPort, contentPath, + serverListName, namespace); + } + } + + public ServerListManager(Properties properties) throws NacosException { + isStarted = false; + String serverAddrsStr = properties.getProperty(PropertyKeyConst.SERVER_ADDR); + String namespace = properties.getProperty(PropertyKeyConst.NAMESPACE); + initParam(properties); + if (StringUtils.isNotEmpty(serverAddrsStr)) { + isFixed = true; + List serverAddrs = new ArrayList(); + String[] serverAddrsArr = serverAddrsStr.split(","); + for (String serverAddr : serverAddrsArr) { + String[] serverAddrArr = serverAddr.split(":"); + if (serverAddrArr.length == 1) { + serverAddrs.add(serverAddrArr[0] + ":" + ParamUtil.getDefaultServerPort()); + } else { + serverAddrs.add(serverAddr); + } + } + serverUrls = serverAddrs; + if (StringUtils.isBlank(namespace)) { + name = FIXED_NAME + "-" + getFixedNameSuffix(serverUrls.toArray(new String[serverUrls.size()])); + } else { + this.namespace = namespace; + this.tenant = namespace; + name = FIXED_NAME + "-" + getFixedNameSuffix(serverUrls.toArray(new String[serverUrls.size()])) + "-" + + namespace; + } + } else { + if (StringUtils.isBlank(endpoint)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "endpoint is blank"); + } + isFixed = false; + if (StringUtils.isBlank(namespace)) { + name = endpoint; + addressServerUrl = String.format("http://%s:%d/%s/%s", endpoint, endpointPort, contentPath, + serverListName); + } else { + this.namespace = namespace; + this.tenant = namespace; + name = endpoint + "-" + namespace; + addressServerUrl = String.format("http://%s:%d/%s/%s?namespace=%s", endpoint, endpointPort, + contentPath, serverListName, namespace); + } + } + } + + private void initParam(Properties properties) { + String endpointTmp = properties.getProperty(PropertyKeyConst.ENDPOINT); + if (!StringUtils.isBlank(endpointTmp)) { + endpoint = endpointTmp; + } + String contentPathTmp = properties.getProperty(PropertyKeyConst.CONTEXT_PATH); + if (!StringUtils.isBlank(contentPathTmp)) { + contentPath = contentPathTmp; + } + String serverListNameTmp = properties.getProperty(PropertyKeyConst.CLUSTER_NAME); + if (!StringUtils.isBlank(serverListNameTmp)) { + serverListName = serverListNameTmp; + } + } - public ServerListManager(String endpoint, String namespace) throws NacosException { - isFixed = false; - isStarted = false; - if (StringUtils.isBlank(endpoint)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "endpoint is blank"); - } - if (StringUtils.isBlank(namespace)) { - name = endpoint; - addressServerUrl = String.format("http://%s:%d/%s/%s", endpoint, endpointPort, contentPath, - serverListName); - } else { - if (StringUtils.isBlank(endpoint)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "endpoint is blank"); - } - name = endpoint + "-" + namespace; - this.namespace = namespace; - this.tenant = namespace; - addressServerUrl = String.format("http://%s:%d/%s/%s?namespace=%s", endpoint, endpointPort, contentPath, - serverListName, namespace); - } - } - - public ServerListManager(Properties properties) throws NacosException { - isStarted = false; - String serverAddrsStr = properties.getProperty(PropertyKeyConst.SERVER_ADDR); - String namespace = properties.getProperty(PropertyKeyConst.NAMESPACE); - initParam(properties); - if (StringUtils.isNotEmpty(serverAddrsStr)) { - isFixed = true; - List serverAddrs = new ArrayList(); - String[] serverAddrsArr = serverAddrsStr.split(","); - for (String serverAddr : serverAddrsArr) { - String[] serverAddrArr = serverAddr.split(":"); - if (serverAddrArr.length == 1) { - serverAddrs.add(serverAddrArr[0] + ":" + ParamUtil.getDefaultServerPort()); - } else { - serverAddrs.add(serverAddr); - } - } - serverUrls = serverAddrs; - if (StringUtils.isBlank(namespace)) { - name = FIXED_NAME + "-" + getFixedNameSuffix(serverUrls.toArray(new String[serverUrls.size()])); - } else { - this.namespace = namespace; - this.tenant = namespace; - name = FIXED_NAME + "-" + getFixedNameSuffix(serverUrls.toArray(new String[serverUrls.size()])) + "-" - + namespace; - } - } else { - if (StringUtils.isBlank(endpoint)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "endpoint is blank"); - } - isFixed = false; - if (StringUtils.isBlank(namespace)) { - name = endpoint; - addressServerUrl = String.format("http://%s:%d/%s/%s", endpoint, endpointPort, contentPath, - serverListName); - } else { - this.namespace = namespace; - this.tenant = namespace; - name = endpoint + "-" + namespace; - addressServerUrl = String.format("http://%s:%d/%s/%s?namespace=%s", endpoint, endpointPort, - contentPath, serverListName, namespace); - } - } - } - - private void initParam(Properties properties) { - String endpointTmp = properties.getProperty(PropertyKeyConst.ENDPOINT); - if (!StringUtils.isBlank(endpointTmp)) { - endpoint = endpointTmp; - } - String contentPathTmp = properties.getProperty(PropertyKeyConst.CONTEXT_PATH); - if (!StringUtils.isBlank(contentPathTmp)) { - contentPath = contentPathTmp; - } - String serverListNameTmp = properties.getProperty(PropertyKeyConst.CLUSTER_NAME); - if (!StringUtils.isBlank(serverListNameTmp)) { - serverListName = serverListNameTmp; - } - } - public synchronized void start() throws NacosException { - + if (isStarted || isFixed) { return; } - GetServerListTask getServersTask = new GetServerListTask(addressServerUrl); - for (int i = 0; i < initServerlistRetryTimes && serverUrls.isEmpty(); ++i) { - getServersTask.run(); - try { - this.wait((i + 1) * 100L); - } catch (Exception e) { - log.warn("get serverlist fail,url: " + addressServerUrl); - } - } + GetServerListTask getServersTask = new GetServerListTask(addressServerUrl); + for (int i = 0; i < initServerlistRetryTimes && serverUrls.isEmpty(); ++i) { + getServersTask.run(); + try { + this.wait((i + 1) * 100L); + } catch (Exception e) { + log.warn("get serverlist fail,url: " + addressServerUrl); + } + } - if (serverUrls.isEmpty()) { - log.error("NACOS-0008", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0008", "环境问题", - "fail to get NACOS-server serverlist! env:" + name + ", not connnect url:" + addressServerUrl)); - log.error(name, "NACOS-XXXX", "[init-serverlist] fail to get NACOS-server serverlist!"); - throw new NacosException(NacosException.SERVER_ERROR, - "fail to get NACOS-server serverlist! env:" + name + ", not connnect url:" + addressServerUrl); - } + if (serverUrls.isEmpty()) { + log.error("NACOS-0008", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0008", "环境问题", + "fail to get NACOS-server serverlist! env:" + name + ", not connnect url:" + addressServerUrl)); + log.error(name, "NACOS-XXXX", "[init-serverlist] fail to get NACOS-server serverlist!"); + throw new NacosException(NacosException.SERVER_ERROR, + "fail to get NACOS-server serverlist! env:" + name + ", not connnect url:" + addressServerUrl); + } TimerService.scheduleWithFixedDelay(getServersTask, 0L, 30L, TimeUnit.SECONDS); isStarted = true; } - + Iterator iterator() { if (serverUrls.isEmpty()) { log.error(name, "NACOS-XXXX", "[iterator-serverlist] No server address defined!"); } return new ServerAddressIterator(serverUrls); } - class GetServerListTask implements Runnable { final String url; - + GetServerListTask(String url) { this.url = url; } - + @Override - public void run() { - /** - * get serverlist from nameserver - */ + public void run() { + /** + * get serverlist from nameserver + */ try { updateIfChanged(getApacheServerList(url, name)); } catch (Exception e) { - log.error(name, "NACOS-XXXX", "[update-serverlist] failed to update serverlist from address server!", e); + log.error(name, "NACOS-XXXX", "[update-serverlist] failed to update serverlist from address server!", + e); } } } - + private void updateIfChanged(List newList) { if (null == newList || newList.isEmpty()) { - - log.warn("NACOS-0001", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0001", "环境问题","[update-serverlist] current serverlist from address server is empty!!!")); + + log.warn("NACOS-0001", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0001", "环境问题", + "[update-serverlist] current serverlist from address server is empty!!!")); log.warn(name, "[update-serverlist] current serverlist from address server is empty!!!"); return; } - /** - * no change - */ - if (newList.equals(serverUrls)) { + /** + * no change + */ + if (newList.equals(serverUrls)) { return; } serverUrls = new ArrayList(newList); currentServerAddr = iterator().next(); - + EventDispatcher.fireEvent(new ServerlistChangeEvent()); log.info(name, "[update-serverlist] serverlist updated to {}", serverUrls); } @@ -253,38 +254,38 @@ public class ServerListManager { private List getApacheServerList(String url, String name) { try { HttpResult httpResult = HttpSimpleClient.httpGet(url, null, null, null, 3000); - - if (HttpURLConnection.HTTP_OK == httpResult.code) { - if (DEFAULT_NAME.equals(name) ) { - EnvUtil.setSelfEnv(httpResult.headers); - } + + if (HttpURLConnection.HTTP_OK == httpResult.code) { + if (DEFAULT_NAME.equals(name)) { + EnvUtil.setSelfEnv(httpResult.headers); + } List lines = IOUtils.readLines(new StringReader(httpResult.content)); - List result = new ArrayList(lines.size()); - for (String serverAddr : lines) { - if (null == serverAddr || serverAddr.trim().isEmpty()) { - continue; - } else { - String[] ipPort = serverAddr.trim().split(":"); - String ip = ipPort[0].trim(); - if (ipPort.length == 1) { - result.add(ip + ":" + ParamUtil.getDefaultServerPort()); - } else { - result.add(serverAddr); - } - } - } + List result = new ArrayList(lines.size()); + for (String serverAddr : lines) { + if (null == serverAddr || serverAddr.trim().isEmpty()) { + continue; + } else { + String[] ipPort = serverAddr.trim().split(":"); + String ip = ipPort[0].trim(); + if (ipPort.length == 1) { + result.add(ip + ":" + ParamUtil.getDefaultServerPort()); + } else { + result.add(serverAddr); + } + } + } return result; } else { log.error(addressServerUrl, "NACOS-XXXX", "[check-serverlist] error. code={}", httpResult.code); return null; } } catch (IOException e) { - log.error("NACOS-0001", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0001", "环境问题",e.toString())); + log.error("NACOS-0001", LoggerHelper.getErrorCodeStr("NACOS", "NACOS-0001", "环境问题", e.toString())); log.error(addressServerUrl, "NACOS-XXXX", "[check-serverlist] exception. msg={}", e.toString(), e); return null; } } - + String getUrlString() { return serverUrls.toString(); } @@ -294,105 +295,103 @@ public class ServerListManager { String split = ""; for (String serverIp : serverIps) { sb.append(split); - sb.append(serverIp); + sb.append(serverIp.replaceAll(":", "_")); split = "-"; } return sb.toString(); } - @Override + @Override public String toString() { - return "ServerManager-" + name + "-" +getUrlString(); + return "ServerManager-" + name + "-" + getUrlString(); } - public boolean contain(String ip){ - - return serverUrls.contains(ip); - } + public boolean contain(String ip) { - public void refreshCurrentServerAddr() { - currentServerAddr = iterator().next(); - } + return serverUrls.contains(ip); + } - public String getCurrentServerAddr() { - if (StringUtils.isBlank(currentServerAddr)) { - currentServerAddr = iterator().next(); - } - return currentServerAddr; - } + public void refreshCurrentServerAddr() { + currentServerAddr = iterator().next(); + } - - public String getContentPath() { - return contentPath; - } - - public String getName() { - return name; - } - - public String getNamespace() { - return namespace; - } + public String getCurrentServerAddr() { + if (StringUtils.isBlank(currentServerAddr)) { + currentServerAddr = iterator().next(); + } + return currentServerAddr; + } - public String getTenant() { - return tenant; - } - - /** - * 不同环境的名称 - */ - private String name; - private String namespace = ""; - private String tenant = ""; + public String getContentPath() { + return contentPath; + } + + public String getName() { + return name; + } + + public String getNamespace() { + return namespace; + } + + public String getTenant() { + return tenant; + } + + /** + * 不同环境的名称 + */ + private String name; + private String namespace = ""; + private String tenant = ""; static public final String DEFAULT_NAME = "default"; static public final String CUSTOM_NAME = "custom"; static public final String FIXED_NAME = "fixed"; private int initServerlistRetryTimes = 5; - /** - * 和其他server的连接超时和socket超时 - */ + /** + * 和其他server的连接超时和socket超时 + */ static final int TIMEOUT = 5000; - + final boolean isFixed; - boolean isStarted = false; - private String endpoint; - private int endpointPort = 8080; - private String contentPath = ParamUtil.getDefaultContextPath(); - private String serverListName = ParamUtil.getDefaultNodesPath(); + boolean isStarted = false; + private String endpoint; + private int endpointPort = 8080; + private String contentPath = ParamUtil.getDefaultContextPath(); + private String serverListName = ParamUtil.getDefaultNodesPath(); volatile List serverUrls = new ArrayList(); private volatile String currentServerAddr; - - public String serverPort = ParamUtil.getDefaultServerPort(); - + + public String serverPort = ParamUtil.getDefaultServerPort(); + public String addressServerUrl; } - /** - * 对地址列表排序,同机房优先。 + * 对地址列表排序,同机房优先。 */ class ServerAddressIterator implements Iterator { static class RandomizedServerAddress implements Comparable { static Random random = new Random(); - + String serverIp; int priority = 0; int seed; - - public RandomizedServerAddress(String ip) { - try { - this.serverIp = ip; - /** - * change random scope from 32 to Integer.MAX_VALUE to fix load balance issue - */ - this.seed = random.nextInt(Integer.MAX_VALUE); - } catch (Exception e) { - throw new RuntimeException(e); - } - } + + public RandomizedServerAddress(String ip) { + try { + this.serverIp = ip; + /** + * change random scope from 32 to Integer.MAX_VALUE to fix load balance issue + */ + this.seed = random.nextInt(Integer.MAX_VALUE); + } catch (Exception e) { + throw new RuntimeException(e); + } + } @Override @SuppressFBWarnings("EQ_COMPARETO_USE_OBJECT_EQUALS") @@ -425,7 +424,7 @@ class ServerAddressIterator implements Iterator { public void remove() { throw new UnsupportedOperationException(); } - + final List sorted; final Iterator iter; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/SpasAdapter.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/SpasAdapter.java index d3b6ae18a..181c2cc1a 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/SpasAdapter.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/SpasAdapter.java @@ -27,81 +27,79 @@ import java.util.*; /** * 适配spas接口 - * - * @author Nacos * + * @author Nacos */ public class SpasAdapter { - - public static List getSignHeaders(String resource, String secretKey) { - List header = new ArrayList(); - String timeStamp = String.valueOf(System.currentTimeMillis()); - header.add("Timestamp"); - header.add(timeStamp); - if (secretKey != null) { - header.add("Spas-Signature"); - String signature = ""; - if (StringUtils.isBlank(resource)) { - signature = signWithhmacSHA1Encrypt(timeStamp, secretKey); - } else { - signature = signWithhmacSHA1Encrypt(resource + "+" + timeStamp, secretKey); - } - header.add(signature); - } - return header; - } - - - public static List getSignHeaders(List paramValues, String secretKey) { - if (null == paramValues) { - return null; - } - Map signMap = new HashMap(5); - for (Iterator iter = paramValues.iterator(); iter.hasNext();) { - String key = iter.next(); - if (TENANT_KEY.equals(key) || GROUP_KEY.equals(key)) { - signMap.put(key, iter.next()); - } else { - iter.next(); - } - } - String resource = ""; - if (signMap.size() > 1) { - resource = signMap.get(TENANT_KEY) + "+" + signMap.get(GROUP_KEY); - } else { - if (!StringUtils.isBlank(signMap.get(GROUP_KEY))) { - resource = signMap.get(GROUP_KEY); - } - } - return getSignHeaders(resource, secretKey); - } - public static String getSk() { - return CredentialService.getInstance().getCredential().getSecretKey(); - } + public static List getSignHeaders(String resource, String secretKey) { + List header = new ArrayList(); + String timeStamp = String.valueOf(System.currentTimeMillis()); + header.add("Timestamp"); + header.add(timeStamp); + if (secretKey != null) { + header.add("Spas-Signature"); + String signature = ""; + if (StringUtils.isBlank(resource)) { + signature = signWithhmacSHA1Encrypt(timeStamp, secretKey); + } else { + signature = signWithhmacSHA1Encrypt(resource + "+" + timeStamp, secretKey); + } + header.add(signature); + } + return header; + } - public static String getAk() { - return CredentialService.getInstance().getCredential().getAccessKey(); - } - - public static String signWithhmacSHA1Encrypt(String encryptText, String encryptKey) { - try { - byte[] data = encryptKey.getBytes("UTF-8"); - // 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称 - SecretKey secretKey = new SecretKeySpec(data, "HmacSHA1"); - // 生成一个指定 Mac 算法 的 Mac 对象 - Mac mac = Mac.getInstance("HmacSHA1"); - // 用给定密钥初始化 Mac 对象 - mac.init(secretKey); - byte[] text = encryptText.getBytes("UTF-8"); - byte[] textFinal = mac.doFinal(text); - // 完成 Mac 操作, base64编码,将byte数组转换为字符串 - return new String(Base64.encodeBase64(textFinal), Constants.ENCODE); - } catch (Exception e) { - throw new RuntimeException("signWithhmacSHA1Encrypt fail", e); - } - } - - private static String GROUP_KEY = "group"; - private static String TENANT_KEY = "tenant"; + public static List getSignHeaders(List paramValues, String secretKey) { + if (null == paramValues) { + return null; + } + Map signMap = new HashMap(5); + for (Iterator iter = paramValues.iterator(); iter.hasNext(); ) { + String key = iter.next(); + if (TENANT_KEY.equals(key) || GROUP_KEY.equals(key)) { + signMap.put(key, iter.next()); + } else { + iter.next(); + } + } + String resource = ""; + if (signMap.size() > 1) { + resource = signMap.get(TENANT_KEY) + "+" + signMap.get(GROUP_KEY); + } else { + if (!StringUtils.isBlank(signMap.get(GROUP_KEY))) { + resource = signMap.get(GROUP_KEY); + } + } + return getSignHeaders(resource, secretKey); + } + + public static String getSk() { + return CredentialService.getInstance().getCredential().getSecretKey(); + } + + public static String getAk() { + return CredentialService.getInstance().getCredential().getAccessKey(); + } + + public static String signWithhmacSHA1Encrypt(String encryptText, String encryptKey) { + try { + byte[] data = encryptKey.getBytes("UTF-8"); + // 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称 + SecretKey secretKey = new SecretKeySpec(data, "HmacSHA1"); + // 生成一个指定 Mac 算法 的 Mac 对象 + Mac mac = Mac.getInstance("HmacSHA1"); + // 用给定密钥初始化 Mac 对象 + mac.init(secretKey); + byte[] text = encryptText.getBytes("UTF-8"); + byte[] textFinal = mac.doFinal(text); + // 完成 Mac 操作, base64编码,将byte数组转换为字符串 + return new String(Base64.encodeBase64(textFinal), Constants.ENCODE); + } catch (Exception e) { + throw new RuntimeException("signWithhmacSHA1Encrypt fail", e); + } + } + + private static String GROUP_KEY = "group"; + private static String TENANT_KEY = "tenant"; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/TimerService.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/TimerService.java index 3140220ee..88e828ae0 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/TimerService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/TimerService.java @@ -21,28 +21,27 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; - /** * Time Service - * @author Nacos * + * @author Nacos */ public class TimerService { static public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, - long delay, TimeUnit unit) { + long delay, TimeUnit unit) { return scheduledExecutor.scheduleWithFixedDelay(command, initialDelay, delay, unit); } - + @SuppressWarnings("PMD.ThreadPoolCreationRule") static ScheduledExecutorService scheduledExecutor = Executors - .newSingleThreadScheduledExecutor(new ThreadFactory() { - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setName("com.alibaba.nacos.client.Timer"); - t.setDaemon(true); - return t; - } - }); - + .newSingleThreadScheduledExecutor(new ThreadFactory() { + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("com.alibaba.nacos.client.Timer"); + t.setDaemon(true); + return t; + } + }); + } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/listener/impl/PropertiesListener.java b/client/src/main/java/com/alibaba/nacos/client/config/listener/impl/PropertiesListener.java index 724518030..6b047b100 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/listener/impl/PropertiesListener.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/listener/impl/PropertiesListener.java @@ -26,14 +26,13 @@ import java.util.Properties; /** * Properties Listener - * - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") public abstract class PropertiesListener extends AbstractListener { - final static public Logger log = LogUtils.logger(PropertiesListener.class); - + final static public Logger log = LogUtils.logger(PropertiesListener.class); + public void receiveConfigInfo(String configInfo) { if (StringUtils.isEmpty(configInfo)) { return; @@ -43,19 +42,17 @@ public abstract class PropertiesListener extends AbstractListener { try { properties.load(new StringReader(configInfo)); innerReceive(properties); - } - catch (IOException e) { - log.error("NACOS-XXXX","load properties error:" + configInfo, e); + } catch (IOException e) { + log.error("NACOS-XXXX", "load properties error:" + configInfo, e); } } - /** - * properties type for receiver - * - * @param properties - * properties - */ - public abstract void innerReceive(Properties properties); + /** + * properties type for receiver + * + * @param properties properties + */ + public abstract void innerReceive(Properties properties); } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/ConcurrentDiskUtil.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/ConcurrentDiskUtil.java index 595cf601d..599dc0d15 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/ConcurrentDiskUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/ConcurrentDiskUtil.java @@ -30,217 +30,199 @@ import java.nio.charset.CharsetDecoder; /** * concurrent disk util;op file with file lock - * - * @author configCenter * + * @author configCenter */ public class ConcurrentDiskUtil { - /** - * get file content - * - * @param path - * file path - * @param charsetName - * charsetName - * @return content - * @throws IOException - * IOException - */ - public static String getFileContent(String path, String charsetName) - throws IOException { - File file = new File(path); - return getFileContent(file, charsetName); - } + /** + * get file content + * + * @param path file path + * @param charsetName charsetName + * @return content + * @throws IOException IOException + */ + public static String getFileContent(String path, String charsetName) + throws IOException { + File file = new File(path); + return getFileContent(file, charsetName); + } - /** - * get file content - * - * @param file - * file - * @param charsetName - * charsetName - * @return content - * @throws IOException - * IOException - */ - public static String getFileContent(File file, String charsetName) - throws IOException { - RandomAccessFile fis = null; - FileLock rlock = null; - try { - fis = new RandomAccessFile(file, "r"); - FileChannel fcin = fis.getChannel(); - int i = 0; - do { - try { - rlock = fcin.tryLock(0L, Long.MAX_VALUE, true); - } catch (Exception e) { - ++i; - if (i > RETRY_COUNT) { - log.error("read {} fail;retryed time:{}", - file.getName(), i); - throw new IOException("read " + file.getAbsolutePath() - + " conflict"); - } - sleep(SLEEP_BASETIME * i); - log.warn("read {} conflict;retry time:{}", file.getName(), - i); - } - } while (null == rlock); - int fileSize = (int) fcin.size(); - ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize); - fcin.read(byteBuffer); - byteBuffer.flip(); - return byteBufferToString(byteBuffer, charsetName); - } finally { - if (rlock != null) { - rlock.release(); - rlock = null; - } - if (fis != null) { - fis.close(); - fis = null; - } - } - } + /** + * get file content + * + * @param file file + * @param charsetName charsetName + * @return content + * @throws IOException IOException + */ + public static String getFileContent(File file, String charsetName) + throws IOException { + RandomAccessFile fis = null; + FileLock rlock = null; + try { + fis = new RandomAccessFile(file, "r"); + FileChannel fcin = fis.getChannel(); + int i = 0; + do { + try { + rlock = fcin.tryLock(0L, Long.MAX_VALUE, true); + } catch (Exception e) { + ++i; + if (i > RETRY_COUNT) { + log.error("read {} fail;retryed time:{}", + file.getName(), i); + throw new IOException("read " + file.getAbsolutePath() + + " conflict"); + } + sleep(SLEEP_BASETIME * i); + log.warn("read {} conflict;retry time:{}", file.getName(), + i); + } + } while (null == rlock); + int fileSize = (int)fcin.size(); + ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize); + fcin.read(byteBuffer); + byteBuffer.flip(); + return byteBufferToString(byteBuffer, charsetName); + } finally { + if (rlock != null) { + rlock.release(); + rlock = null; + } + if (fis != null) { + fis.close(); + fis = null; + } + } + } - /** - * write file content - * - * @param path - * file path - * @param content - * content - * @param charsetName - * charsetName - * @return whether write ok - * @throws IOException - * IOException - */ - public static Boolean writeFileContent(String path, String content, - String charsetName) throws IOException { - File file = new File(path); - return writeFileContent(file, content, charsetName); - } + /** + * write file content + * + * @param path file path + * @param content content + * @param charsetName charsetName + * @return whether write ok + * @throws IOException IOException + */ + public static Boolean writeFileContent(String path, String content, + String charsetName) throws IOException { + File file = new File(path); + return writeFileContent(file, content, charsetName); + } - /** - * write file content - * - * @param file - * file - * @param content - * content - * @param charsetName - * charsetName - * @return whether write ok - * @throws IOException - * IOException - */ - public static Boolean writeFileContent(File file, String content, - String charsetName) throws IOException { - if (!file.exists()) { - boolean isCreateOk = file.createNewFile(); - if (!isCreateOk) { - return false; - } - } - FileChannel channel = null; - FileLock lock = null; - RandomAccessFile raf = null; - try { - raf = new RandomAccessFile(file, "rw"); - channel = raf.getChannel(); - int i = 0; - do { - try { - lock = channel.tryLock(); - } catch (Exception e) { - ++i; - if (i > RETRY_COUNT) { - log.error("write {} fail;retryed time:{}", - file.getName(), i); - throw new IOException("write " + file.getAbsolutePath() - + " conflict"); - } - sleep(SLEEP_BASETIME * i); - log.warn("write {} conflict;retry time:{}", file.getName(), - i); - } - } while (null == lock); + /** + * write file content + * + * @param file file + * @param content content + * @param charsetName charsetName + * @return whether write ok + * @throws IOException IOException + */ + public static Boolean writeFileContent(File file, String content, + String charsetName) throws IOException { + if (!file.exists()) { + boolean isCreateOk = file.createNewFile(); + if (!isCreateOk) { + return false; + } + } + FileChannel channel = null; + FileLock lock = null; + RandomAccessFile raf = null; + try { + raf = new RandomAccessFile(file, "rw"); + channel = raf.getChannel(); + int i = 0; + do { + try { + lock = channel.tryLock(); + } catch (Exception e) { + ++i; + if (i > RETRY_COUNT) { + log.error("write {} fail;retryed time:{}", + file.getName(), i); + throw new IOException("write " + file.getAbsolutePath() + + " conflict"); + } + sleep(SLEEP_BASETIME * i); + log.warn("write {} conflict;retry time:{}", file.getName(), + i); + } + } while (null == lock); - ByteBuffer sendBuffer = ByteBuffer.wrap(content - .getBytes(charsetName)); - while (sendBuffer.hasRemaining()) { - channel.write(sendBuffer); - } - channel.truncate(content.length()); - } catch (FileNotFoundException e) { - throw new IOException("file not exist"); - } finally { - if (lock != null) { - try { - lock.release(); - lock = null; - } catch (IOException e) { - log.warn("close wrong", e); - } - } - if (channel != null) { - try { - channel.close(); - channel = null; - } catch (IOException e) { - log.warn("close wrong", e); - } - } - if (raf != null) { - try { - raf.close(); - raf = null; - } catch (IOException e) { - log.warn("close wrong", e); - } - } + ByteBuffer sendBuffer = ByteBuffer.wrap(content + .getBytes(charsetName)); + while (sendBuffer.hasRemaining()) { + channel.write(sendBuffer); + } + channel.truncate(content.length()); + } catch (FileNotFoundException e) { + throw new IOException("file not exist"); + } finally { + if (lock != null) { + try { + lock.release(); + lock = null; + } catch (IOException e) { + log.warn("close wrong", e); + } + } + if (channel != null) { + try { + channel.close(); + channel = null; + } catch (IOException e) { + log.warn("close wrong", e); + } + } + if (raf != null) { + try { + raf.close(); + raf = null; + } catch (IOException e) { + log.warn("close wrong", e); + } + } - } - return true; - } + } + return true; + } - /** - * transfer ByteBuffer to String - * - * @param buffer - * buffer - * @param charsetName - * charsetName - * @return String - * @throws IOException - * IOException - */ - public static String byteBufferToString(ByteBuffer buffer, - String charsetName) throws IOException { - Charset charset = null; - CharsetDecoder decoder = null; - CharBuffer charBuffer = null; - charset = Charset.forName(charsetName); - decoder = charset.newDecoder(); - charBuffer = decoder.decode(buffer.asReadOnlyBuffer()); - return charBuffer.toString(); - } + /** + * transfer ByteBuffer to String + * + * @param buffer buffer + * @param charsetName charsetName + * @return String + * @throws IOException IOException + */ + public static String byteBufferToString(ByteBuffer buffer, + String charsetName) throws IOException { + Charset charset = null; + CharsetDecoder decoder = null; + CharBuffer charBuffer = null; + charset = Charset.forName(charsetName); + decoder = charset.newDecoder(); + charBuffer = decoder.decode(buffer.asReadOnlyBuffer()); + return charBuffer.toString(); + } - private static void sleep(int time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - log.warn("sleep wrong", e); - } - } + private static void sleep(int time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + log.warn("sleep wrong", e); + } + } - static final public Logger log = LogUtils.logger(ConcurrentDiskUtil.class); - static final int RETRY_COUNT = 10; - /** - * ms - */ - static final int SLEEP_BASETIME = 10; + static final public Logger log = LogUtils.logger(ConcurrentDiskUtil.class); + static final int RETRY_COUNT = 10; + /** + * ms + */ + static final int SLEEP_BASETIME = 10; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/ContentUtils.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/ContentUtils.java index 42cb5c001..1e9497c8c 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/ContentUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/ContentUtils.java @@ -21,9 +21,8 @@ import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR; /** * Content Util - * - * @author Nacos * + * @author Nacos */ public class ContentUtils { @@ -43,7 +42,6 @@ public class ContentUtils { } } - public static String getContentIdentity(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { @@ -52,7 +50,6 @@ public class ContentUtils { return content.substring(0, index); } - public static String getContent(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { @@ -61,18 +58,15 @@ public class ContentUtils { return content.substring(index + 1); } - public static String truncateContent(String content) { if (content == null) { return ""; - } - else if (content.length() <= SHOW_CONTENT_SIZE) { + } else if (content.length() <= SHOW_CONTENT_SIZE) { return content; - } - else { - return content.substring(0, SHOW_CONTENT_SIZE) + "..."; + } else { + return content.substring(0, SHOW_CONTENT_SIZE) + "..."; } } - private static int SHOW_CONTENT_SIZE = 100; + private static int SHOW_CONTENT_SIZE = 100; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/IOUtils.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/IOUtils.java index 3d920b0b1..62ac7e2c6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/IOUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/IOUtils.java @@ -21,21 +21,19 @@ import java.io.*; import java.util.ArrayList; import java.util.List; - /** * IO Util - * - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class IOUtils { static public String toString(InputStream input, String encoding) throws IOException { return (null == encoding) ? toString(new InputStreamReader(input, Constants.ENCODE)) - : toString(new InputStreamReader(input, encoding)); + : toString(new InputStreamReader(input, encoding)); } - + static public String toString(Reader reader) throws IOException { CharArrayWriter sw = new CharArrayWriter(); copy(reader, sw); @@ -45,7 +43,7 @@ public class IOUtils { static public long copy(Reader input, Writer output) throws IOException { char[] buffer = new char[1 << 12]; long count = 0; - for (int n = 0; (n = input.read(buffer)) >= 0;) { + for (int n = 0; (n = input.read(buffer)) >= 0; ) { output.write(buffer, 0, n); count += n; } @@ -59,7 +57,7 @@ public class IOUtils { BufferedReader reader = toBufferedReader(input); List list = new ArrayList(); String line = null; - for (;;) { + for (; ; ) { line = reader.readLine(); if (null != line) { list.add(line); @@ -71,8 +69,8 @@ public class IOUtils { } static private BufferedReader toBufferedReader(Reader reader) { - return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader( - reader); + return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader( + reader); } public static void delete(File fileOrDir) throws IOException { @@ -82,16 +80,16 @@ public class IOUtils { if (fileOrDir.isDirectory()) { cleanDirectory(fileOrDir); - } else { - if (fileOrDir.exists()) { - boolean isDeleteOk = fileOrDir.delete(); - if (!isDeleteOk) { - throw new IOException("delete fail"); - } - } - } + } else { + if (fileOrDir.exists()) { + boolean isDeleteOk = fileOrDir.delete(); + if (!isDeleteOk) { + throw new IOException("delete fail"); + } + } + } } - + /** * 清理目录下的内容 */ @@ -107,10 +105,10 @@ public class IOUtils { } File[] files = directory.listFiles(); - /** - * null if security restricted - */ - if (files == null) { + /** + * null if security restricted + */ + if (files == null) { throw new IOException("Failed to list contents of " + directory); } @@ -129,7 +127,7 @@ public class IOUtils { } public static void writeStringToFile(File file, String data, String encoding) - throws IOException { + throws IOException { OutputStream os = null; try { os = new FileOutputStream(file); diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/JVMUtil.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/JVMUtil.java index 7f6ed199f..cb5aa117f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/JVMUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/JVMUtil.java @@ -19,31 +19,30 @@ import com.alibaba.nacos.client.logger.Logger; /** * Get jvm config - * - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class JVMUtil { - /** - * whether is multi instance - * - * @return whether multi - */ - public static Boolean isMultiInstance() { - return isMultiInstance; - } + /** + * whether is multi instance + * + * @return whether multi + */ + public static Boolean isMultiInstance() { + return isMultiInstance; + } - private static Boolean isMultiInstance = false; - private static String TRUE = "true"; - static final public Logger log = LogUtils.logger(JVMUtil.class); + private static Boolean isMultiInstance = false; + private static String TRUE = "true"; + static final public Logger log = LogUtils.logger(JVMUtil.class); - static { - String multiDeploy = System.getProperty("isMultiInstance", "false"); - if (TRUE.equals(multiDeploy)) { - isMultiInstance = true; - } - log.info("isMultiInstance:{}", isMultiInstance); - } + static { + String multiDeploy = System.getProperty("isMultiInstance", "false"); + if (TRUE.equals(multiDeploy)) { + isMultiInstance = true; + } + log.info("isMultiInstance:{}", isMultiInstance); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/LogUtils.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/LogUtils.java index 43de7b504..a52d3547f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/LogUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/LogUtils.java @@ -22,38 +22,38 @@ import com.alibaba.nacos.client.logger.LoggerFactory; /** * Log Util - * - * @author Nacos * + * @author Nacos */ public class LogUtils { - static int JM_LOG_RETAIN_COUNT = 7; - static String JM_LOG_FILE_SIZE = "10MB"; + static int JM_LOG_RETAIN_COUNT = 7; + static String JM_LOG_FILE_SIZE = "10MB"; static { - String tmp = "7"; + String tmp = "7"; try { - /** - * change timeout from 100 to 200 - */ - tmp = System.getProperty("JM.LOG.RETAIN.COUNT","7"); + /** + * change timeout from 100 to 200 + */ + tmp = System.getProperty("JM.LOG.RETAIN.COUNT", "7"); JM_LOG_RETAIN_COUNT = Integer.parseInt(tmp); } catch (NumberFormatException e) { - e.printStackTrace(); - throw e; + e.printStackTrace(); + throw e; } - - JM_LOG_FILE_SIZE = System.getProperty("JM.LOG.FILE.SIZE","10MB"); - + + JM_LOG_FILE_SIZE = System.getProperty("JM.LOG.FILE.SIZE", "10MB"); + // logger init Logger logger = LoggerFactory.getLogger("com.alibaba.nacos.client.config"); logger.setLevel(Level.INFO); logger.setAdditivity(false); - logger.activateAppenderWithSizeRolling("nacos", "config.log", Constants.ENCODE, JM_LOG_FILE_SIZE, JM_LOG_RETAIN_COUNT); + logger.activateAppenderWithSizeRolling("nacos", "config.log", Constants.ENCODE, JM_LOG_FILE_SIZE, + JM_LOG_RETAIN_COUNT); } - public static Logger logger(Class clazz) { - return LoggerFactory.getLogger(clazz); - } + public static Logger logger(Class clazz) { + return LoggerFactory.getLogger(clazz); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/MD5.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/MD5.java index c3550359f..fced89bf8 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/MD5.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/MD5.java @@ -25,16 +25,16 @@ import java.util.concurrent.locks.ReentrantLock; /** * MD5 util - * - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class MD5 { - private static int DIGITS_SIZE = 16; - private static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + private static int DIGITS_SIZE = 16; + private static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static Map rDigits = new HashMap(16); + static { for (int i = 0; i < digits.length; ++i) { rDigits.put(digits[i], i); @@ -45,41 +45,34 @@ public class MD5 { private MessageDigest mHasher; private ReentrantLock opLock = new ReentrantLock(); - private MD5() { try { mHasher = MessageDigest.getInstance("md5"); - } - catch (Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } - public static MD5 getInstance() { return me; } - public String getMD5String(String content) { return bytes2string(hash(content)); } - public String getMD5String(byte[] content) { return bytes2string(hash(content)); } - public byte[] getMD5Bytes(byte[] content) { return hash(content); } - /** * 对字符串进行md5 - * + * * @param str * @return md5 byte[16] */ @@ -91,20 +84,17 @@ public class MD5 { throw new IllegalArgumentException("md5 need"); } return bt; - } - catch (UnsupportedEncodingException e) { + } catch (UnsupportedEncodingException e) { throw new RuntimeException("unsupported utf-8 encoding", e); - } - finally { + } finally { opLock.unlock(); } } - /** * 对二进制数据进行md5 - * - * @param str + * + * @param data * @return md5 byte[16] */ public byte[] hash(byte[] data) { @@ -115,16 +105,14 @@ public class MD5 { throw new IllegalArgumentException("md5 need"); } return bt; - } - finally { + } finally { opLock.unlock(); } } - /** * 将一个字节数组转化为可见的字符串 - * + * * @param bt * @return */ @@ -141,5 +129,4 @@ public class MD5 { return new String(out); } - } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java index 397ebbf47..0112fa180 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java @@ -23,18 +23,16 @@ import com.alibaba.nacos.client.utils.StringUtils; /** * Param check util - * + * * @author Nacos - * */ public class ParamUtils { - private static char[] validChars = new char[] { '_', '-', '.', ':' }; - + private static char[] validChars = new char[] {'_', '-', '.', ':'}; /** * 白名单的方式检查, 合法的参数只能包含字母、数字、以及validChars中的字符, 并且不能为空 - * + * * @param param * @return */ @@ -47,18 +45,15 @@ public class ParamUtils { char ch = param.charAt(i); if (Character.isLetterOrDigit(ch)) { continue; - } - else if (isValidChar(ch)) { + } else if (isValidChar(ch)) { continue; - } - else { + } else { return false; } } return true; } - private static boolean isValidChar(char ch) { for (char c : validChars) { if (c == ch) { @@ -67,88 +62,88 @@ public class ParamUtils { } return false; } - - public static void checkKeyParam(String dataId, String group) throws NacosException { - if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); - } - if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); - } - } - - public static void checkTDG(String tenant, String dataId, String group) throws NacosException { - checkTenant(tenant); - if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); - } - if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); - } - } - - public static void checkKeyParam(String dataId, String group, String datumId) - throws NacosException { - if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); - } - if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); - } - if (StringUtils.isBlank(datumId) || !ParamUtils.isValid(datumId)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "datumId invalid"); - } - } - - public static void checkKeyParam(List dataIds, String group) throws NacosException { - if (dataIds == null || dataIds.size() == 0) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataIds invalid"); - } - for (String dataId : dataIds) { - if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); - } - } - if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); - } - } - public static void checkParam(String dataId, String group, String content) throws NacosException { - checkKeyParam(dataId, group); - if (StringUtils.isBlank(content)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "content invalid"); - } - } - - public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { - checkKeyParam(dataId, group, datumId); - if (StringUtils.isBlank(content)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "content invalid"); - } - } + public static void checkKeyParam(String dataId, String group) throws NacosException { + if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); + } + if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); + } + } - public static void checkTenant(String tenant) throws NacosException { - if (StringUtils.isBlank(tenant) || !ParamUtils.isValid(tenant)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "tenant invalid"); - } - } - - public static void checkBetaIps(String betaIps) throws NacosException { - if (StringUtils.isBlank(betaIps)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "betaIps invalid"); - } - String[] ipsArr = betaIps.split(","); - for (String ip : ipsArr) { - if (!IPUtil.isIPV4(ip)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "betaIps invalid"); - } - } - } + public static void checkTDG(String tenant, String dataId, String group) throws NacosException { + checkTenant(tenant); + if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); + } + if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); + } + } - public static void checkContent(String content) throws NacosException { - if (StringUtils.isBlank(content)) { - throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "content invalid"); - } - } + public static void checkKeyParam(String dataId, String group, String datumId) + throws NacosException { + if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); + } + if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); + } + if (StringUtils.isBlank(datumId) || !ParamUtils.isValid(datumId)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "datumId invalid"); + } + } + + public static void checkKeyParam(List dataIds, String group) throws NacosException { + if (dataIds == null || dataIds.size() == 0) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataIds invalid"); + } + for (String dataId : dataIds) { + if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataId invalid"); + } + } + if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "group invalid"); + } + } + + public static void checkParam(String dataId, String group, String content) throws NacosException { + checkKeyParam(dataId, group); + if (StringUtils.isBlank(content)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "content invalid"); + } + } + + public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { + checkKeyParam(dataId, group, datumId); + if (StringUtils.isBlank(content)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "content invalid"); + } + } + + public static void checkTenant(String tenant) throws NacosException { + if (StringUtils.isBlank(tenant) || !ParamUtils.isValid(tenant)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "tenant invalid"); + } + } + + public static void checkBetaIps(String betaIps) throws NacosException { + if (StringUtils.isBlank(betaIps)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "betaIps invalid"); + } + String[] ipsArr = betaIps.split(","); + for (String ip : ipsArr) { + if (!IPUtil.isIPV4(ip)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "betaIps invalid"); + } + } + } + + public static void checkContent(String content) throws NacosException { + if (StringUtils.isBlank(content)) { + throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "content invalid"); + } + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/SnapShotSwitch.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/SnapShotSwitch.java index bba324cb5..cab5ea88d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/SnapShotSwitch.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/SnapShotSwitch.java @@ -19,24 +19,23 @@ import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; /** * Snapshot switch - * - * @author Nacos * + * @author Nacos */ public class SnapShotSwitch { - /** - * whether use local cache - */ - private static Boolean isSnapShot = true; + /** + * whether use local cache + */ + private static Boolean isSnapShot = true; - public static Boolean getIsSnapShot() { - return isSnapShot; - } + public static Boolean getIsSnapShot() { + return isSnapShot; + } + + public static void setIsSnapShot(Boolean isSnapShot) { + SnapShotSwitch.isSnapShot = isSnapShot; + LocalConfigInfoProcessor.cleanAllSnapshot(); + } - public static void setIsSnapShot(Boolean isSnapShot) { - SnapShotSwitch.isSnapShot = isSnapShot; - LocalConfigInfoProcessor.cleanAllSnapshot(); - } - } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/utils/TenantUtil.java b/client/src/main/java/com/alibaba/nacos/client/config/utils/TenantUtil.java index eb7cac9f5..8924b25f7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/utils/TenantUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/utils/TenantUtil.java @@ -19,26 +19,25 @@ import com.alibaba.nacos.client.utils.StringUtils; /** * Tenant Util - * - * @author Nacos * + * @author Nacos */ public class TenantUtil { - static String userTenant = ""; - - static { - userTenant = System.getProperty("tenant.id", ""); - if (StringUtils.isBlank(userTenant)) { - userTenant = System.getProperty("acm.namespace", ""); - } - } + static String userTenant = ""; - public static String getUserTenant() { - return userTenant; - } + static { + userTenant = System.getProperty("tenant.id", ""); + if (StringUtils.isBlank(userTenant)) { + userTenant = System.getProperty("acm.namespace", ""); + } + } - public static void setUserTenant(String userTenant) { - TenantUtil.userTenant = userTenant; - } + public static String getUserTenant() { + return userTenant; + } + + public static void setUserTenant(String userTenant) { + TenantUtil.userTenant = userTenant; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/Base64.java b/client/src/main/java/com/alibaba/nacos/client/identify/Base64.java index 57877a30f..1829b0c14 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/Base64.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/Base64.java @@ -1,9 +1,10 @@ /* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -14,694 +15,622 @@ * limitations under the License. */ package com.alibaba.nacos.client.identify; -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ import java.io.UnsupportedEncodingException; /** -* Provides Base64 encoding and decoding as defined by RFC 2045. -* -*

-* This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose -* Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. -*

-*

-* The class can be parameterized in the following manner with various constructors: -*

    -*
  • URL-safe mode: Default off.
  • -*
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of -* 4 in the encoded data. -*
  • Line separator: Default is CRLF ("\r\n")
  • -*
-*

-*

-* Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode -* character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). -*

-*

-* This class is not thread-safe. Each thread should use its own instance. -*

-* -* @see RFC 2045 -* @author Apache Software Foundation -* @since 1.0 -* @version $Revision: 1080712 $ -*/ + * Provides Base64 encoding and decoding as defined by RFC 2045. + *

+ *

This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein.

+ * The class can be parameterized in the following manner with various constructors:

  • URL-safe mode: Default + * off.
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being + * multiples of 4 in the encoded data.
  • Line separator: Default is CRLF ("\r\n")

Since this class + * operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode character + * encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).

This + * class is not thread-safe. Each thread should use its own instance.

+ * + * @author Apache Software Foundation + * @version $Revision: 1080712 $ + * @see RFC 2045 + * @since 1.0 + */ public class Base64 { - /** - * BASE32 characters are 6 bits in length. - * They are formed by taking a block of 3 octets to form a 24-bit string, - * which is converted into 4 BASE64 characters. - */ - private static final int BITS_PER_ENCODED_BYTE = 6; - private static final int BYTES_PER_UNENCODED_BLOCK = 3; - private static final int BYTES_PER_ENCODED_BLOCK = 4; + /** + * BASE32 characters are 6 bits in length. They are formed by taking a block of 3 octets to form a 24-bit string, + * which is converted into 4 BASE64 characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 6; + private static final int BYTES_PER_UNENCODED_BLOCK = 3; + private static final int BYTES_PER_ENCODED_BLOCK = 4; - /** - * Chunk separator per RFC 2045 section 2.1. - * - *

- * N.B. The next major release may break compatibility and make this field private. - *

- * - * @see RFC 2045 section 2.1 - */ - static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + /** + * Chunk separator per RFC 2045 section 2.1. + *

+ *

N.B. The next major release may break compatibility and make this field private.

+ * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; - /** - * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" - * equivalents as specified in Table 1 of RFC 2045. - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] STANDARD_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; + /** + * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" + * equivalents as specified in Table 1 of RFC 2045. + *

+ * Thanks to "commons" project in ws.apache.org for this code. http://svn.apache + * .org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; - /** - * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / - * changed to - and _ to make the encoded Base64 results more URL-SAFE. - * This table is only used when the Base64's mode is set to URL-SAFE. - */ - private static final byte[] URL_SAFE_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed to - and _ to make the encoded Base64 + * results more URL-SAFE. This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; - /** - * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in - * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 - * alphabet but fall within the bounds of the array are translated to -1. - * - * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both - * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] DECODE_TABLE = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 - }; + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in + * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 + * alphabet but fall within the bounds of the array are translated to -1. + *

+ * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both + * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). + *

+ * Thanks to "commons" project in ws.apache.org for this code. http://svn.apache + * .org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; - /** - * Base64 uses 6-bit fields. - */ - /** Mask used to extract 6 bits, used when encoding */ - private static final int MASK_6BITS = 0x3f; + /** + * Base64 uses 6-bit fields. + */ + /** + * Mask used to extract 6 bits, used when encoding + */ + private static final int MASK_6BITS = 0x3f; - // The static final fields above are used for the original static byte[] methods on Base64. - // The private member fields below are used with the new streaming approach, which requires - // some state be preserved between calls of encode() and decode(). + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). - /** - * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able - * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch - * between the two modes. - */ - private final byte[] encodeTable; + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able + * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; - /** - * Only one decode table currently; keep for consistency with Base32 code - */ - private final byte[] decodeTable = DECODE_TABLE; + /** + * Only one decode table currently; keep for consistency with Base32 code + */ + private final byte[] decodeTable = DECODE_TABLE; - /** - * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. - */ - private final byte[] lineSeparator; + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. - * decodeSize = 3 + lineSeparator.length; - */ - private final int decodeSize; + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. - * encodeSize = 4 + lineSeparator.length; - */ - private final int encodeSize; + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; - /** - * Place holder for the bytes we're dealing with for our based logic. - * Bitwise operations store and extract the encoding or decoding from this variable. - */ - private int bitWorkArea; + /** + * Place holder for the bytes we're dealing with for our based logic. Bitwise operations store and extract the + * encoding or decoding from this variable. + */ + private int bitWorkArea; - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- */ - public Base64() { - this(0, CHUNK_SEPARATOR, false); - } + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.

When encoding the line + * length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.

+ *

+ *

When decoding all variants are supported.

+ */ + public Base64() { + this(0, CHUNK_SEPARATOR, false); + } - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). - * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @param urlSafe - * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode - * operations. Decoding seamlessly handles both modes. - * @throws IllegalArgumentException - * The provided lineSeparator included some base64 characters. That's not going to work! - * @since 1.4 - */ - public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { - chunkSeparatorLength = lineSeparator == null ? 0 : lineSeparator.length; - unencodedBlockSize = BYTES_PER_UNENCODED_BLOCK; - encodedBlockSize = BYTES_PER_ENCODED_BLOCK; - this.lineLength = (lineLength > 0 && chunkSeparatorLength > 0) ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; - // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 - // @see test case Base64Test.testConstructors() - if (lineSeparator != null) { - if (containsAlphabetOrPad(lineSeparator)) { - String sep = null; - try { - sep = new String(lineSeparator, "UTF-8"); - } catch (UnsupportedEncodingException e) { - } - throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); - } - if (lineLength > 0){ - this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; - this.lineSeparator = new byte[lineSeparator.length]; - System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); - } else { - this.encodeSize = BYTES_PER_ENCODED_BLOCK; - this.lineSeparator = null; - } - } else { - this.encodeSize = BYTES_PER_ENCODED_BLOCK; - this.lineSeparator = null; - } - this.decodeSize = this.encodeSize - 1; - this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; - } + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.

When encoding the line + * length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE.

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

When decoding all variants are supported.

+ * + * @param lineLength Each line of encoded data will be at most of the given length (rounded down to nearest + * multiple of 4). If lineLength <= 0, then the output will not be divided into lines (chunks). + * Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to + * encode operations. Decoding seamlessly handles both modes. + * @throws IllegalArgumentException The provided lineSeparator included some base64 characters. That's not going to + * work! + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { + chunkSeparatorLength = lineSeparator == null ? 0 : lineSeparator.length; + unencodedBlockSize = BYTES_PER_UNENCODED_BLOCK; + encodedBlockSize = BYTES_PER_ENCODED_BLOCK; + this.lineLength = (lineLength > 0 && chunkSeparatorLength > 0) ? (lineLength / encodedBlockSize) + * encodedBlockSize : 0; + // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 + // @see test case Base64Test.testConstructors() + if (lineSeparator != null) { + if (containsAlphabetOrPad(lineSeparator)) { + String sep = null; + try { + sep = new String(lineSeparator, "UTF-8"); + } catch (UnsupportedEncodingException e) { + } + throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); + } + if (lineLength > 0) { + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } - /** - *

- * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with - * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last - * remaining bytes (if not multiple of 3). - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in - * byte[] array of binary data to base64 encode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - */ - void encode(byte[] in, int inPos, int inAvail) { - if (eof) { - return; - } - if (inAvail < 0) { - eof = true; - if (0 == modulus && lineLength == 0) { - return; - } - ensureBufferSize(encodeSize); - int savedPos = pos; - switch (modulus) { - case 1 : - buffer[pos++] = encodeTable[(bitWorkArea >> 2) & MASK_6BITS]; - buffer[pos++] = encodeTable[(bitWorkArea << 4) & MASK_6BITS]; - - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[pos++] = PAD; - buffer[pos++] = PAD; - } - break; + /** + *

Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once + * with the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush + * last remaining bytes (if not multiple of 3).

Thanks to "commons" project in ws.apache.org for the + * bitwise operations, and general approach. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of binary data to base64 encode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + */ + void encode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + if (0 == modulus && lineLength == 0) { + return; + } + ensureBufferSize(encodeSize); + int savedPos = pos; + switch (modulus) { + case 1: + buffer[pos++] = encodeTable[(bitWorkArea >> 2) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea << 4) & MASK_6BITS]; - case 2 : - buffer[pos++] = encodeTable[(bitWorkArea >> 10) & MASK_6BITS]; - buffer[pos++] = encodeTable[(bitWorkArea >> 4) & MASK_6BITS]; - buffer[pos++] = encodeTable[(bitWorkArea << 2) & MASK_6BITS]; - - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[pos++] = PAD; - } - break; - default: - break; - } - currentLinePos += pos - savedPos; - /** - * if currentPos == 0 we are at the start of a line, so don't add CRLF - */ - if (lineLength > 0 && currentLinePos > 0) { - System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); - pos += lineSeparator.length; - } - } else { - for (int i = 0; i < inAvail; i++) { - ensureBufferSize(encodeSize); - modulus = (modulus+1) % BYTES_PER_UNENCODED_BLOCK; - int b = in[inPos++]; - if (b < 0) { - b += 256; - } - bitWorkArea = (bitWorkArea << 8) + b; - if (0 == modulus) { - buffer[pos++] = encodeTable[(bitWorkArea >> 18) & MASK_6BITS]; - buffer[pos++] = encodeTable[(bitWorkArea >> 12) & MASK_6BITS]; - buffer[pos++] = encodeTable[(bitWorkArea >> 6) & MASK_6BITS]; - buffer[pos++] = encodeTable[bitWorkArea & MASK_6BITS]; - currentLinePos += BYTES_PER_ENCODED_BLOCK; - if (lineLength > 0 && lineLength <= currentLinePos) { - System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); - pos += lineSeparator.length; - currentLinePos = 0; - } - } - } - } - } + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + buffer[pos++] = PAD; + } + break; - /** - *

- * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once - * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" - * call is not necessary when decoding, but it doesn't hurt, either. - *

- *

- * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are - * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, - * garbage-out philosophy: it will not check the provided data for validity. - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in - * byte[] array of ascii data to base64 decode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - */ - void decode(byte[] in, int inPos, int inAvail) { - if (eof) { - return; - } - if (inAvail < 0) { - eof = true; - } - for (int i = 0; i < inAvail; i++) { - ensureBufferSize(decodeSize); - byte b = in[inPos++]; - if (b == PAD) { - // We're done. - eof = true; - break; - } else { - if (b >= 0 && b < DECODE_TABLE.length) { - int result = DECODE_TABLE[b]; - if (result >= 0) { - modulus = (modulus+1) % BYTES_PER_ENCODED_BLOCK; - bitWorkArea = (bitWorkArea << BITS_PER_ENCODED_BYTE) + result; - if (modulus == 0) { - buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); - buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); - buffer[pos++] = (byte) (bitWorkArea & MASK_8BITS); - } - } - } - } - } + case 2: + buffer[pos++] = encodeTable[(bitWorkArea >> 10) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 4) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea << 2) & MASK_6BITS]; - // Two forms of EOF as far as base64 decoder is concerned: actual - // EOF (-1) and first time '=' character is encountered in stream. - // This approach makes the '=' padding characters completely optional. - if (eof && modulus != 0) { - ensureBufferSize(decodeSize); - - // We have some spare bits remaining - // Output all whole multiples of 8 bits and ignore the rest - switch (modulus) { - // case 1: // 6 bits - ignore entirely - // break; - case 2 : - bitWorkArea = bitWorkArea >> 4; - buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); - break; - case 3 : - bitWorkArea = bitWorkArea >> 2; - buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); - buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); - break; - default: - break; - } - } - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * @param binaryData - * binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - */ - public static byte[] encodeBase64(byte[] binaryData) { - return encodeBase64(binaryData, false, false, Integer.MAX_VALUE); - } + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + } + break; + default: + break; + } + currentLinePos += pos - savedPos; + /** + * if currentPos == 0 we are at the start of a line, so don't add CRLF + */ + if (lineLength > 0 && currentLinePos > 0) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(encodeSize); + modulus = (modulus + 1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + bitWorkArea = (bitWorkArea << 8) + b; + if (0 == modulus) { + buffer[pos++] = encodeTable[(bitWorkArea >> 18) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 12) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 6) & MASK_6BITS]; + buffer[pos++] = encodeTable[bitWorkArea & MASK_6BITS]; + currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if true this encoder will emit - and _ instead of the usual + and / characters. - * @param maxResultSize - * The maximum result size to accept. - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than maxResultSize - * @since 1.4 - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { - if (binaryData == null || binaryData.length == 0) { - return binaryData; - } + /** + *

Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either.

Ignores all non-base64 characters. + * This is how chunked (e.g. 76 character) data is handled, since CR and LF are silently ignored, but has + * implications for other bytes, too. This method subscribes to the garbage-in, garbage-out philosophy: it will not + * check the provided data for validity.

Thanks to "commons" project in ws.apache.org for the bitwise + * operations, and general approach. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/

+ * + * @param in byte[] array of ascii data to base64 decode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + */ + void decode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(decodeSize); + byte b = in[inPos++]; + if (b == PAD) { + // We're done. + eof = true; + break; + } else { + if (b >= 0 && b < DECODE_TABLE.length) { + int result = DECODE_TABLE[b]; + if (result >= 0) { + modulus = (modulus + 1) % BYTES_PER_ENCODED_BLOCK; + bitWorkArea = (bitWorkArea << BITS_PER_ENCODED_BYTE) + result; + if (modulus == 0) { + buffer[pos++] = (byte)((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte)((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte)(bitWorkArea & MASK_8BITS); + } + } + } + } + } - // Create this so can use the super-class method - // Also ensures that the same roundings are performed by the ctor and the code - Base64 b64 = isChunked ? new Base64(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); - long len = b64.getEncodedLength(binaryData); - if (len > maxResultSize) { - throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + - len + - ") than the specified maximum size of " + - maxResultSize); - } - - return b64.encode(binaryData); - } + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (eof && modulus != 0) { + ensureBufferSize(decodeSize); + // We have some spare bits remaining + // Output all whole multiples of 8 bits and ignore the rest + switch (modulus) { + // case 1: // 6 bits - ignore entirely + // break; + case 2: + bitWorkArea = bitWorkArea >> 4; + buffer[pos++] = (byte)((bitWorkArea) & MASK_8BITS); + break; + case 3: + bitWorkArea = bitWorkArea >> 2; + buffer[pos++] = (byte)((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte)((bitWorkArea) & MASK_8BITS); + break; + default: + break; + } + } + } + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false, false, Integer.MAX_VALUE); + } - /** - * Decodes Base64 data into octets - * - * @param base64Data - * Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(byte[] base64Data) { - return new Base64().decode(base64Data); - } + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / + * characters. + * @param maxResultSize The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + // Create this so can use the super-class method + // Also ensures that the same roundings are performed by the ctor and the code + Base64 b64 = isChunked ? new Base64(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe) : new Base64(0, CHUNK_SEPARATOR, + urlSafe); + long len = b64.getEncodedLength(binaryData); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + + len + + ") than the specified maximum size of " + + maxResultSize); + } - /** - * Returns whether or not the octet is in the Base32 alphabet. - * - * @param octet - * The value to test - * @return true if the value is defined in the the Base32 alphabet false otherwise. - */ - protected boolean isInAlphabet(byte octet) { - return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; - } + return b64.encode(binaryData); + } - /** - * Below from base class - */ + /** + * Decodes Base64 data into octets + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + return new Base64().decode(base64Data); + } - /** - * MIME chunk size per RFC 2045 section 6.8. - * - *

- * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any - * equal signs. - *

- * - * @see RFC 2045 section 6.8 - */ - private static final int MIME_CHUNK_SIZE = 76; + /** + * Returns whether or not the octet is in the Base32 alphabet. + * + * @param octet The value to test + * @return true if the value is defined in the the Base32 alphabet false otherwise. + */ + protected boolean isInAlphabet(byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } - private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + /** + * Below from base class + */ - /** - * Defines the default buffer size - currently {@value} - * - must be large enough for at least one encoded block+separator - */ - private static final int DEFAULT_BUFFER_SIZE = 8192; + /** + * MIME chunk size per RFC 2045 section 6.8. + *

+ *

The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs.

+ * + * @see RFC 2045 section 6.8 + */ + private static final int MIME_CHUNK_SIZE = 76; - /** Mask used to extract 8 bits, used in decoding bytes */ - private static final int MASK_8BITS = 0xff; + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; - /** - * Byte used to pad output. - */ - private static final byte PAD_DEFAULT = '='; + /** + * Defines the default buffer size - currently {@value} - must be large enough for at least one encoded + * block+separator + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; - private static final byte PAD = PAD_DEFAULT; + /** + * Mask used to extract 8 bits, used in decoding bytes + */ + private static final int MASK_8BITS = 0xff; - /** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */ - private final int unencodedBlockSize; + /** + * Byte used to pad output. + */ + private static final byte PAD_DEFAULT = '='; - /** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */ - private final int encodedBlockSize; + private static final byte PAD = PAD_DEFAULT; - /** - * Chunksize for encoding. Not used when decoding. - * A value of zero or less implies no chunking of the encoded data. - * Rounded down to nearest multiple of encodedBlockSize. - */ - private final int lineLength; + /** + * Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 + */ + private final int unencodedBlockSize; - /** - * Size of chunk separator. Not used unless {@link #lineLength} > 0. - */ - private final int chunkSeparatorLength; + /** + * Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 + */ + private final int encodedBlockSize; - /** - * Buffer for streaming. - */ - private byte[] buffer; + /** + * Chunksize for encoding. Not used when decoding. A value of zero or less implies no chunking of the encoded data. + * Rounded down to nearest multiple of encodedBlockSize. + */ + private final int lineLength; - /** - * Position where next character should be written in the buffer. - */ - private int pos; + /** + * Size of chunk separator. Not used unless {@link #lineLength} > 0. + */ + private final int chunkSeparatorLength; - /** - * Position where next character should be read from the buffer. - */ - private int readPos; + /** + * Buffer for streaming. + */ + private byte[] buffer; - /** - * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, - * and must be thrown away. - */ - private boolean eof; + /** + * Position where next character should be written in the buffer. + */ + private int pos; - /** - * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to - * make sure each encoded line never goes beyond lineLength (if lineLength > 0). - */ - private int currentLinePos; + /** + * Position where next character should be read from the buffer. + */ + private int readPos; - /** - * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. - * This variable helps track that. - */ - private int modulus; + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, and + * must be thrown away. + */ + private boolean eof; - /** - * Ensure that the buffer has room for size bytes - * - * @param size minimum spare space required - */ - private void ensureBufferSize(int size){ - if ((buffer == null) || (buffer.length < pos + size)){ - if (buffer == null) { - buffer = new byte[DEFAULT_BUFFER_SIZE]; - pos = 0; - readPos = 0; - } else { - byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; - System.arraycopy(buffer, 0, b, 0, buffer.length); - buffer = b; - } - } - } + /** + * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to + * make sure each encoded line never goes beyond lineLength (if lineLength > 0). + */ + private int currentLinePos; - /** - * Extracts buffered data into the provided byte[] array, starting at position bPos, - * up to a maximum of bAvail bytes. Returns how many bytes were actually extracted. - * - * @param b - * byte[] array to extract the buffered data into. - * @param bPos - * position in byte[] array to start extraction at. - * @param bAvail - * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). - * @return The number of bytes successfully extracted into the provided byte[] array. - */ - private int readResults(byte[] b, int bPos, int bAvail) { - if (buffer != null) { - int len = Math.min(pos - readPos, bAvail); - System.arraycopy(buffer, readPos, b, bPos, len); - readPos += len; - if (readPos >= pos) { - buffer = null; - } - return len; - } - return eof ? -1 : 0; - } + /** + * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This + * variable helps track that. + */ + private int modulus; - /** - * Resets this object to its initial newly constructed state. - */ - private void reset() { - buffer = null; - pos = 0; - readPos = 0; - currentLinePos = 0; - modulus = 0; - eof = false; - } + /** + * Ensure that the buffer has room for size bytes + * + * @param size minimum spare space required + */ + private void ensureBufferSize(int size) { + if ((buffer == null) || (buffer.length < pos + size)) { + if (buffer == null) { + buffer = new byte[DEFAULT_BUFFER_SIZE]; + pos = 0; + readPos = 0; + } else { + byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; + System.arraycopy(buffer, 0, b, 0, buffer.length); + buffer = b; + } + } + } - /** - * Decodes a byte[] containing characters in the Base-N alphabet. - * - * @param pArray - * A byte array containing Base-N character data - * @return a byte array containing binary data - */ - private byte[] decode(byte[] pArray) { - reset(); - if (pArray == null || pArray.length == 0) { - return pArray; - } - decode(pArray, 0, pArray.length); - decode(pArray, 0, -1); - byte[] result = new byte[pos]; - readResults(result, 0, result.length); - return result; - } + /** + * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail + * bytes. Returns how many bytes were actually extracted. + * + * @param b byte[] array to extract the buffered data into. + * @param bPos position in byte[] array to start extraction at. + * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). + * @return The number of bytes successfully extracted into the provided byte[] array. + */ + private int readResults(byte[] b, int bPos, int bAvail) { + if (buffer != null) { + int len = Math.min(pos - readPos, bAvail); + System.arraycopy(buffer, readPos, b, bPos, len); + readPos += len; + if (readPos >= pos) { + buffer = null; + } + return len; + } + return eof ? -1 : 0; + } - /** - * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. - * - * @param pArray - * a byte array containing binary data - * @return A byte array containing only the basen alphabetic character data - */ - private byte[] encode(byte[] pArray) { - reset(); - if (pArray == null || pArray.length == 0) { - return pArray; - } - encode(pArray, 0, pArray.length); - encode(pArray, 0, -1); - byte[] buf = new byte[pos - readPos]; - readResults(buf, 0, buf.length); - return buf; - } + /** + * Resets this object to its initial newly constructed state. + */ + private void reset() { + buffer = null; + pos = 0; + readPos = 0; + currentLinePos = 0; + modulus = 0; + eof = false; + } - /** - * Tests a given byte array to see if it contains any characters within the alphabet or PAD. - * - * Intended for use in checking line-ending arrays - * - * @param arrayOctet - * byte array to test - * @return true if any byte is a valid character in the alphabet or PAD; false otherwise - */ - private boolean containsAlphabetOrPad(byte[] arrayOctet) { - if (arrayOctet == null) { - return false; - } - for (int i = 0; i < arrayOctet.length; i++) { - if (PAD == arrayOctet[i] || isInAlphabet(arrayOctet[i])) { - return true; - } - } - return false; - } + /** + * Decodes a byte[] containing characters in the Base-N alphabet. + * + * @param pArray A byte array containing Base-N character data + * @return a byte array containing binary data + */ + private byte[] decode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + decode(pArray, 0, pArray.length); + decode(pArray, 0, -1); + byte[] result = new byte[pos]; + readResults(result, 0, result.length); + return result; + } - /** - * Calculates the amount of space needed to encode the supplied array. - * - * @param pArray byte[] array which will later be encoded - * - * @return amount of space needed to encoded the supplied array. - * Returns a long since a max-len array will require > Integer.MAX_VALUE - */ - private long getEncodedLength(byte[] pArray) { - // Calculate non-chunked size - rounded up to allow for padding - // cast to long is needed to avoid possibility of overflow - long len = ((pArray.length + unencodedBlockSize-1) / unencodedBlockSize) * (long) encodedBlockSize; - if (lineLength > 0) { - /** - * Round up to nearest multiple - */ - len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength; - } - return len; - } + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. + * + * @param pArray a byte array containing binary data + * @return A byte array containing only the basen alphabetic character data + */ + private byte[] encode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + encode(pArray, 0, pArray.length); + encode(pArray, 0, -1); + byte[] buf = new byte[pos - readPos]; + readResults(buf, 0, buf.length); + return buf; + } + + /** + * Tests a given byte array to see if it contains any characters within the alphabet or PAD. + *

+ * Intended for use in checking line-ending arrays + * + * @param arrayOctet byte array to test + * @return true if any byte is a valid character in the alphabet or PAD; false otherwise + */ + private boolean containsAlphabetOrPad(byte[] arrayOctet) { + if (arrayOctet == null) { + return false; + } + for (int i = 0; i < arrayOctet.length; i++) { + if (PAD == arrayOctet[i] || isInAlphabet(arrayOctet[i])) { + return true; + } + } + return false; + } + + /** + * Calculates the amount of space needed to encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * @return amount of space needed to encoded the supplied array. Returns a long since a max-len array will require > + * Integer.MAX_VALUE + */ + private long getEncodedLength(byte[] pArray) { + // Calculate non-chunked size - rounded up to allow for padding + // cast to long is needed to avoid possibility of overflow + long len = ((pArray.length + unencodedBlockSize - 1) / unencodedBlockSize) * (long)encodedBlockSize; + if (lineLength > 0) { + /** + * Round up to nearest multiple + */ + len += ((len + lineLength - 1) / lineLength) * chunkSeparatorLength; + } + return len; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/Constants.java b/client/src/main/java/com/alibaba/nacos/client/identify/Constants.java index 2f566f754..339103f0c 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/Constants.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/Constants.java @@ -17,16 +17,15 @@ package com.alibaba.nacos.client.identify; /** * Identify Constants - * - * @author Nacos * + * @author Nacos */ public class Constants { - public static final String ACCESS_KEY = "accessKey"; - - public static final String SECRET_KEY = "secretKey"; - - public static final String PROPERTIES_FILENAME = "spas.properties"; + public static final String ACCESS_KEY = "accessKey"; + + public static final String SECRET_KEY = "secretKey"; + + public static final String PROPERTIES_FILENAME = "spas.properties"; public static final String CREDENTIAL_PATH = "/home/admin/.spas_key/"; diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/CredentialListener.java b/client/src/main/java/com/alibaba/nacos/client/identify/CredentialListener.java index 943facba9..a96c8ec60 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/CredentialListener.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/CredentialListener.java @@ -17,13 +17,12 @@ package com.alibaba.nacos.client.identify; /** * Credential Listener - * - * @author Nacos * + * @author Nacos */ public interface CredentialListener { - /** - * update Credential - */ + /** + * update Credential + */ public void onUpdateCredential(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/CredentialService.java b/client/src/main/java/com/alibaba/nacos/client/identify/CredentialService.java index cf8f792ca..063f18fbc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/CredentialService.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/CredentialService.java @@ -23,34 +23,33 @@ import java.util.concurrent.ConcurrentHashMap; /** * Credential Service - * - * @author Nacos * + * @author Nacos */ public final class CredentialService implements SpasCredentialLoader { - static final public Logger log = LogUtils.logger(CredentialService.class); - private static ConcurrentHashMap instances = new ConcurrentHashMap(); + static final public Logger log = LogUtils.logger(CredentialService.class); + private static ConcurrentHashMap instances + = new ConcurrentHashMap(); private String appName; - private Credentials credentials = new Credentials(); + private Credentials credentials = new Credentials(); private CredentialWatcher watcher; private CredentialListener listener; - - private CredentialService(String appName) { + + private CredentialService(String appName) { if (appName == null) { - String value = System.getProperty("project.name"); - if (StringUtils.isNotEmpty(value)) { - appName = value; - } + String value = System.getProperty("project.name"); + if (StringUtils.isNotEmpty(value)) { + appName = value; + } } this.appName = appName; watcher = new CredentialWatcher(appName, this); - } - - - public static CredentialService getInstance() { + } + + public static CredentialService getInstance() { return getInstance(null); - } + } public static CredentialService getInstance(String appName) { String key = appName != null ? appName : Constants.NO_APP_NAME; @@ -85,13 +84,13 @@ public final class CredentialService implements SpasCredentialLoader { log.info(appName, this.getClass().getSimpleName() + " is freed"); } - public Credentials getCredential() { + public Credentials getCredential() { Credentials localCredential = credentials; if (localCredential.valid()) { return localCredential; } - return credentials; - } + return credentials; + } public void setCredential(Credentials credential) { boolean changed = !(credentials == credential || (credentials != null && credentials.identical(credential))); @@ -113,23 +112,23 @@ public final class CredentialService implements SpasCredentialLoader { } @Deprecated - public void setAccessKey(String accessKey) { - credentials.setAccessKey(accessKey); - } + public void setAccessKey(String accessKey) { + credentials.setAccessKey(accessKey); + } @Deprecated - public void setSecretKey(String secretKey) { - credentials.setSecretKey(secretKey); - } + public void setSecretKey(String secretKey) { + credentials.setSecretKey(secretKey); + } @Deprecated - public String getAccessKey() { - return credentials.getAccessKey(); - } + public String getAccessKey() { + return credentials.getAccessKey(); + } @Deprecated - public String getSecretKey() { - return credentials.getSecretKey(); - } + public String getSecretKey() { + return credentials.getSecretKey(); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/CredentialWatcher.java b/client/src/main/java/com/alibaba/nacos/client/identify/CredentialWatcher.java index 553849ba9..4da1f9886 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/CredentialWatcher.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/CredentialWatcher.java @@ -31,12 +31,11 @@ import com.alibaba.nacos.client.utils.StringUtils; /** * Credential Watcher - * - * @author Nacos * + * @author Nacos */ public class CredentialWatcher { - static final public Logger SpasLogger = LogUtils.logger(CredentialWatcher.class); + static final public Logger SpasLogger = LogUtils.logger(CredentialWatcher.class); private static final long REFRESH_INTERVAL = 10 * 1000; private CredentialService serviceInstance; @@ -104,21 +103,20 @@ public class CredentialWatcher { propertyPath = url.getPath(); } if (propertyPath == null || propertyPath.isEmpty()) { - - String value = System.getProperty("spas.identity"); - if (StringUtils.isNotEmpty(value)) { - propertyPath = value; - } - if (propertyPath == null || propertyPath.isEmpty()) { - propertyPath = Constants.CREDENTIAL_PATH + (appName == null ? Constants.CREDENTIAL_DEFAULT : appName); + + String value = System.getProperty("spas.identity"); + if (StringUtils.isNotEmpty(value)) { + propertyPath = value; } - else { + if (propertyPath == null || propertyPath.isEmpty()) { + propertyPath = Constants.CREDENTIAL_PATH + (appName == null ? Constants.CREDENTIAL_DEFAULT + : appName); + } else { if (logWarn) { SpasLogger.info(appName, "Defined credential file: -D" + "spas.identity" + "=" + propertyPath); } } - } - else { + } else { if (logWarn) { SpasLogger.info(appName, "Load credential file from classpath: " + Constants.PROPERTIES_FILENAME); } @@ -130,7 +128,8 @@ public class CredentialWatcher { try { propertiesIS = new FileInputStream(propertyPath); } catch (FileNotFoundException e) { - if (appName != null && !appName.equals(Constants.CREDENTIAL_DEFAULT) && propertyPath.equals(Constants.CREDENTIAL_PATH + appName)) { + if (appName != null && !appName.equals(Constants.CREDENTIAL_DEFAULT) && propertyPath.equals( + Constants.CREDENTIAL_PATH + appName)) { propertyPath = Constants.CREDENTIAL_PATH + Constants.CREDENTIAL_DEFAULT; continue; } @@ -154,22 +153,21 @@ public class CredentialWatcher { } return; } - } - else { + } else { Properties properties = new Properties(); try { properties.load(propertiesIS); } catch (IOException e) { - SpasLogger.error("26", "Unable to load credential file, appName:" + appName - + "Unable to load credential file " + propertyPath, e); - propertyPath = null; + SpasLogger.error("26", "Unable to load credential file, appName:" + appName + + "Unable to load credential file " + propertyPath, e); + propertyPath = null; return; } finally { try { propertiesIS.close(); } catch (IOException e) { - SpasLogger.error("27", "Unable to close credential file, appName:" + appName - + "Unable to close credential file " + propertyPath, e); + SpasLogger.error("27", "Unable to close credential file, appName:" + appName + + "Unable to close credential file " + propertyPath, e); } } @@ -202,12 +200,12 @@ public class CredentialWatcher { } Credentials credential = new Credentials(accessKey, secretKey); - if (!credential.valid()) { - SpasLogger.warn("1", "Credential file missing required property" + appName + "Credential file missing " - + Constants.ACCESS_KEY + " or " + Constants.SECRET_KEY); - propertyPath = null; - // return; - } + if (!credential.valid()) { + SpasLogger.warn("1", "Credential file missing required property" + appName + "Credential file missing " + + Constants.ACCESS_KEY + " or " + Constants.SECRET_KEY); + propertyPath = null; + // return; + } serviceInstance.setCredential(credential); } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/Credentials.java b/client/src/main/java/com/alibaba/nacos/client/identify/Credentials.java index f54100bf6..a271561da 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/Credentials.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/Credentials.java @@ -17,40 +17,39 @@ package com.alibaba.nacos.client.identify; /** * Credentials - * - * @author Nacos * + * @author Nacos */ public class Credentials implements SpasCredential { - - private volatile String accessKey; - - private volatile String secretKey; - - public Credentials(String accessKey, String secretKey) { - this.accessKey = accessKey; - this.secretKey = secretKey; - } - - public Credentials() { - this(null, null); - } - - public String getAccessKey() { - return accessKey; - } - public void setAccessKey(String accessKey) { - this.accessKey = accessKey; - } + private volatile String accessKey; - public String getSecretKey() { - return secretKey; - } + private volatile String secretKey; - public void setSecretKey(String secretKey) { - this.secretKey = secretKey; - } + public Credentials(String accessKey, String secretKey) { + this.accessKey = accessKey; + this.secretKey = secretKey; + } + + public Credentials() { + this(null, null); + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } public boolean valid() { return accessKey != null && !accessKey.isEmpty() && secretKey != null && !secretKey.isEmpty(); @@ -58,8 +57,10 @@ public class Credentials implements SpasCredential { public boolean identical(Credentials other) { return this == other || - (other != null && - (accessKey == null && other.accessKey == null || accessKey != null && accessKey.equals(other.accessKey)) && - (secretKey == null && other.secretKey == null || secretKey != null && secretKey.equals(other.secretKey))); + (other != null && + (accessKey == null && other.accessKey == null || accessKey != null && accessKey.equals(other.accessKey)) + && + (secretKey == null && other.secretKey == null || secretKey != null && secretKey.equals( + other.secretKey))); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/STSConfig.java b/client/src/main/java/com/alibaba/nacos/client/identify/STSConfig.java index e555c5353..139877468 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/STSConfig.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/STSConfig.java @@ -19,110 +19,110 @@ import com.alibaba.nacos.client.utils.StringUtils; /** * Sts config - * + * * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class STSConfig { - private static final String RAM_SECURITY_CREDENTIALS_URL - = ""; - private String ramRoleName; - /** - * STS 临时凭证有效期剩余多少时开始刷新(允许本地时间比 STS 服务时间最多慢多久) - */ - private int timeToRefreshInMillisecond = 3 * 60 * 1000; - /** - * 获取 STS 临时凭证的元数据接口(包含角色名称) - */ - private String securityCredentialsUrl; - /** - * 设定 STS 临时凭证,不再通过元数据接口获取 - */ - private String securityCredentials; - /** - * 是否缓存 - */ - private boolean cacheSecurityCredentials = true; + private static final String RAM_SECURITY_CREDENTIALS_URL + = ""; + private String ramRoleName; + /** + * STS 临时凭证有效期剩余多少时开始刷新(允许本地时间比 STS 服务时间最多慢多久) + */ + private int timeToRefreshInMillisecond = 3 * 60 * 1000; + /** + * 获取 STS 临时凭证的元数据接口(包含角色名称) + */ + private String securityCredentialsUrl; + /** + * 设定 STS 临时凭证,不再通过元数据接口获取 + */ + private String securityCredentials; + /** + * 是否缓存 + */ + private boolean cacheSecurityCredentials = true; - private static class Singleton { - private static final STSConfig INSTANCE = new STSConfig(); - } + private static class Singleton { + private static final STSConfig INSTANCE = new STSConfig(); + } - private STSConfig() { - String ramRoleName = System.getProperty("ram.role.name"); - if (!StringUtils.isBlank(ramRoleName)) { - setRamRoleName(ramRoleName); - } + private STSConfig() { + String ramRoleName = System.getProperty("ram.role.name"); + if (!StringUtils.isBlank(ramRoleName)) { + setRamRoleName(ramRoleName); + } - String timeToRefreshInMillisecond = System.getProperty("time.to.refresh.in.millisecond"); - if (!StringUtils.isBlank(timeToRefreshInMillisecond)) { - setTimeToRefreshInMillisecond(Integer.parseInt(timeToRefreshInMillisecond)); - } + String timeToRefreshInMillisecond = System.getProperty("time.to.refresh.in.millisecond"); + if (!StringUtils.isBlank(timeToRefreshInMillisecond)) { + setTimeToRefreshInMillisecond(Integer.parseInt(timeToRefreshInMillisecond)); + } - String securityCredentials = System.getProperty("security.credentials"); - if (!StringUtils.isBlank(securityCredentials)) { - setSecurityCredentials(securityCredentials); - } + String securityCredentials = System.getProperty("security.credentials"); + if (!StringUtils.isBlank(securityCredentials)) { + setSecurityCredentials(securityCredentials); + } - String securityCredentialsUrl = System.getProperty("security.credentials.url"); - if (!StringUtils.isBlank(securityCredentialsUrl)) { - setSecurityCredentialsUrl(securityCredentialsUrl); - } + String securityCredentialsUrl = System.getProperty("security.credentials.url"); + if (!StringUtils.isBlank(securityCredentialsUrl)) { + setSecurityCredentialsUrl(securityCredentialsUrl); + } - String cacheSecurityCredentials = System.getProperty("cache.security.credentials"); - if (!StringUtils.isBlank(cacheSecurityCredentials)) { - setCacheSecurityCredentials(Boolean.valueOf(cacheSecurityCredentials)); - } - } + String cacheSecurityCredentials = System.getProperty("cache.security.credentials"); + if (!StringUtils.isBlank(cacheSecurityCredentials)) { + setCacheSecurityCredentials(Boolean.valueOf(cacheSecurityCredentials)); + } + } - public static STSConfig getInstance() { - return Singleton.INSTANCE; - } + public static STSConfig getInstance() { + return Singleton.INSTANCE; + } - public String getRamRoleName() { - return ramRoleName; - } + public String getRamRoleName() { + return ramRoleName; + } - public void setRamRoleName(String ramRoleName) { - this.ramRoleName = ramRoleName; - } + public void setRamRoleName(String ramRoleName) { + this.ramRoleName = ramRoleName; + } - public int getTimeToRefreshInMillisecond() { - return timeToRefreshInMillisecond; - } + public int getTimeToRefreshInMillisecond() { + return timeToRefreshInMillisecond; + } - public void setTimeToRefreshInMillisecond(int timeToRefreshInMillisecond) { - this.timeToRefreshInMillisecond = timeToRefreshInMillisecond; - } + public void setTimeToRefreshInMillisecond(int timeToRefreshInMillisecond) { + this.timeToRefreshInMillisecond = timeToRefreshInMillisecond; + } - public String getSecurityCredentialsUrl() { - if (securityCredentialsUrl == null && ramRoleName != null) { - return RAM_SECURITY_CREDENTIALS_URL + ramRoleName; - } - return securityCredentialsUrl; - } + public String getSecurityCredentialsUrl() { + if (securityCredentialsUrl == null && ramRoleName != null) { + return RAM_SECURITY_CREDENTIALS_URL + ramRoleName; + } + return securityCredentialsUrl; + } - public void setSecurityCredentialsUrl(String securityCredentialsUrl) { - this.securityCredentialsUrl = securityCredentialsUrl; - } + public void setSecurityCredentialsUrl(String securityCredentialsUrl) { + this.securityCredentialsUrl = securityCredentialsUrl; + } - public String getSecurityCredentials() { - return securityCredentials; - } + public String getSecurityCredentials() { + return securityCredentials; + } - public void setSecurityCredentials(String securityCredentials) { - this.securityCredentials = securityCredentials; - } + public void setSecurityCredentials(String securityCredentials) { + this.securityCredentials = securityCredentials; + } - public boolean isSTSOn() { - return StringUtils.isNotEmpty(getSecurityCredentials()) || StringUtils.isNotEmpty(getSecurityCredentialsUrl()); - } + public boolean isSTSOn() { + return StringUtils.isNotEmpty(getSecurityCredentials()) || StringUtils.isNotEmpty(getSecurityCredentialsUrl()); + } - public boolean isCacheSecurityCredentials() { - return cacheSecurityCredentials; - } + public boolean isCacheSecurityCredentials() { + return cacheSecurityCredentials; + } - public void setCacheSecurityCredentials(boolean cacheSecurityCredentials) { - this.cacheSecurityCredentials = cacheSecurityCredentials; - } + public void setCacheSecurityCredentials(boolean cacheSecurityCredentials) { + this.cacheSecurityCredentials = cacheSecurityCredentials; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredential.java b/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredential.java index 8d6a2bd91..7849eaeef 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredential.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredential.java @@ -17,22 +17,21 @@ package com.alibaba.nacos.client.identify; /** * Spas Credential Interface - * - * @author Nacos * + * @author Nacos */ public interface SpasCredential { - /** - * get AccessKey - * - * @return AccessKey - */ - public String getAccessKey(); + /** + * get AccessKey + * + * @return AccessKey + */ + public String getAccessKey(); - /** - * get SecretKey - * - * @return SecretKey - */ - public String getSecretKey(); + /** + * get SecretKey + * + * @return SecretKey + */ + public String getSecretKey(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredentialLoader.java b/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredentialLoader.java index 12012ee2a..9cd1d37d3 100644 --- a/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredentialLoader.java +++ b/client/src/main/java/com/alibaba/nacos/client/identify/SpasCredentialLoader.java @@ -17,15 +17,14 @@ package com.alibaba.nacos.client.identify; /** * Spas Credential Loader - * - * @author Nacos * + * @author Nacos */ public interface SpasCredentialLoader { - /** - * get Credential - * - * @return Credential - */ + /** + * get Credential + * + * @return Credential + */ SpasCredential getCredential(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/Level.java b/client/src/main/java/com/alibaba/nacos/client/logger/Level.java index 38522f801..4f7031cf5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/Level.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/Level.java @@ -17,14 +17,18 @@ package com.alibaba.nacos.client.logger; /** * 阿里中间件日志级别 - * + * * @author zhuyong 2014年3月20日 上午9:57:27 */ public enum Level { - /** - * log level - */ - DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR"), OFF("OFF"); + /** + * log level + */ + DEBUG("DEBUG"), + INFO("INFO"), + WARN("WARN"), + ERROR("ERROR"), + OFF("OFF"); private String name; diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/Logger.java b/client/src/main/java/com/alibaba/nacos/client/logger/Logger.java index f38c3e811..35e1995ef 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/Logger.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/Logger.java @@ -20,7 +20,7 @@ import com.alibaba.nacos.client.logger.option.ActivateOption; /** *

  * 阿里中间件日志API,用于输出定制化的日志
- * 
+ *
  * 定制格式如下:01 %d{yyyy-MM-dd HH:mm:ss.SSS} %p [%-5t:%c{2}] %m%n
  * 其中:
  * 01                           日志API版本,后续如果格式有变化,会修改此版本号,方便机器解析
@@ -29,17 +29,17 @@ import com.alibaba.nacos.client.logger.option.ActivateOption;
  * [%-5t:%c{2}]                 线程名:日志名
  * %m                           日志信息
  * %n                           换行
- * 
+ *
  * 关于%m,也有其中的格式要求:[Context] [STAT-INFO] [ERROR-CODE]
  * 其中:
  * Context                      打印时间时的上下文信息,如果没有,则内容为空,但'[]'这个占位符仍要输出
  * STAT-INFO                    待定
  * ERROR-CODE                   常见的错误码,帮助用户解决问题
- * 
+ *
  * 在异常中,也需要输出ErrorCode及对应的TraceUrl,可以使用
  * com.alibaba.nacos.client.logger.support.LoggerHelper.getErrorCodeStr(String errorCode)来获取格式化后的串
  * 
- * + * * @author zhuyong 2014年3月20日 上午9:58:27 */ public interface Logger extends ActivateOption { @@ -55,7 +55,7 @@ public interface Logger extends ActivateOption { * 输出Debug日志 * * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数数组 + * @param args 格式化串参数数组 */ void debug(String format, Object... args); @@ -71,8 +71,8 @@ public interface Logger extends ActivateOption { * 输出Debug日志 * * @param context 日志上下文信息 - * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数数组 + * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param args 格式化串参数数组 */ void debug(String context, String format, Object... args); @@ -87,7 +87,7 @@ public interface Logger extends ActivateOption { * 输出Info日志 * * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数数组 + * @param args 格式化串参数数组 */ void info(String format, Object... args); @@ -103,8 +103,8 @@ public interface Logger extends ActivateOption { * 输出Info日志 * * @param context 日志上下文信息 - * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数数组 + * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param args 格式化串参数数组 */ void info(String context, String format, Object... args); @@ -119,7 +119,7 @@ public interface Logger extends ActivateOption { * 输出Warn日志 * * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param t 异常信息 + * @param t 异常信息 * @since 0.1.5 */ void warn(String message, Throwable t); @@ -128,7 +128,7 @@ public interface Logger extends ActivateOption { * 输出Warn日志 * * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数数组 + * @param args 格式化串参数数组 */ void warn(String format, Object... args); @@ -144,8 +144,8 @@ public interface Logger extends ActivateOption { * 输出Warn日志 * * @param context 日志上下文信息 - * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数数组 + * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param args 格式化串参数数组 */ void warn(String context, String format, Object... args); @@ -153,7 +153,7 @@ public interface Logger extends ActivateOption { * 输出Error日志 * * @param errorCode 错误码,如HSF-0001 - * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) */ void error(String errorCode, String message); @@ -161,8 +161,8 @@ public interface Logger extends ActivateOption { * 输出Error日志 * * @param errorCode 错误码,如HSF-0001 - * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param t 异常信息 + * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param t 异常信息 */ void error(String errorCode, String message, Throwable t); @@ -170,68 +170,71 @@ public interface Logger extends ActivateOption { * 输出Error日志 * * @param errorCode 错误码,如HSF-0001 - * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param objs 格式化串参数数组 + * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param objs 格式化串参数数组 */ void error(String errorCode, String format, Object... objs); /** * 输出Error日志 * - * @param context 日志上下文信息 + * @param context 日志上下文信息 * @param errorCode 错误码 - * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) */ void error(String context, String errorCode, String message); /** * 输出Error日志 * - * @param context 日志上下文信息 + * @param context 日志上下文信息 * @param errorCode 错误码 - * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param t 异常信息 + * @param message 日志信息(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param t 异常信息 */ void error(String context, String errorCode, String message, Throwable t); /** * 输出Error日志 * - * @param context 日志上下文信息 + * @param context 日志上下文信息 * @param errorCode 错误码 - * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) - * @param args 格式化串参数 + * @param format 日志信息格式化字符串,比如 'Hi,{} {} {}'(当使用ResourceBundle用于国际化日志输出时,message为对应的key, since 0.1.5) + * @param args 格式化串参数 */ void error(String context, String errorCode, String format, Object... args); - /** - * 判断Debug级别是否开启 - * - * @return Debug级别是否开启 - */ + /** + * 判断Debug级别是否开启 + * + * @return Debug级别是否开启 + */ boolean isDebugEnabled(); /** * 判断Info级别是否开启 + * * @return Info级别是否开启 */ boolean isInfoEnabled(); /** * 判断Warn级别是否开启 + * * @return Warn级别是否开启 */ boolean isWarnEnabled(); - /** - * 判断Error级别是否开启 - * - * @return Error级别是否开启 - */ + /** + * 判断Error级别是否开启 + * + * @return Error级别是否开启 + */ boolean isErrorEnabled(); /** * * 获取内部日志实现对象 + * * @return 内部日志实现对象 */ Object getDelegate(); diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/LoggerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/LoggerFactory.java index bc421aca7..e7f40e34c 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/LoggerFactory.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/LoggerFactory.java @@ -15,16 +15,15 @@ */ package com.alibaba.nacos.client.logger; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.alibaba.nacos.client.logger.log4j.Log4jLoggerFactory; import com.alibaba.nacos.client.logger.log4j2.Log4j2LoggerFactory; import com.alibaba.nacos.client.logger.nop.NopLoggerFactory; import com.alibaba.nacos.client.logger.slf4j.Slf4jLoggerFactory; import com.alibaba.nacos.client.logger.support.ILoggerFactory; import com.alibaba.nacos.client.logger.support.LogLog; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** *
  * 阿里中间件LoggerFactory,获取具体日志实现
@@ -57,17 +56,12 @@ public class LoggerFactory {
             LogLog.info("Init JM logger with Slf4jLoggerFactory success, " + LoggerFactory.class.getClassLoader());
         } catch (Throwable e1) {
             try {
-                setLoggerFactory(new Log4jLoggerFactory());
-                LogLog.info("Init JM logger with Log4jLoggerFactory, " + LoggerFactory.class.getClassLoader());
+                setLoggerFactory(new Log4j2LoggerFactory());
+                LogLog.info("Init JM logger with Log4j2LoggerFactory, " + LoggerFactory.class.getClassLoader());
             } catch (Throwable e2) {
-                try {
-                    setLoggerFactory(new Log4j2LoggerFactory());
-                    LogLog.info("Init JM logger with Log4j2LoggerFactory, " + LoggerFactory.class.getClassLoader());
-                } catch (Throwable e3) {
-                    setLoggerFactory(new NopLoggerFactory());
-                    LogLog.warn("Init JM logger with NopLoggerFactory, pay attention. "
-                                + LoggerFactory.class.getClassLoader(), e2);
-                }
+                setLoggerFactory(new NopLoggerFactory());
+                LogLog.warn("Init JM logger with NopLoggerFactory, pay attention. "
+                    + LoggerFactory.class.getClassLoader(), e2);
             }
         }
 
diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONArray.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONArray.java
index d14f47e70..99763cc61 100644
--- a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONArray.java
+++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONArray.java
@@ -28,371 +28,367 @@ import java.util.Iterator;
 
 /**
  * A JSON array. JSONObject supports java.util.List interface.
- * 
- * @author FangYidong
+ *
+ * @author FangYidong
  */
 @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
 public class JSONArray extends ArrayList implements JSONAware, JSONStreamAware {
-	private static final long serialVersionUID = 3957988303675231981L;
-	
-	/**
-	 * Constructs an empty JSONArray.
-	 */
-	public JSONArray(){
-		super();
-	}
-	
-	/**
-	 * Constructs a JSONArray containing the elements of the specified
-	 * collection, in the order they are returned by the collection's iterator.
-	 * 
-	 * @param c the collection whose elements are to be placed into this JSONArray
-	 */
-	public JSONArray(Collection c){
-		super(c);
-	}
-	
+    private static final long serialVersionUID = 3957988303675231981L;
+
     /**
-     * Encode a list into JSON text and write it to out. 
-     * If this list is also a JSONStreamAware or a JSONAware, JSONStreamAware and JSONAware specific behaviours will be ignored at this top level.
-     * 
-     * @see com.alibaba.nacos.client.logger.json.JSONValue#writeJSONString(Object, Writer)
-     * 
+     * Constructs an empty JSONArray.
+     */
+    public JSONArray() {
+        super();
+    }
+
+    /**
+     * Constructs a JSONArray containing the elements of the specified collection, in the order they are returned by the
+     * collection's iterator.
+     *
+     * @param c the collection whose elements are to be placed into this JSONArray
+     */
+    public JSONArray(Collection c) {
+        super(c);
+    }
+
+    /**
+     * Encode a list into JSON text and write it to out. If this list is also a JSONStreamAware or a JSONAware,
+     * JSONStreamAware and JSONAware specific behaviours will be ignored at this top level.
+     *
      * @param collection
      * @param out
+     * @see com.alibaba.nacos.client.logger.json.JSONValue#writeJSONString(Object, Writer)
      */
-	public static void writeJSONString(Collection collection, Writer out) throws IOException{
-		if(collection == null){
-			out.write("null");
-			return;
-		}
-		
-		boolean first = true;
-		Iterator iter=collection.iterator();
-		
+    public static void writeJSONString(Collection collection, Writer out) throws IOException {
+        if (collection == null) {
+            out.write("null");
+            return;
+        }
+
+        boolean first = true;
+        Iterator iter = collection.iterator();
+
         out.write('[');
-		while(iter.hasNext()){
-            if(first) {
+        while (iter.hasNext()) {
+            if (first) {
                 first = false;
-            }
-            else {
+            } else {
                 out.write(',');
             }
-			Object value=iter.next();
-			if(value == null){
-				out.write("null");
-				continue;
-			}
-			
-			JSONValue.writeJSONString(value, out);
-		}
-		out.write(']');
-	}
-	
-	public void writeJSONString(Writer out) throws IOException{
-		writeJSONString(this, out);
-	}
-	
-	/**
-	 * Convert a list to JSON text. The result is a JSON array. 
-	 * If this list is also a JSONAware, JSONAware specific behaviours will be omitted at this top level.
-	 * 
-	 * @see com.alibaba.nacos.client.logger.json.JSONValue#toJSONString(Object)
-	 * 
-	 * @param collection
-	 * @return JSON text, or "null" if list is null.
-	 */
-	public static String toJSONString(Collection collection){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(collection, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
+            Object value = iter.next();
+            if (value == null) {
+                out.write("null");
+                continue;
+            }
 
-	public static void writeJSONString(byte[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(byte[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(short[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(short[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(int[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(int[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(long[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(long[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(float[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(float[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(double[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(double[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(boolean[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(boolean[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(char[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[\"");
-			out.write(String.valueOf(array[0]));
-			
-			for(int i = 1; i < array.length; i++){
-				out.write("\",\"");
-				out.write(String.valueOf(array[i]));
-			}
-			
-			out.write("\"]");
-		}
-	}
-	
-	public static String toJSONString(char[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public static void writeJSONString(Object[] array, Writer out) throws IOException{
-		if(array == null){
-			out.write("null");
-		} else if(array.length == 0) {
-			out.write("[]");
-		} else {
-			out.write("[");
-			JSONValue.writeJSONString(array[0], out);
-			
-			for(int i = 1; i < array.length; i++){
-				out.write(",");
-				JSONValue.writeJSONString(array[i], out);
-			}
-			
-			out.write("]");
-		}
-	}
-	
-	public static String toJSONString(Object[] array){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(array, writer);
-			return writer.toString();
-		} catch(IOException e){
-			// This should never happen for a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public String toJSONString(){
-		return toJSONString(this);
-	}
+            JSONValue.writeJSONString(value, out);
+        }
+        out.write(']');
+    }
 
-	/**
-	 * Returns a string representation of this array. This is equivalent to
-	 * calling {@link JSONArray#toJSONString()}.
-	 */
-	public String toString() {
-		return toJSONString();
-	}
+    public void writeJSONString(Writer out) throws IOException {
+        writeJSONString(this, out);
+    }
+
+    /**
+     * Convert a list to JSON text. The result is a JSON array. If this list is also a JSONAware, JSONAware specific
+     * behaviours will be omitted at this top level.
+     *
+     * @param collection
+     * @return JSON text, or "null" if list is null.
+     * @see com.alibaba.nacos.client.logger.json.JSONValue#toJSONString(Object)
+     */
+    public static String toJSONString(Collection collection) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(collection, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(byte[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(byte[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(short[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(short[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(int[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(int[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(long[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(long[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(float[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(float[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(double[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(double[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(boolean[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(boolean[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(char[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[\"");
+            out.write(String.valueOf(array[0]));
+
+            for (int i = 1; i < array.length; i++) {
+                out.write("\",\"");
+                out.write(String.valueOf(array[i]));
+            }
+
+            out.write("\"]");
+        }
+    }
+
+    public static String toJSONString(char[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void writeJSONString(Object[] array, Writer out) throws IOException {
+        if (array == null) {
+            out.write("null");
+        } else if (array.length == 0) {
+            out.write("[]");
+        } else {
+            out.write("[");
+            JSONValue.writeJSONString(array[0], out);
+
+            for (int i = 1; i < array.length; i++) {
+                out.write(",");
+                JSONValue.writeJSONString(array[i], out);
+            }
+
+            out.write("]");
+        }
+    }
+
+    public static String toJSONString(Object[] array) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(array, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen for a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public String toJSONString() {
+        return toJSONString(this);
+    }
+
+    /**
+     * Returns a string representation of this array. This is equivalent to calling {@link JSONArray#toJSONString()}.
+     */
+    public String toString() {
+        return toJSONString();
+    }
 }
diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONAware.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONAware.java
index 778cdb158..2b2ebc1f7 100644
--- a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONAware.java
+++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONAware.java
@@ -16,15 +16,16 @@
 package com.alibaba.nacos.client.logger.json;
 
 /**
- * Beans that support customized output of JSON text shall implement this interface.  
- * @author FangYidong
+ * Beans that support customized output of JSON text shall implement this interface.
+ *
+ * @author FangYidong
  */
 @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
 public interface JSONAware {
-	/**
-	 * format change
-	 * 
-	 * @return JSON text
-	 */
-	String toJSONString();
+    /**
+     * format change
+     *
+     * @return JSON text
+     */
+    String toJSONString();
 }
diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONObject.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONObject.java
index 836aa2b69..66439f038 100644
--- a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONObject.java
+++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONObject.java
@@ -28,125 +28,118 @@ import java.util.Map;
 
 /**
  * A JSON object. Key value pairs are unordered. JSONObject supports java.util.Map interface.
- * 
- * @author FangYidong
+ *
+ * @author FangYidong
  */
 @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
-public class JSONObject extends HashMap implements Map, JSONAware, JSONStreamAware{
-	
-	private static final long serialVersionUID = -503443796854799292L;
-	
-	
-	public JSONObject() {
-		super();
-	}
+public class JSONObject extends HashMap implements Map, JSONAware, JSONStreamAware {
 
-	/**
-	 * Allows creation of a JSONObject from a Map. After that, both the
-	 * generated JSONObject and the Map can be modified independently.
-	 * 
-	 * @param map
-	 */
-	public JSONObject(Map map) {
-		super(map);
-	}
+    private static final long serialVersionUID = -503443796854799292L;
 
+    public JSONObject() {
+        super();
+    }
 
     /**
-     * Encode a map into JSON text and write it to out.
-     * If this map is also a JSONAware or JSONStreamAware, JSONAware or JSONStreamAware specific behaviours will be ignored at this top level.
-     * 
-     * @see com.alibaba.nacos.client.logger.json.JSONValue#writeJSONString(Object, Writer)
-     * 
+     * Allows creation of a JSONObject from a Map. After that, both the generated JSONObject and the Map can be modified
+     * independently.
+     *
+     * @param map
+     */
+    public JSONObject(Map map) {
+        super(map);
+    }
+
+    /**
+     * Encode a map into JSON text and write it to out. If this map is also a JSONAware or JSONStreamAware, JSONAware or
+     * JSONStreamAware specific behaviours will be ignored at this top level.
+     *
      * @param map
      * @param out
+     * @see com.alibaba.nacos.client.logger.json.JSONValue#writeJSONString(Object, Writer)
      */
-	public static void writeJSONString(Map map, Writer out) throws IOException {
-		if(map == null){
-			out.write("null");
-			return;
-		}
-		
-		boolean first = true;
-		Iterator iter=map.entrySet().iterator();
-		
+    public static void writeJSONString(Map map, Writer out) throws IOException {
+        if (map == null) {
+            out.write("null");
+            return;
+        }
+
+        boolean first = true;
+        Iterator iter = map.entrySet().iterator();
+
         out.write('{');
-		while(iter.hasNext()){
-			if (first) {
-				first = false;
-			}
-			else {
-				out.write(',');
-			}
-			Map.Entry entry = (Map.Entry) iter.next();
+        while (iter.hasNext()) {
+            if (first) {
+                first = false;
+            } else {
+                out.write(',');
+            }
+            Map.Entry entry = (Map.Entry)iter.next();
             out.write('\"');
             out.write(escape(String.valueOf(entry.getKey())));
             out.write('\"');
             out.write(':');
-			JSONValue.writeJSONString(entry.getValue(), out);
-		}
-		out.write('}');
-	}
+            JSONValue.writeJSONString(entry.getValue(), out);
+        }
+        out.write('}');
+    }
 
-	public void writeJSONString(Writer out) throws IOException{
-		writeJSONString(this, out);
-	}
-	
-	/**
-	 * Convert a map to JSON text. The result is a JSON object. 
-	 * If this map is also a JSONAware, JSONAware specific behaviours will be omitted at this top level.
-	 * 
-	 * @see com.alibaba.nacos.client.logger.json.JSONValue#toJSONString(Object)
-	 * 
-	 * @param map
-	 * @return JSON text, or "null" if map is null.
-	 */
-	public static String toJSONString(Map map){
-		final StringWriter writer = new StringWriter();
-		
-		try {
-			writeJSONString(map, writer);
-			return writer.toString();
-		} catch (IOException e) {
-			// This should never happen with a StringWriter
-			throw new RuntimeException(e);
-		}
-	}
-	
-	public String toJSONString(){
-		return toJSONString(this);
-	}
-	
-	public String toString(){
-		return toJSONString();
-	}
+    public void writeJSONString(Writer out) throws IOException {
+        writeJSONString(this, out);
+    }
 
-	public static String toString(String key,Object value){
+    /**
+     * Convert a map to JSON text. The result is a JSON object. If this map is also a JSONAware, JSONAware specific
+     * behaviours will be omitted at this top level.
+     *
+     * @param map
+     * @return JSON text, or "null" if map is null.
+     * @see com.alibaba.nacos.client.logger.json.JSONValue#toJSONString(Object)
+     */
+    public static String toJSONString(Map map) {
+        final StringWriter writer = new StringWriter();
+
+        try {
+            writeJSONString(map, writer);
+            return writer.toString();
+        } catch (IOException e) {
+            // This should never happen with a StringWriter
+            throw new RuntimeException(e);
+        }
+    }
+
+    public String toJSONString() {
+        return toJSONString(this);
+    }
+
+    public String toString() {
+        return toJSONString();
+    }
+
+    public static String toString(String key, Object value) {
         StringBuffer sb = new StringBuffer();
         sb.append('\"');
-        if(key == null) {
+        if (key == null) {
             sb.append("null");
-        }
-        else {
+        } else {
             JSONValue.escape(key, sb);
         }
-		sb.append('\"').append(':');
-		
-		sb.append(JSONValue.toJSONString(value));
-		
-		return sb.toString();
-	}
-	
-	/**
-	 * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F).
-	 * It's the same as JSONValue.escape() only for compatibility here.
-	 * 
-	 * @see com.alibaba.nacos.client.logger.json.JSONValue#escape(String)
-	 * 
-	 * @param s
-	 * @return
-	 */
-	public static String escape(String s){
-		return JSONValue.escape(s);
-	}
+        sb.append('\"').append(':');
+
+        sb.append(JSONValue.toJSONString(value));
+
+        return sb.toString();
+    }
+
+    /**
+     * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). It's the same as
+     * JSONValue.escape() only for compatibility here.
+     *
+     * @param s
+     * @return
+     * @see com.alibaba.nacos.client.logger.json.JSONValue#escape(String)
+     */
+    public static String escape(String s) {
+        return JSONValue.escape(s);
+    }
 }
diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONStreamAware.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONStreamAware.java
index 97c757902..5946913cb 100644
--- a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONStreamAware.java
+++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONStreamAware.java
@@ -19,20 +19,17 @@ import java.io.IOException;
 import java.io.Writer;
 
 /**
- * Beans that support customized output of JSON text to a writer shall implement
- * this interface.
- * 
- * @author FangYidong
+ * Beans that support customized output of JSON text to a writer shall implement this interface.
+ *
+ * @author FangYidong
  */
 @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
 public interface JSONStreamAware {
-	/**
-	 * write JSON string to out.
-	 * 
-	 * @param out
-	 *            out writer
-	 * @throws IOException
-	 *             Exception
-	 */
-	void writeJSONString(Writer out) throws IOException;
+    /**
+     * write JSON string to out.
+     *
+     * @param out out writer
+     * @throws IOException Exception
+     */
+    void writeJSONString(Writer out) throws IOException;
 }
diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONValue.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONValue.java
index 47e15586a..dfbac41e8 100644
--- a/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONValue.java
+++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/JSONValue.java
@@ -30,314 +30,286 @@ import java.util.Map;
 import com.alibaba.nacos.client.logger.json.parser.JSONParser;
 import com.alibaba.nacos.client.logger.json.parser.ParseException;
 
-
-
 /**
- * @author FangYidong
+ * @author FangYidong
  */
 @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule")
 public class JSONValue {
-	/**
-	 * Parse JSON text into java object from the input source. 
-	 * Please use parseWithException() if you don't want to ignore the exception.
-	 * 
-	 * @see com.alibaba.nacos.client.logger.jsonparser.JSONParser#parse(Reader)
-	 * @see #parseWithException(Reader)
-	 * 
-	 * @param in
-	 * @return Instance of the following:
-	 *	com.alibaba.nacos.client.logger.jsonJSONObject,
-	 * 	com.alibaba.nacos.client.logger.jsonJSONArray,
-	 * 	java.lang.String,
-	 * 	java.lang.Number,
-	 * 	java.lang.Boolean,
-	 * 	null
-	 * 
-	 * @deprecated this method may throw an {@code Error} instead of returning
-	 * {@code null}; please use {@link JSONValue#parseWithException(Reader)}
-	 * instead
-	 */
-	public static Object parse(Reader in){
-		try{
-			JSONParser parser=new JSONParser();
-			return parser.parse(in);
-		}
-		catch(Exception e){
-			return null;
-		}
-	}
-	
-	/**
-	 * Parse JSON text into java object from the given string. 
-	 * Please use parseWithException() if you don't want to ignore the exception.
-	 * 
-	 * @see com.alibaba.nacos.client.logger.jsonparser.JSONParser#parse(Reader)
-	 * @see #parseWithException(Reader)
-	 * 
-	 * @param s
-	 * @return Instance of the following:
-	 *	com.alibaba.nacos.client.logger.jsonJSONObject,
-	 * 	com.alibaba.nacos.client.logger.jsonJSONArray,
-	 * 	java.lang.String,
-	 * 	java.lang.Number,
-	 * 	java.lang.Boolean,
-	 * 	null
-	 * 
-	 * @deprecated this method may throw an {@code Error} instead of returning
-	 * {@code null}; please use {@link JSONValue#parseWithException(String)}
-	 * instead
-	 */
-	public static Object parse(String s){
-		StringReader in=new StringReader(s);
-		return parse(in);
-	}
-	
-	/**
-	 * Parse JSON text into java object from the input source.
-	 * 
-	 * @see com.alibaba.nacos.client.logger.jsonparser.JSONParser
-	 * 
-	 * @param in
-	 * @return Instance of the following:
-	 * 	com.alibaba.nacos.client.logger.jsonJSONObject,
-	 * 	com.alibaba.nacos.client.logger.jsonJSONArray,
-	 * 	java.lang.String,
-	 * 	java.lang.Number,
-	 * 	java.lang.Boolean,
-	 * 	null
-	 * 
-	 * @throws IOException
-	 * @throws ParseException
-	 */
-	public static Object parseWithException(Reader in) throws IOException, ParseException {
-		JSONParser parser=new JSONParser();
-		return parser.parse(in);
-	}
-	
-	public static Object parseWithException(String s) throws ParseException{
-		JSONParser parser=new JSONParser();
-		return parser.parse(s);
-	}
-	
+    /**
+     * Parse JSON text into java object from the input source. Please use parseWithException() if you don't want to
+     * ignore the exception.
+     *
+     * @param in
+     * @return Instance of the following: com.alibaba.nacos.client.logger.jsonJSONObject,
+     * com.alibaba.nacos.client.logger.jsonJSONArray, java.lang.String, java.lang.Number, java.lang.Boolean, null
+     * @see com.alibaba.nacos.client.logger.jsonparser.JSONParser#parse(Reader)
+     * @see #parseWithException(Reader)
+     * @deprecated this method may throw an {@code Error} instead of returning {@code null}; please use {@link
+     * JSONValue#parseWithException(Reader)} instead
+     */
+    public static Object parse(Reader in) {
+        try {
+            JSONParser parser = new JSONParser();
+            return parser.parse(in);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * Parse JSON text into java object from the given string. Please use parseWithException() if you don't want to
+     * ignore the exception.
+     *
+     * @param s
+     * @return Instance of the following: com.alibaba.nacos.client.logger.jsonJSONObject,
+     * com.alibaba.nacos.client.logger.jsonJSONArray, java.lang.String, java.lang.Number, java.lang.Boolean, null
+     * @see com.alibaba.nacos.client.logger.jsonparser.JSONParser#parse(Reader)
+     * @see #parseWithException(Reader)
+     * @deprecated this method may throw an {@code Error} instead of returning {@code null}; please use {@link
+     * JSONValue#parseWithException(String)} instead
+     */
+    public static Object parse(String s) {
+        StringReader in = new StringReader(s);
+        return parse(in);
+    }
+
+    /**
+     * Parse JSON text into java object from the input source.
+     *
+     * @param in
+     * @return Instance of the following: com.alibaba.nacos.client.logger.jsonJSONObject,
+     * com.alibaba.nacos.client.logger.jsonJSONArray, java.lang.String, java.lang.Number, java.lang.Boolean, null
+     * @throws IOException
+     * @throws ParseException
+     * @see com.alibaba.nacos.client.logger.jsonparser.JSONParser
+     */
+    public static Object parseWithException(Reader in) throws IOException, ParseException {
+        JSONParser parser = new JSONParser();
+        return parser.parse(in);
+    }
+
+    public static Object parseWithException(String s) throws ParseException {
+        JSONParser parser = new JSONParser();
+        return parser.parse(s);
+    }
+
     /**
      * Encode an object into JSON text and write it to out.
      * 

- * If this object is a Map or a List, and it's also a JSONStreamAware or a JSONAware, JSONStreamAware or JSONAware will be considered firstly. + * If this object is a Map or a List, and it's also a JSONStreamAware or a JSONAware, JSONStreamAware or JSONAware + * will be considered firstly. *

- * DO NOT call this method from writeJSONString(Writer) of a class that implements both JSONStreamAware and (Map or List) with - * "this" as the first parameter, use JSONObject.writeJSONString(Map, Writer) or JSONArray.writeJSONString(List, Writer) instead. - * - * @see com.alibaba.nacos.client.logger.jsonJSONObject#writeJSONString(Map, Writer) - * @see com.alibaba.nacos.client.logger.jsonJSONArray#writeJSONString(List, Writer) - * + * DO NOT call this method from writeJSONString(Writer) of a class that implements both JSONStreamAware and (Map or + * List) with "this" as the first parameter, use JSONObject.writeJSONString(Map, Writer) or + * JSONArray.writeJSONString(List, Writer) instead. + * * @param value * @param writer + * @see com.alibaba.nacos.client.logger.jsonJSONObject#writeJSONString(Map, Writer) + * @see com.alibaba.nacos.client.logger.jsonJSONArray#writeJSONString(List, Writer) */ - public static void writeJSONString(Object value, Writer out) throws IOException { - if(value == null){ - out.write("null"); - return; - } - - if(value instanceof String){ - out.write('\"'); - out.write(escape((String)value)); - out.write('\"'); - return; - } - - if(value instanceof Double){ - if(((Double)value).isInfinite() || ((Double)value).isNaN()) { - out.write("null"); - } - else { - out.write(value.toString()); - } - return; - } - - if(value instanceof Float){ - if(((Float)value).isInfinite() || ((Float)value).isNaN()) { - out.write("null"); - } - else { - out.write(value.toString()); - } - return; - } - - if(value instanceof Number){ - out.write(value.toString()); - return; - } - - if(value instanceof Boolean){ - out.write(value.toString()); - return; - } - - if((value instanceof JSONStreamAware)){ - ((JSONStreamAware)value).writeJSONString(out); - return; - } - - if((value instanceof JSONAware)){ - out.write(((JSONAware)value).toJSONString()); - return; - } - - if(value instanceof Map){ - JSONObject.writeJSONString((Map)value, out); - return; - } - - if(value instanceof Collection){ - JSONArray.writeJSONString((Collection)value, out); + public static void writeJSONString(Object value, Writer out) throws IOException { + if (value == null) { + out.write("null"); return; - } - - if(value instanceof byte[]){ - JSONArray.writeJSONString((byte[])value, out); - return; - } - - if(value instanceof short[]){ - JSONArray.writeJSONString((short[])value, out); - return; - } - - if(value instanceof int[]){ - JSONArray.writeJSONString((int[])value, out); - return; - } - - if(value instanceof long[]){ - JSONArray.writeJSONString((long[])value, out); - return; - } - - if(value instanceof float[]){ - JSONArray.writeJSONString((float[])value, out); - return; - } - - if(value instanceof double[]){ - JSONArray.writeJSONString((double[])value, out); - return; - } - - if(value instanceof boolean[]){ - JSONArray.writeJSONString((boolean[])value, out); - return; - } - - if(value instanceof char[]){ - JSONArray.writeJSONString((char[])value, out); - return; - } - - if(value instanceof Object[]){ - JSONArray.writeJSONString((Object[])value, out); - return; - } - - out.write(value.toString()); - } + } - /** - * Convert an object to JSON text. - *

- * If this object is a Map or a List, and it's also a JSONAware, JSONAware will be considered firstly. - *

- * DO NOT call this method from toJSONString() of a class that implements both JSONAware and Map or List with - * "this" as the parameter, use JSONObject.toJSONString(Map) or JSONArray.toJSONString(List) instead. - * - * @see com.alibaba.nacos.client.logger.json.JSONObject#toJSONString(Map) - * - * @param value - * @return JSON text, or "null" if value is null or it's an NaN or an INF number. - */ - public static String toJSONString(Object value){ - final StringWriter writer = new StringWriter(); - - try{ - writeJSONString(value, writer); - return writer.toString(); - } catch(IOException e){ - // This should never happen for a StringWriter - throw new RuntimeException(e); - } - } + if (value instanceof String) { + out.write('\"'); + out.write(escape((String)value)); + out.write('\"'); + return; + } - /** - * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). - * @param s - * @return - */ - public static String escape(String s){ - if(s==null) { - return null; - } + if (value instanceof Double) { + if (((Double)value).isInfinite() || ((Double)value).isNaN()) { + out.write("null"); + } else { + out.write(value.toString()); + } + return; + } + + if (value instanceof Float) { + if (((Float)value).isInfinite() || ((Float)value).isNaN()) { + out.write("null"); + } else { + out.write(value.toString()); + } + return; + } + + if (value instanceof Number) { + out.write(value.toString()); + return; + } + + if (value instanceof Boolean) { + out.write(value.toString()); + return; + } + + if ((value instanceof JSONStreamAware)) { + ((JSONStreamAware)value).writeJSONString(out); + return; + } + + if ((value instanceof JSONAware)) { + out.write(((JSONAware)value).toJSONString()); + return; + } + + if (value instanceof Map) { + JSONObject.writeJSONString((Map)value, out); + return; + } + + if (value instanceof Collection) { + JSONArray.writeJSONString((Collection)value, out); + return; + } + + if (value instanceof byte[]) { + JSONArray.writeJSONString((byte[])value, out); + return; + } + + if (value instanceof short[]) { + JSONArray.writeJSONString((short[])value, out); + return; + } + + if (value instanceof int[]) { + JSONArray.writeJSONString((int[])value, out); + return; + } + + if (value instanceof long[]) { + JSONArray.writeJSONString((long[])value, out); + return; + } + + if (value instanceof float[]) { + JSONArray.writeJSONString((float[])value, out); + return; + } + + if (value instanceof double[]) { + JSONArray.writeJSONString((double[])value, out); + return; + } + + if (value instanceof boolean[]) { + JSONArray.writeJSONString((boolean[])value, out); + return; + } + + if (value instanceof char[]) { + JSONArray.writeJSONString((char[])value, out); + return; + } + + if (value instanceof Object[]) { + JSONArray.writeJSONString((Object[])value, out); + return; + } + + out.write(value.toString()); + } + + /** + * Convert an object to JSON text. + *

+ * If this object is a Map or a List, and it's also a JSONAware, JSONAware will be considered firstly. + *

+ * DO NOT call this method from toJSONString() of a class that implements both JSONAware and Map or List with "this" + * as the parameter, use JSONObject.toJSONString(Map) or JSONArray.toJSONString(List) instead. + * + * @param value + * @return JSON text, or "null" if value is null or it's an NaN or an INF number. + * @see com.alibaba.nacos.client.logger.json.JSONObject#toJSONString(Map) + */ + public static String toJSONString(Object value) { + final StringWriter writer = new StringWriter(); + + try { + writeJSONString(value, writer); + return writer.toString(); + } catch (IOException e) { + // This should never happen for a StringWriter + throw new RuntimeException(e); + } + } + + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). + * + * @param s + * @return + */ + public static String escape(String s) { + if (s == null) { + return null; + } StringBuffer sb = new StringBuffer(); escape(s, sb); return sb.toString(); } /** - * @param s - Must not be null. + * @param s - Must not be null. * @param sb */ static void escape(String s, StringBuffer sb) { - final int len = s.length(); - for(int i=0;i= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') - || (ch >= '\u2000' && ch <= '\u20FF'); - } - - private static int FOUR = 4; + final int len = s.length(); + for (int i = 0; i < len; i++) { + char ch = s.charAt(i); + switch (ch) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + case '/': + sb.append("\\/"); + break; + default: + //Reference: http://www.unicode.org/versions/Unicode5.1.0/ + if (isUnicodeChar(ch)) { + String ss = Integer.toHexString(ch); + sb.append("\\u"); + for (int k = 0; k < FOUR - ss.length(); k++) { + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } else { + sb.append(ch); + } + } + }//for + } + + private static boolean isUnicodeChar(char ch) { + return (ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') + || (ch >= '\u2000' && ch <= '\u20FF'); + } + + private static int FOUR = 4; } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContainerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContainerFactory.java index 0185fe166..606b1ad0a 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContainerFactory.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContainerFactory.java @@ -20,21 +20,24 @@ import java.util.Map; /** * Container factory for creating containers for JSON object and JSON array. - * + * + * @author FangYidong * @see com.alibaba.nacos.client.logger.json.parser.JSONParser#parse(java.io.Reader, ContainerFactory) - * - * @author FangYidong */ public interface ContainerFactory { - /** - * create json container - * @return A Map instance to store JSON object, or null if you want to use com.alibaba.nacos.client.logger.jsonJSONObject. - */ - Map createObjectContainer(); - - /** - * create array json container - * @return A List instance to store JSON array, or null if you want to use com.alibaba.nacos.client.logger.jsonJSONArray. - */ - List creatArrayContainer(); + /** + * create json container + * + * @return A Map instance to store JSON object, or null if you want to use com.alibaba.nacos.client.logger + * .jsonJSONObject. + */ + Map createObjectContainer(); + + /** + * create array json container + * + * @return A List instance to store JSON array, or null if you want to use com.alibaba.nacos.client.logger + * .jsonJSONArray. + */ + List creatArrayContainer(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContentHandler.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContentHandler.java index 09e9decad..bf961752a 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContentHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ContentHandler.java @@ -18,112 +18,100 @@ package com.alibaba.nacos.client.logger.json.parser; import java.io.IOException; /** - * A simplified and stoppable SAX-like content handler for stream processing of JSON text. - * + * A simplified and stoppable SAX-like content handler for stream processing of JSON text. + * + * @author FangYidong * @see org.xml.sax.ContentHandler * @see com.alibaba.nacos.client.logger.json.parser.JSONParser#parse(java.io.Reader, ContentHandler, boolean) - * - * @author FangYidong */ public interface ContentHandler { - /** - * Receive notification of the beginning of JSON processing. - * The parser will invoke this method only once. - * - * @throws ParseException - * - JSONParser will stop and throw the same exception to the caller when receiving this exception. + /** + * Receive notification of the beginning of JSON processing. The parser will invoke this method only once. + * + * @throws ParseException - JSONParser will stop and throw the same exception to the caller when receiving this + * exception. * @throws IOException - */ - void startJSON() throws ParseException, IOException; - - /** - * Receive notification of the end of JSON processing. - * - * @throws ParseException - * @throws IOException - */ - void endJSON() throws ParseException, IOException; - - /** - * Receive notification of the beginning of a JSON object. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * - JSONParser will stop and throw the same exception to the caller when receiving this exception. + */ + void startJSON() throws ParseException, IOException; + + /** + * Receive notification of the end of JSON processing. + * + * @throws ParseException + * @throws IOException + */ + void endJSON() throws ParseException, IOException; + + /** + * Receive notification of the beginning of a JSON object. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException - JSONParser will stop and throw the same exception to the caller when receiving this + * exception. * @throws IOException * @see #endJSON - */ - boolean startObject() throws ParseException, IOException; - - /** - * Receive notification of the end of a JSON object. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * @throws IOException + */ + boolean startObject() throws ParseException, IOException; + + /** + * Receive notification of the end of a JSON object. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * @throws IOException * @see #startObject - */ - boolean endObject() throws ParseException, IOException; - - /** - * Receive notification of the beginning of a JSON object entry. - * - * @param key - Key of a JSON object entry. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * @throws IOException + */ + boolean endObject() throws ParseException, IOException; + + /** + * Receive notification of the beginning of a JSON object entry. + * + * @param key - Key of a JSON object entry. + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * @throws IOException * @see #endObjectEntry - */ - boolean startObjectEntry(String key) throws ParseException, IOException; - - /** - * Receive notification of the end of the value of previous object entry. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * @throws IOException + */ + boolean startObjectEntry(String key) throws ParseException, IOException; + + /** + * Receive notification of the end of the value of previous object entry. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * @throws IOException * @see #startObjectEntry - */ - boolean endObjectEntry() throws ParseException, IOException; - - /** - * Receive notification of the beginning of a JSON array. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * @throws IOException + */ + boolean endObjectEntry() throws ParseException, IOException; + + /** + * Receive notification of the beginning of a JSON array. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * @throws IOException * @see #endArray - */ - boolean startArray() throws ParseException, IOException; - - /** - * Receive notification of the end of a JSON array. - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * @throws IOException + */ + boolean startArray() throws ParseException, IOException; + + /** + * Receive notification of the end of a JSON array. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * @throws IOException * @see #startArray - */ - boolean endArray() throws ParseException, IOException; - - /** - * Receive notification of the JSON primitive values: - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean - * null - * - * @param value - Instance of the following: - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean - * null - * - * @return false if the handler wants to stop parsing after return. - * @throws ParseException - * @throws IOException - */ - boolean primitive(Object value) throws ParseException, IOException; - + */ + boolean endArray() throws ParseException, IOException; + + /** + * Receive notification of the JSON primitive values: java.lang.String, java.lang.Number, java.lang.Boolean null + * + * @param value - Instance of the following: java.lang.String, java.lang.Number, java.lang.Boolean null + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * @throws IOException + */ + boolean primitive(Object value) throws ParseException, IOException; + } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/JSONParser.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/JSONParser.java index a7bcee0d6..25c04412e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/JSONParser.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/JSONParser.java @@ -29,546 +29,520 @@ import java.util.Map; import com.alibaba.nacos.client.logger.json.JSONArray; import com.alibaba.nacos.client.logger.json.JSONObject; - /** * Parser for JSON text. Please note that JSONParser is NOT thread-safe. - * - * @author FangYidong + * + * @author FangYidong */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class JSONParser { - public static final int S_INIT=0; - public static final int S_IN_FINISHED_VALUE=1; - public static final int S_IN_OBJECT=2; - public static final int S_IN_ARRAY=3; - public static final int S_PASSED_PAIR_KEY=4; - public static final int S_IN_PAIR_VALUE=5; - public static final int S_END=6; - public static final int S_IN_ERROR=-1; - - private LinkedList handlerStatusStack; - private Yylex lexer = new Yylex((Reader)null); - private Yytoken token = null; - private int status = S_INIT; - - private int peekStatus(LinkedList statusStack){ - if(statusStack.size()==0) { - return -1; - } - Integer status=(Integer)statusStack.getFirst(); - return status.intValue(); - } - + public static final int S_INIT = 0; + public static final int S_IN_FINISHED_VALUE = 1; + public static final int S_IN_OBJECT = 2; + public static final int S_IN_ARRAY = 3; + public static final int S_PASSED_PAIR_KEY = 4; + public static final int S_IN_PAIR_VALUE = 5; + public static final int S_END = 6; + public static final int S_IN_ERROR = -1; + + private LinkedList handlerStatusStack; + private Yylex lexer = new Yylex((Reader)null); + private Yytoken token = null; + private int status = S_INIT; + + private int peekStatus(LinkedList statusStack) { + if (statusStack.size() == 0) { + return -1; + } + Integer status = (Integer)statusStack.getFirst(); + return status.intValue(); + } + /** - * Reset the parser to the initial state without resetting the underlying reader. - * + * Reset the parser to the initial state without resetting the underlying reader. */ - public void reset(){ + public void reset() { token = null; status = S_INIT; handlerStatusStack = null; } - + /** * Reset the parser to the initial state with a new character reader. - * + * * @param in - The new character reader. * @throws IOException * @throws ParseException */ - public void reset(Reader in){ - lexer.yyreset(in); - reset(); - } - - /** - * @return The position of the beginning of the current token. - */ - public int getPosition(){ - return lexer.getPosition(); - } - - public Object parse(String s) throws ParseException{ - return parse(s, (ContainerFactory)null); - } - - public Object parse(String s, ContainerFactory containerFactory) throws ParseException{ - StringReader in=new StringReader(s); - try{ - return parse(in, containerFactory); - } - catch(IOException ie){ - /* - * Actually it will never happen. - */ - throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); - } - } - - public Object parse(Reader in) throws IOException, ParseException{ - return parse(in, (ContainerFactory)null); - } - - /** - * Parse JSON text into java object from the input source. - * - * @param in + public void reset(Reader in) { + lexer.yyreset(in); + reset(); + } + + /** + * @return The position of the beginning of the current token. + */ + public int getPosition() { + return lexer.getPosition(); + } + + public Object parse(String s) throws ParseException { + return parse(s, (ContainerFactory)null); + } + + public Object parse(String s, ContainerFactory containerFactory) throws ParseException { + StringReader in = new StringReader(s); + try { + return parse(in, containerFactory); + } catch (IOException ie) { + /* + * Actually it will never happen. + */ + throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); + } + } + + public Object parse(Reader in) throws IOException, ParseException { + return parse(in, (ContainerFactory)null); + } + + /** + * Parse JSON text into java object from the input source. + * + * @param in * @param containerFactory - Use this factory to createyour own JSON object and JSON array containers. - * @return Instance of the following: - * com.alibaba.nacos.client.logger.jsonJSONObject, - * com.alibaba.nacos.client.logger.jsonJSONArray, - * java.lang.String, - * java.lang.Number, - * java.lang.Boolean, - * null - * - * @throws IOException - * @throws ParseException - */ - public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, ParseException{ - reset(in); - LinkedList statusStack = new LinkedList(); - LinkedList valueStack = new LinkedList(); - - try{ - do{ - nextToken(); - switch(status){ - case S_INIT: - switch(token.type){ - case Yytoken.TYPE_VALUE: - status=S_IN_FINISHED_VALUE; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(token.value); - break; - case Yytoken.TYPE_LEFT_BRACE: - status=S_IN_OBJECT; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(createObjectContainer(containerFactory)); - break; - case Yytoken.TYPE_LEFT_SQUARE: - status=S_IN_ARRAY; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(createArrayContainer(containerFactory)); - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - - case S_IN_FINISHED_VALUE: - if(token.type==Yytoken.TYPE_EOF) { - return valueStack.removeFirst(); - } - else { - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - case S_IN_OBJECT: - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - if(token.value instanceof String){ - String key=(String)token.value; - valueStack.addFirst(key); - status=S_PASSED_PAIR_KEY; - statusStack.addFirst(Integer.valueOf(status)); - } - else{ - status=S_IN_ERROR; - } - break; - case Yytoken.TYPE_RIGHT_BRACE: - if(valueStack.size()>1){ - statusStack.removeFirst(); - valueStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - break; - default: - status=S_IN_ERROR; - break; - }//inner switch - break; - - case S_PASSED_PAIR_KEY: - switch(token.type){ - case Yytoken.TYPE_COLON: - break; - case Yytoken.TYPE_VALUE: - statusStack.removeFirst(); - String key=(String)valueStack.removeFirst(); - Map parent=(Map)valueStack.getFirst(); - parent.put(key,token.value); - status=peekStatus(statusStack); - break; - case Yytoken.TYPE_LEFT_SQUARE: - statusStack.removeFirst(); - key=(String)valueStack.removeFirst(); - parent=(Map)valueStack.getFirst(); - List newArray=createArrayContainer(containerFactory); - parent.put(key,newArray); - status=S_IN_ARRAY; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(newArray); - break; - case Yytoken.TYPE_LEFT_BRACE: - statusStack.removeFirst(); - key=(String)valueStack.removeFirst(); - parent=(Map)valueStack.getFirst(); - Map newObject=createObjectContainer(containerFactory); - parent.put(key,newObject); - status=S_IN_OBJECT; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(newObject); - break; - default: - status=S_IN_ERROR; - } - break; - - case S_IN_ARRAY: - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - List val=(List)valueStack.getFirst(); - val.add(token.value); - break; - case Yytoken.TYPE_RIGHT_SQUARE: - if(valueStack.size()>1){ - statusStack.removeFirst(); - valueStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - break; - case Yytoken.TYPE_LEFT_BRACE: - val=(List)valueStack.getFirst(); - Map newObject=createObjectContainer(containerFactory); - val.add(newObject); - status=S_IN_OBJECT; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(newObject); - break; - case Yytoken.TYPE_LEFT_SQUARE: - val=(List)valueStack.getFirst(); - List newArray=createArrayContainer(containerFactory); - val.add(newArray); - status=S_IN_ARRAY; - statusStack.addFirst(Integer.valueOf(status)); - valueStack.addFirst(newArray); - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - case S_IN_ERROR: - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - default: - break; - }//switch - if(status==S_IN_ERROR){ - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - }while(token.type!=Yytoken.TYPE_EOF); - } - catch(IOException ie){ - throw ie; - } - - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - - private void nextToken() throws ParseException, IOException{ - token = lexer.yylex(); - if(token == null) { - token = new Yytoken(Yytoken.TYPE_EOF, null); - } - } - - private Map createObjectContainer(ContainerFactory containerFactory){ - if(containerFactory == null) { - return new JSONObject(); - } - Map m = containerFactory.createObjectContainer(); - - if(m == null) { - return new JSONObject(); - } - return m; - } - - private List createArrayContainer(ContainerFactory containerFactory){ - if(containerFactory == null) { - return new JSONArray(); - } - List l = containerFactory.creatArrayContainer(); - - if(l == null) { - return new JSONArray(); - } - return l; - } - - public void parse(String s, ContentHandler contentHandler) throws ParseException{ - parse(s, contentHandler, false); - } - - public void parse(String s, ContentHandler contentHandler, boolean isResume) throws ParseException{ - StringReader in=new StringReader(s); - try{ - parse(in, contentHandler, isResume); - } - catch(IOException ie){ - /* - * Actually it will never happen. - */ - throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); - } - } - - public void parse(Reader in, ContentHandler contentHandler) throws IOException, ParseException{ - parse(in, contentHandler, false); - } - - /** - * Stream processing of JSON text. - * - * @see ContentHandler - * - * @param in - * @param contentHandler - * @param isResume - Indicates if it continues previous parsing operation. - * If set to true, resume parsing the old stream, and parameter 'in' will be ignored. - * If this method is called for the first time in this instance, isResume will be ignored. - * - * @throws IOException - * @throws ParseException - */ - public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException, ParseException{ - if(!isResume){ - reset(in); - handlerStatusStack = new LinkedList(); - } - else{ - if(handlerStatusStack == null){ - isResume = false; - reset(in); - handlerStatusStack = new LinkedList(); - } - } - - LinkedList statusStack = handlerStatusStack; - - try{ - do{ - switch(status){ - case S_INIT: - contentHandler.startJSON(); - nextToken(); - switch(token.type){ - case Yytoken.TYPE_VALUE: - status=S_IN_FINISHED_VALUE; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.primitive(token.value)) { - return; - } - break; - case Yytoken.TYPE_LEFT_BRACE: - status=S_IN_OBJECT; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startObject()) { - return; - } - break; - case Yytoken.TYPE_LEFT_SQUARE: - status=S_IN_ARRAY; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startArray()) { - return; - } - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - - case S_IN_FINISHED_VALUE: - nextToken(); - if(token.type==Yytoken.TYPE_EOF){ - contentHandler.endJSON(); - status = S_END; - return; - } - else{ - status = S_IN_ERROR; - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - - case S_IN_OBJECT: - nextToken(); - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - if(token.value instanceof String){ - String key=(String)token.value; - status=S_PASSED_PAIR_KEY; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startObjectEntry(key)) { - return; - } - } - else{ - status=S_IN_ERROR; - } - break; - case Yytoken.TYPE_RIGHT_BRACE: - if(statusStack.size()>1){ - statusStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - if(!contentHandler.endObject()) { - return; - } - break; - default: - status=S_IN_ERROR; - break; - }//inner switch - break; - - case S_PASSED_PAIR_KEY: - nextToken(); - switch(token.type){ - case Yytoken.TYPE_COLON: - break; - case Yytoken.TYPE_VALUE: - statusStack.removeFirst(); - status=peekStatus(statusStack); - if(!contentHandler.primitive(token.value)) { - return; - } - if(!contentHandler.endObjectEntry()) { - return; - } - break; - case Yytoken.TYPE_LEFT_SQUARE: - statusStack.removeFirst(); - statusStack.addFirst(Integer.valueOf(S_IN_PAIR_VALUE)); - status=S_IN_ARRAY; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startArray()) { - return; - } - break; - case Yytoken.TYPE_LEFT_BRACE: - statusStack.removeFirst(); - statusStack.addFirst(Integer.valueOf(S_IN_PAIR_VALUE)); - status=S_IN_OBJECT; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startObject()) { - return; - } - break; - default: - status=S_IN_ERROR; - } - break; - - case S_IN_PAIR_VALUE: - /* - * S_IN_PAIR_VALUE is just a marker to indicate the end of an object entry, it doesn't proccess any token, - * therefore delay consuming token until next round. - */ - statusStack.removeFirst(); - status = peekStatus(statusStack); - if(!contentHandler.endObjectEntry()) { - return; - } - break; - - case S_IN_ARRAY: - nextToken(); - switch(token.type){ - case Yytoken.TYPE_COMMA: - break; - case Yytoken.TYPE_VALUE: - if(!contentHandler.primitive(token.value)) { - return; - } - break; - case Yytoken.TYPE_RIGHT_SQUARE: - if(statusStack.size()>1){ - statusStack.removeFirst(); - status=peekStatus(statusStack); - } - else{ - status=S_IN_FINISHED_VALUE; - } - if(!contentHandler.endArray()) { - return; - } - break; - case Yytoken.TYPE_LEFT_BRACE: - status=S_IN_OBJECT; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startObject()) { - return; - } - break; - case Yytoken.TYPE_LEFT_SQUARE: - status=S_IN_ARRAY; - statusStack.addFirst(Integer.valueOf(status)); - if(!contentHandler.startArray()) { - return; - } - break; - default: - status=S_IN_ERROR; - }//inner switch - break; - - case S_END: - return; - - case S_IN_ERROR: - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - default: - break; - }//switch - if(status==S_IN_ERROR){ - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } - }while(token.type!=Yytoken.TYPE_EOF); - } - catch(IOException ie){ - status = S_IN_ERROR; - throw ie; - } - catch(ParseException pe){ - status = S_IN_ERROR; - throw pe; - } - catch(RuntimeException re){ - status = S_IN_ERROR; - throw re; - } - catch(Error e){ - status = S_IN_ERROR; - throw e; - } - - status = S_IN_ERROR; - throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); - } + * @return Instance of the following: com.alibaba.nacos.client.logger.jsonJSONObject, + * com.alibaba.nacos.client.logger.jsonJSONArray, java.lang.String, java.lang.Number, java.lang.Boolean, null + * @throws IOException + * @throws ParseException + */ + public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, ParseException { + reset(in); + LinkedList statusStack = new LinkedList(); + LinkedList valueStack = new LinkedList(); + + try { + do { + nextToken(); + switch (status) { + case S_INIT: + switch (token.type) { + case Yytoken.TYPE_VALUE: + status = S_IN_FINISHED_VALUE; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(token.value); + break; + case Yytoken.TYPE_LEFT_BRACE: + status = S_IN_OBJECT; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(createObjectContainer(containerFactory)); + break; + case Yytoken.TYPE_LEFT_SQUARE: + status = S_IN_ARRAY; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(createArrayContainer(containerFactory)); + break; + default: + status = S_IN_ERROR; + }//inner switch + break; + + case S_IN_FINISHED_VALUE: + if (token.type == Yytoken.TYPE_EOF) { + return valueStack.removeFirst(); + } else { + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + case S_IN_OBJECT: + switch (token.type) { + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if (token.value instanceof String) { + String key = (String)token.value; + valueStack.addFirst(key); + status = S_PASSED_PAIR_KEY; + statusStack.addFirst(Integer.valueOf(status)); + } else { + status = S_IN_ERROR; + } + break; + case Yytoken.TYPE_RIGHT_BRACE: + if (valueStack.size() > 1) { + statusStack.removeFirst(); + valueStack.removeFirst(); + status = peekStatus(statusStack); + } else { + status = S_IN_FINISHED_VALUE; + } + break; + default: + status = S_IN_ERROR; + break; + }//inner switch + break; + + case S_PASSED_PAIR_KEY: + switch (token.type) { + case Yytoken.TYPE_COLON: + break; + case Yytoken.TYPE_VALUE: + statusStack.removeFirst(); + String key = (String)valueStack.removeFirst(); + Map parent = (Map)valueStack.getFirst(); + parent.put(key, token.value); + status = peekStatus(statusStack); + break; + case Yytoken.TYPE_LEFT_SQUARE: + statusStack.removeFirst(); + key = (String)valueStack.removeFirst(); + parent = (Map)valueStack.getFirst(); + List newArray = createArrayContainer(containerFactory); + parent.put(key, newArray); + status = S_IN_ARRAY; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(newArray); + break; + case Yytoken.TYPE_LEFT_BRACE: + statusStack.removeFirst(); + key = (String)valueStack.removeFirst(); + parent = (Map)valueStack.getFirst(); + Map newObject = createObjectContainer(containerFactory); + parent.put(key, newObject); + status = S_IN_OBJECT; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(newObject); + break; + default: + status = S_IN_ERROR; + } + break; + + case S_IN_ARRAY: + switch (token.type) { + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + List val = (List)valueStack.getFirst(); + val.add(token.value); + break; + case Yytoken.TYPE_RIGHT_SQUARE: + if (valueStack.size() > 1) { + statusStack.removeFirst(); + valueStack.removeFirst(); + status = peekStatus(statusStack); + } else { + status = S_IN_FINISHED_VALUE; + } + break; + case Yytoken.TYPE_LEFT_BRACE: + val = (List)valueStack.getFirst(); + Map newObject = createObjectContainer(containerFactory); + val.add(newObject); + status = S_IN_OBJECT; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(newObject); + break; + case Yytoken.TYPE_LEFT_SQUARE: + val = (List)valueStack.getFirst(); + List newArray = createArrayContainer(containerFactory); + val.add(newArray); + status = S_IN_ARRAY; + statusStack.addFirst(Integer.valueOf(status)); + valueStack.addFirst(newArray); + break; + default: + status = S_IN_ERROR; + }//inner switch + break; + case S_IN_ERROR: + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + default: + break; + }//switch + if (status == S_IN_ERROR) { + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + } while (token.type != Yytoken.TYPE_EOF); + } catch (IOException ie) { + throw ie; + } + + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + + private void nextToken() throws ParseException, IOException { + token = lexer.yylex(); + if (token == null) { + token = new Yytoken(Yytoken.TYPE_EOF, null); + } + } + + private Map createObjectContainer(ContainerFactory containerFactory) { + if (containerFactory == null) { + return new JSONObject(); + } + Map m = containerFactory.createObjectContainer(); + + if (m == null) { + return new JSONObject(); + } + return m; + } + + private List createArrayContainer(ContainerFactory containerFactory) { + if (containerFactory == null) { + return new JSONArray(); + } + List l = containerFactory.creatArrayContainer(); + + if (l == null) { + return new JSONArray(); + } + return l; + } + + public void parse(String s, ContentHandler contentHandler) throws ParseException { + parse(s, contentHandler, false); + } + + public void parse(String s, ContentHandler contentHandler, boolean isResume) throws ParseException { + StringReader in = new StringReader(s); + try { + parse(in, contentHandler, isResume); + } catch (IOException ie) { + /* + * Actually it will never happen. + */ + throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); + } + } + + public void parse(Reader in, ContentHandler contentHandler) throws IOException, ParseException { + parse(in, contentHandler, false); + } + + /** + * Stream processing of JSON text. + * + * @param in + * @param contentHandler + * @param isResume - Indicates if it continues previous parsing operation. If set to true, resume parsing the + * old stream, and parameter 'in' will be ignored. If this method is called for the first time + * in this instance, isResume will be ignored. + * @throws IOException + * @throws ParseException + * @see ContentHandler + */ + public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException, ParseException { + if (!isResume) { + reset(in); + handlerStatusStack = new LinkedList(); + } else { + if (handlerStatusStack == null) { + isResume = false; + reset(in); + handlerStatusStack = new LinkedList(); + } + } + + LinkedList statusStack = handlerStatusStack; + + try { + do { + switch (status) { + case S_INIT: + contentHandler.startJSON(); + nextToken(); + switch (token.type) { + case Yytoken.TYPE_VALUE: + status = S_IN_FINISHED_VALUE; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.primitive(token.value)) { + return; + } + break; + case Yytoken.TYPE_LEFT_BRACE: + status = S_IN_OBJECT; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startObject()) { + return; + } + break; + case Yytoken.TYPE_LEFT_SQUARE: + status = S_IN_ARRAY; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startArray()) { + return; + } + break; + default: + status = S_IN_ERROR; + }//inner switch + break; + + case S_IN_FINISHED_VALUE: + nextToken(); + if (token.type == Yytoken.TYPE_EOF) { + contentHandler.endJSON(); + status = S_END; + return; + } else { + status = S_IN_ERROR; + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + + case S_IN_OBJECT: + nextToken(); + switch (token.type) { + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if (token.value instanceof String) { + String key = (String)token.value; + status = S_PASSED_PAIR_KEY; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startObjectEntry(key)) { + return; + } + } else { + status = S_IN_ERROR; + } + break; + case Yytoken.TYPE_RIGHT_BRACE: + if (statusStack.size() > 1) { + statusStack.removeFirst(); + status = peekStatus(statusStack); + } else { + status = S_IN_FINISHED_VALUE; + } + if (!contentHandler.endObject()) { + return; + } + break; + default: + status = S_IN_ERROR; + break; + }//inner switch + break; + + case S_PASSED_PAIR_KEY: + nextToken(); + switch (token.type) { + case Yytoken.TYPE_COLON: + break; + case Yytoken.TYPE_VALUE: + statusStack.removeFirst(); + status = peekStatus(statusStack); + if (!contentHandler.primitive(token.value)) { + return; + } + if (!contentHandler.endObjectEntry()) { + return; + } + break; + case Yytoken.TYPE_LEFT_SQUARE: + statusStack.removeFirst(); + statusStack.addFirst(Integer.valueOf(S_IN_PAIR_VALUE)); + status = S_IN_ARRAY; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startArray()) { + return; + } + break; + case Yytoken.TYPE_LEFT_BRACE: + statusStack.removeFirst(); + statusStack.addFirst(Integer.valueOf(S_IN_PAIR_VALUE)); + status = S_IN_OBJECT; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startObject()) { + return; + } + break; + default: + status = S_IN_ERROR; + } + break; + + case S_IN_PAIR_VALUE: + /* + * S_IN_PAIR_VALUE is just a marker to indicate the end of an object entry, it doesn't proccess any token, + * therefore delay consuming token until next round. + */ + statusStack.removeFirst(); + status = peekStatus(statusStack); + if (!contentHandler.endObjectEntry()) { + return; + } + break; + + case S_IN_ARRAY: + nextToken(); + switch (token.type) { + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if (!contentHandler.primitive(token.value)) { + return; + } + break; + case Yytoken.TYPE_RIGHT_SQUARE: + if (statusStack.size() > 1) { + statusStack.removeFirst(); + status = peekStatus(statusStack); + } else { + status = S_IN_FINISHED_VALUE; + } + if (!contentHandler.endArray()) { + return; + } + break; + case Yytoken.TYPE_LEFT_BRACE: + status = S_IN_OBJECT; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startObject()) { + return; + } + break; + case Yytoken.TYPE_LEFT_SQUARE: + status = S_IN_ARRAY; + statusStack.addFirst(Integer.valueOf(status)); + if (!contentHandler.startArray()) { + return; + } + break; + default: + status = S_IN_ERROR; + }//inner switch + break; + + case S_END: + return; + + case S_IN_ERROR: + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + default: + break; + }//switch + if (status == S_IN_ERROR) { + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + } while (token.type != Yytoken.TYPE_EOF); + } catch (IOException ie) { + status = S_IN_ERROR; + throw ie; + } catch (ParseException pe) { + status = S_IN_ERROR; + throw pe; + } catch (RuntimeException re) { + status = S_IN_ERROR; + throw re; + } catch (Error e) { + status = S_IN_ERROR; + throw e; + } + + status = S_IN_ERROR; + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ParseException.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ParseException.java index bd8d02c2a..10430345d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ParseException.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/ParseException.java @@ -17,89 +17,87 @@ package com.alibaba.nacos.client.logger.json.parser; /** * ParseException explains why and where the error occurs in source JSON text. - * - * @author FangYidong * + * @author FangYidong */ public class ParseException extends Exception { - private static final long serialVersionUID = -7880698968187728547L; - - public static final int ERROR_UNEXPECTED_CHAR = 0; - public static final int ERROR_UNEXPECTED_TOKEN = 1; - public static final int ERROR_UNEXPECTED_EXCEPTION = 2; + private static final long serialVersionUID = -7880698968187728547L; - private int errorType; - private Object unexpectedObject; - private int position; - - public ParseException(int errorType){ - this(-1, errorType, null); - } - - public ParseException(int errorType, Object unexpectedObject){ - this(-1, errorType, unexpectedObject); - } - - public ParseException(int position, int errorType, Object unexpectedObject){ - this.position = position; - this.errorType = errorType; - this.unexpectedObject = unexpectedObject; - } - - public int getErrorType() { - return errorType; - } - - public void setErrorType(int errorType) { - this.errorType = errorType; - } - - /** - * @see com.alibaba.nacos.client.logger.json.parser.JSONParser#getPosition() - * - * @return The character position (starting with 0) of the input where the error occurs. - */ - public int getPosition() { - return position; - } - - public void setPosition(int position) { - this.position = position; - } - - /** - * @see com.alibaba.nacos.client.logger.json.parser.Yytoken - * - * @return One of the following base on the value of errorType: - * ERROR_UNEXPECTED_CHAR java.lang.Character - * ERROR_UNEXPECTED_TOKEN com.alibaba.nacos.client.logger.jsonparser.Yytoken - * ERROR_UNEXPECTED_EXCEPTION java.lang.Exception - */ - public Object getUnexpectedObject() { - return unexpectedObject; - } - - public void setUnexpectedObject(Object unexpectedObject) { - this.unexpectedObject = unexpectedObject; - } - - public String getMessage() { - StringBuffer sb = new StringBuffer(); - - switch(errorType){ - case ERROR_UNEXPECTED_CHAR: - sb.append("Unexpected character (").append(unexpectedObject).append(") at position ").append(position).append("."); - break; - case ERROR_UNEXPECTED_TOKEN: - sb.append("Unexpected token ").append(unexpectedObject).append(" at position ").append(position).append("."); - break; - case ERROR_UNEXPECTED_EXCEPTION: - sb.append("Unexpected exception at position ").append(position).append(": ").append(unexpectedObject); - break; - default: - sb.append("Unkown error at position ").append(position).append("."); - break; - } - return sb.toString(); - } + public static final int ERROR_UNEXPECTED_CHAR = 0; + public static final int ERROR_UNEXPECTED_TOKEN = 1; + public static final int ERROR_UNEXPECTED_EXCEPTION = 2; + + private int errorType; + private Object unexpectedObject; + private int position; + + public ParseException(int errorType) { + this(-1, errorType, null); + } + + public ParseException(int errorType, Object unexpectedObject) { + this(-1, errorType, unexpectedObject); + } + + public ParseException(int position, int errorType, Object unexpectedObject) { + this.position = position; + this.errorType = errorType; + this.unexpectedObject = unexpectedObject; + } + + public int getErrorType() { + return errorType; + } + + public void setErrorType(int errorType) { + this.errorType = errorType; + } + + /** + * @return The character position (starting with 0) of the input where the error occurs. + * @see com.alibaba.nacos.client.logger.json.parser.JSONParser#getPosition() + */ + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } + + /** + * @return One of the following base on the value of errorType: ERROR_UNEXPECTED_CHAR java.lang.Character + * ERROR_UNEXPECTED_TOKEN com.alibaba.nacos.client.logger.jsonparser.Yytoken ERROR_UNEXPECTED_EXCEPTION + * java.lang.Exception + * @see com.alibaba.nacos.client.logger.json.parser.Yytoken + */ + public Object getUnexpectedObject() { + return unexpectedObject; + } + + public void setUnexpectedObject(Object unexpectedObject) { + this.unexpectedObject = unexpectedObject; + } + + public String getMessage() { + StringBuffer sb = new StringBuffer(); + + switch (errorType) { + case ERROR_UNEXPECTED_CHAR: + sb.append("Unexpected character (").append(unexpectedObject).append(") at position ").append(position) + .append("."); + break; + case ERROR_UNEXPECTED_TOKEN: + sb.append("Unexpected token ").append(unexpectedObject).append(" at position ").append(position).append( + "."); + break; + case ERROR_UNEXPECTED_EXCEPTION: + sb.append("Unexpected exception at position ").append(position).append(": ").append(unexpectedObject); + break; + default: + sb.append("Unkown error at position ").append(position).append("."); + break; + } + return sb.toString(); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yylex.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yylex.java index 92df931e7..f988c7c28 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yylex.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yylex.java @@ -23,34 +23,38 @@ import java.io.UnsupportedEncodingException; /** * Yylex - * @author Nacos * + * @author Nacos */ class Yylex { - /** This character denotes the end of file */ + /** + * This character denotes the end of file + */ public static final int YYEOF = -1; - /** initial size of the lookahead buffer */ + /** + * initial size of the lookahead buffer + */ private static final int ZZ_BUFFERSIZE = 16384; - /** lexical states */ + /** + * lexical states + */ public static final int YYINITIAL = 0; public static final int STRING_BEGIN = 2; /** - * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l ZZ_LEXSTATE[l+1] is the state in the DFA for the + * lexical state l at the beginning of a line l is of the form l = 2*k, k a non negative integer */ - private static final int ZZ_LEXSTATE[] = { 0, 0, 1, 1 }; + private static final int ZZ_LEXSTATE[] = {0, 0, 1, 1}; /** * Translates characters to character classes */ private static final String ZZ_CMAP_PACKED = - "\11\0\1\7\1\7\2\0\1\7\22\0\1\7\1\0\1\11\10\0" + "\1\6\1\31\1\2\1\4\1\12\12\3\1\32\6\0\4\1\1\5" + "\11\0\1\7\1\7\2\0\1\7\22\0\1\7\1\0\1\11\10\0" + "\1\6\1\31\1\2\1\4\1\12\12\3\1\32\6\0\4\1\1\5" + "\1\1\24\0\1\27\1\10\1\30\3\0\1\22\1\13\2\1\1\21" + "\1\14\5\0\1\23\1\0\1\15\3\0\1\16\1\24\1\17\1\20" + "\5\0\1\25\1\0\1\26\uff82\0"; @@ -65,7 +69,7 @@ class Yylex { private static final int[] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = - "\2\0\2\1\1\2\1\3\1\4\3\1\1\5\1\6" + "\1\7\1\10\1\11\1\12\1\13\1\14\1\15\5\0" + "\2\0\2\1\1\2\1\3\1\4\3\1\1\5\1\6" + "\1\7\1\10\1\11\1\12\1\13\1\14\1\15\5\0" + "\1\14\1\16\1\17\1\20\1\21\1\22\1\23\1\24" + "\1\0\1\25\1\0\1\25\4\0\1\26\1\27\2\0" + "\1\30"; private static int[] zzUnpackAction() { @@ -82,7 +86,7 @@ class Yylex { while (i < l) { int count = packed.charAt(i++); int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); + do { result[j++] = value; } while (--count > 0); } return j; } @@ -93,7 +97,7 @@ class Yylex { private static final int[] ZZ_ROWMAP = zzUnpackRowMap(); private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\33\0\66\0\121\0\154\0\207\0\66\0\242" + "\0\275\0\330\0\66\0\66\0\66\0\66\0\66\0\66" + "\0\0\0\33\0\66\0\121\0\154\0\207\0\66\0\242" + "\0\275\0\330\0\66\0\66\0\66\0\66\0\66\0\66" + "\0\363\0\u010e\0\66\0\u0129\0\u0144\0\u015f\0\u017a\0\u0195" + "\0\66\0\66\0\66\0\66\0\66\0\66\0\66\0\66" + "\0\u01b0\0\u01cb\0\u01e6\0\u01e6\0\u0201\0\u021c\0\u0237\0\u0252" + "\0\66\0\66\0\u026d\0\u0288\0\66"; @@ -118,42 +122,42 @@ class Yylex { /** * The transition table of the DFA */ - private static final int ZZ_TRANS[] = { 2, 2, 3, 4, 2, 2, 2, 5, 2, 6, 2, 2, 7, 8, 2, 9, 2, 2, 2, 2, 2, 10, 11, 12, - 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 19, 20, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 16, 16, 16, 16, 16, 16, 16, -1, - -1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, -1, - -1, -1, -1, -1, -1, -1, -1, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 34, 35, - -1, -1, 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, 39, -1, 39, -1, -1, -1, -1, - -1, 39, 39, -1, -1, -1, -1, 39, 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 33, -1, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 42, -1, 42, -1, 42, -1, -1, -1, -1, -1, 42, 42, -1, -1, -1, -1, 42, 42, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, 43, -1, 43, -1, -1, -1, -1, -1, - 43, 43, -1, -1, -1, -1, 43, 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, -1, - 44, -1, 44, -1, -1, -1, -1, -1, 44, 44, -1, -1, -1, -1, 44, 44, -1, -1, -1, - -1, -1, -1, -1, -1, }; + private static final int ZZ_TRANS[] = {2, 2, 3, 4, 2, 2, 2, 5, 2, 6, 2, 2, 7, 8, 2, 9, 2, 2, 2, 2, 2, 10, 11, 12, + 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 19, 20, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 16, 16, 16, 16, 16, 16, 16, -1, + -1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, -1, + -1, -1, -1, -1, -1, -1, -1, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 34, 35, + -1, -1, 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 37, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, 39, -1, 39, -1, -1, -1, -1, + -1, 39, 39, -1, -1, -1, -1, 39, 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, -1, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 42, -1, 42, -1, 42, -1, -1, -1, -1, -1, 42, 42, -1, -1, -1, -1, 42, 42, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, 43, -1, 43, -1, -1, -1, -1, -1, + 43, 43, -1, -1, -1, -1, 43, 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, -1, + 44, -1, 44, -1, -1, -1, -1, -1, 44, 44, -1, -1, -1, -1, 44, 44, -1, -1, -1, + -1, -1, -1, -1, -1,}; /** * error codes @@ -163,10 +167,10 @@ class Yylex { private static final int ZZ_PUSHBACK_2BIG = 2; private static final int NIGTY = 90; /** - * error messages for the codes above + * error messages for the codes above */ - private static final String[] ZZ_ERROR_MSG = { "Unkown internal scanner error", "Error: could not match input", - "Error: pushback value was too large" }; + private static final String[] ZZ_ERROR_MSG = {"Unkown internal scanner error", "Error: could not match input", + "Error: pushback value was too large"}; /** * ZZ_ATTRIBUTE[aState] contains the attributes of state aState @@ -174,7 +178,7 @@ class Yylex { private static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = - "\2\0\1\11\3\1\1\11\3\1\6\11\2\1\1\11" + "\5\0\10\11\1\0\1\1\1\0\1\1\4\0\2\11" + "\2\0\1\11"; + "\2\0\1\11\3\1\1\11\3\1\6\11\2\1\1\11" + "\5\0\10\11\1\0\1\1\1\0\1\1\4\0\2\11" + "\2\0\1\11"; private static int[] zzUnpackAttribute() { int[] result = new int[45]; @@ -185,50 +189,64 @@ class Yylex { private static int zzUnpackAttribute(String packed, int offset, int[] result) { int i = 0; - int j = offset; + int j = offset; int l = packed.length(); while (i < l) { int count = packed.charAt(i++); int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); + do { result[j++] = value; } while (--count > 0); } return j; } - /** the input device */ + /** + * the input device + */ private java.io.Reader zzReader; - /** the current state of the DFA */ + /** + * the current state of the DFA + */ private int zzState; - /** the current lexical state */ + /** + * the current lexical state + */ private int zzLexicalState = YYINITIAL; /** - * this buffer contains the current text to be matched and is - * the source of the yytext() string + * this buffer contains the current text to be matched and is the source of the yytext() string */ private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; - /** the textposition at the last accepting state */ + /** + * the textposition at the last accepting state + */ private int zzMarkedPos; - /** the current text position in the buffer */ + /** + * the current text position in the buffer + */ private int zzCurrentPos; - /** startRead marks the beginning of the yytext() string in the buffer */ + /** + * startRead marks the beginning of the yytext() string in the buffer + */ private int zzStartRead; /** - * endRead marks the last character in the buffer, that has been read - * from input + * endRead marks the last character in the buffer, that has been read from input */ private int zzEndRead; - /** the number of characters up to the start of the matched text */ + /** + * the number of characters up to the start of the matched text + */ private int yychar; - /** zzAtEOF == true <=> the scanner is at the EOF */ + /** + * zzAtEOF == true <=> the scanner is at the EOF + */ private boolean zzAtEOF; /** @@ -241,8 +259,7 @@ class Yylex { } /** - * Creates a new scanner - * There is also a java.io.InputStream version of this constructor. + * Creates a new scanner There is also a java.io.InputStream version of this constructor. * * @param in the java.io.Reader to read input from. */ @@ -251,11 +268,10 @@ class Yylex { } /** - * Creates a new scanner. - * There is also java.io.Reader version of this constructor. + * Creates a new scanner. There is also java.io.Reader version of this constructor. * * @param in the java.io.Inputstream to read input from. - * @throws UnsupportedEncodingException + * @throws UnsupportedEncodingException */ Yylex(java.io.InputStream in) throws UnsupportedEncodingException { this(new java.io.InputStreamReader(in, Constants.ENCODE)); @@ -269,12 +285,12 @@ class Yylex { */ private static char[] zzUnpackCMap(String packed) { char[] map = new char[0x10000]; - int i = 0; - int j = 0; + int i = 0; + int j = 0; while (i < NIGTY) { int count = packed.charAt(i++); char value = packed.charAt(i++); - do map[j++] = value; while (--count > 0); + do { map[j++] = value; } while (--count > 0); } return map; } @@ -287,26 +303,26 @@ class Yylex { */ private boolean zzRefill() throws java.io.IOException { - /* first: make room (if you can) */ + /* first: make room (if you can) */ if (zzStartRead > 0) { System.arraycopy(zzBuffer, zzStartRead, zzBuffer, 0, zzEndRead - zzStartRead); - /* translate stored positions */ + /* translate stored positions */ zzEndRead -= zzStartRead; zzCurrentPos -= zzStartRead; zzMarkedPos -= zzStartRead; zzStartRead = 0; } - /* is the buffer big enough? */ + /* is the buffer big enough? */ if (zzCurrentPos >= zzBuffer.length) { - /* if not: blow it up */ + /* if not: blow it up */ char newBuffer[] = new char[zzCurrentPos * 2]; System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); zzBuffer = newBuffer; } - /* finally: fill the buffer with new input */ + /* finally: fill the buffer with new input */ int numRead = zzReader.read(zzBuffer, zzEndRead, zzBuffer.length - zzEndRead); if (numRead > 0) { @@ -319,7 +335,7 @@ class Yylex { if (c == -1) { return true; } else { - zzBuffer[zzEndRead++] = (char) c; + zzBuffer[zzEndRead++] = (char)c; return false; } } @@ -333,19 +349,17 @@ class Yylex { */ public final void yyclose() throws java.io.IOException { zzAtEOF = true; - zzEndRead = zzStartRead; + zzEndRead = zzStartRead; if (zzReader != null) { - zzReader.close(); + zzReader.close(); } } /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. - * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. + * Resets the scanner to read from a new input stream. Does not close the old reader. All internal variables are + * reset, the old input stream cannot be reused (internal buffer is discarded and lost). Lexical state is set + * to ZZ_INITIAL. * * @param reader the new input stream */ @@ -381,12 +395,10 @@ class Yylex { } /** - * Returns the character at position pos from the - * matched text. - * It is equivalent to yytext().charAt(pos), but faster + * Returns the character at position pos from the matched text. It is equivalent to yytext().charAt(pos), + * but faster * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. + * @param pos the position of the character to fetch. A value from 0 to yylength()-1. * @return the character at position pos */ public final char yycharat(int pos) { @@ -401,14 +413,10 @@ class Yylex { } /** - * Reports an error that occured while scanning. - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). - * Usual syntax/scanner level error handling should be done - * in error fallback rules. + * Reports an error that occured while scanning. In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method will only be called with things that "Can't Possibly + * Happen". If this method is called, something is seriously wrong (e.g. a JFlex bug producing a faulty scanner + * etc.). Usual syntax/scanner level error handling should be done in error fallback rules. * * @param errorCode the code of the errormessage to display */ @@ -424,23 +432,22 @@ class Yylex { } /** - * Pushes the specified amount of characters back into the input stream. - * They will be read again by then next call of the scanning method + * Pushes the specified amount of characters back into the input stream. They will be read again by then next call + * of the scanning method * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! + * @param number the number of characters to be read again. This number must not be greater than yylength()! */ public void yypushback(int number) { if (number > yylength()) { - zzScanError(ZZ_PUSHBACK_2BIG); + zzScanError(ZZ_PUSHBACK_2BIG); } zzMarkedPos -= number; } /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. + * Resumes scanning until the next regular expression is matched, the end of input is encountered or an I/O-Error + * occurs. * * @return the next token * @throws java.io.IOException if any I/O-Error occurs @@ -476,10 +483,9 @@ class Yylex { { while (true) { - if (zzCurrentPosL < zzEndReadL) { - zzInput = zzBufferL[zzCurrentPosL++]; - } - else if (zzAtEOF) { + if (zzCurrentPosL < zzEndReadL) { + zzInput = zzBufferL[zzCurrentPosL++]; + } else if (zzAtEOF) { zzInput = YYEOF; break zzForAction; } else { @@ -500,18 +506,18 @@ class Yylex { } } int zzNext = zzTransL[zzRowMapL[zzState] + zzCMapL[zzInput]]; - if (zzNext == -1) { - break zzForAction; - } + if (zzNext == -1) { + break zzForAction; + } zzState = zzNext; int zzAttributes = zzAttrL[zzState]; if ((zzAttributes & 1) == 1) { zzAction = zzState; zzMarkedPosL = zzCurrentPosL; - if ((zzAttributes & 8) == 8) { - break zzForAction; - } + if ((zzAttributes & 8) == 8) { + break zzForAction; + } } } @@ -519,7 +525,7 @@ class Yylex { // store back cached position zzMarkedPos = zzMarkedPosL; - + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 11: { sb.append(yytext()); @@ -572,7 +578,8 @@ class Yylex { case 33: break; case 1: { - throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_CHAR, Character.valueOf(yycharat(0))); + throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_CHAR, + Character.valueOf(yycharat(0))); } case 34: break; @@ -614,7 +621,7 @@ class Yylex { case 24: { try { int ch = Integer.parseInt(yytext().substring(2), 16); - sb.append((char) ch); + sb.append((char)ch); } catch (Exception e) { throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_EXCEPTION, e); } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yytoken.java b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yytoken.java index f07146f7e..f2e673b12 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yytoken.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/json/parser/Yytoken.java @@ -20,62 +20,62 @@ package com.alibaba.nacos.client.logger.json.parser; /** - * @author FangYidong + * @author FangYidong */ public class Yytoken { - /** - * JSON primitive value: string,number,boolean,null - */ - public static final int TYPE_VALUE=0; - public static final int TYPE_LEFT_BRACE=1; - public static final int TYPE_RIGHT_BRACE=2; - public static final int TYPE_LEFT_SQUARE=3; - public static final int TYPE_RIGHT_SQUARE=4; - public static final int TYPE_COMMA=5; - public static final int TYPE_COLON=6; - /** - * end of file - */ - public static final int TYPE_EOF=-1; - - public int type=0; - public Object value=null; - - public Yytoken(int type,Object value){ - this.type=type; - this.value=value; - } - - public String toString(){ - StringBuffer sb = new StringBuffer(); - switch(type){ - case TYPE_VALUE: - sb.append("VALUE(").append(value).append(")"); - break; - case TYPE_LEFT_BRACE: - sb.append("LEFT BRACE({)"); - break; - case TYPE_RIGHT_BRACE: - sb.append("RIGHT BRACE(})"); - break; - case TYPE_LEFT_SQUARE: - sb.append("LEFT SQUARE([)"); - break; - case TYPE_RIGHT_SQUARE: - sb.append("RIGHT SQUARE(])"); - break; - case TYPE_COMMA: - sb.append("COMMA(,)"); - break; - case TYPE_COLON: - sb.append("COLON(:)"); - break; - case TYPE_EOF: - sb.append("END OF FILE"); - break; - default: - break; - } - return sb.toString(); - } + /** + * JSON primitive value: string,number,boolean,null + */ + public static final int TYPE_VALUE = 0; + public static final int TYPE_LEFT_BRACE = 1; + public static final int TYPE_RIGHT_BRACE = 2; + public static final int TYPE_LEFT_SQUARE = 3; + public static final int TYPE_RIGHT_SQUARE = 4; + public static final int TYPE_COMMA = 5; + public static final int TYPE_COLON = 6; + /** + * end of file + */ + public static final int TYPE_EOF = -1; + + public int type = 0; + public Object value = null; + + public Yytoken(int type, Object value) { + this.type = type; + this.value = value; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + switch (type) { + case TYPE_VALUE: + sb.append("VALUE(").append(value).append(")"); + break; + case TYPE_LEFT_BRACE: + sb.append("LEFT BRACE({)"); + break; + case TYPE_RIGHT_BRACE: + sb.append("RIGHT BRACE(})"); + break; + case TYPE_LEFT_SQUARE: + sb.append("LEFT SQUARE([)"); + break; + case TYPE_RIGHT_SQUARE: + sb.append("RIGHT SQUARE(])"); + break; + case TYPE_COMMA: + sb.append("COMMA(,)"); + break; + case TYPE_COLON: + sb.append("COLON(:)"); + break; + case TYPE_EOF: + sb.append("END OF FILE"); + break; + default: + break; + } + return sb.toString(); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/log4j/Log4jLogger.java b/client/src/main/java/com/alibaba/nacos/client/logger/log4j/Log4jLogger.java deleted file mode 100644 index 04389b494..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/logger/log4j/Log4jLogger.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.client.logger.log4j; - -import org.apache.log4j.Level; - -import com.alibaba.nacos.client.logger.Logger; -import com.alibaba.nacos.client.logger.option.Log4jActivateOption; -import com.alibaba.nacos.client.logger.support.LoggerHelper; -import com.alibaba.nacos.client.logger.support.LoggerSupport; -import com.alibaba.nacos.client.logger.util.MessageUtil; - - -/** - * Log4jLogger - * @author Nacos - * - */ -public class Log4jLogger extends LoggerSupport implements Logger { - - private org.apache.log4j.Logger delegate; - - public Log4jLogger(org.apache.log4j.Logger delegate) { - super(delegate); - - if (delegate == null) { - throw new IllegalArgumentException("delegate Logger is null"); - } - this.delegate = delegate; - - this.activateOption = new Log4jActivateOption(delegate); - } - - @Override - public void debug(String context, String message) { - if (isDebugEnabled()) { - message = LoggerHelper.getResourceBundleString(getProductName(), message); - delegate.debug(MessageUtil.getMessage(context, message)); - } - } - - @Override - public void debug(String context, String format, Object... args) { - if (isDebugEnabled()) { - format = LoggerHelper.getResourceBundleString(getProductName(), format); - delegate.debug(MessageUtil.getMessage(context, MessageUtil.formatMessage(format, args))); - } - } - - @Override - public void info(String context, String message) { - if (isInfoEnabled()) { - message = LoggerHelper.getResourceBundleString(getProductName(), message); - delegate.info(MessageUtil.getMessage(context, message)); - } - } - - @Override - public void info(String context, String format, Object... args) { - if (isInfoEnabled()) { - format = LoggerHelper.getResourceBundleString(getProductName(), format); - delegate.info(MessageUtil.getMessage(context, MessageUtil.formatMessage(format, args))); - } - } - - @Override - public void warn(String message, Throwable t) { - if (isWarnEnabled()) { - message = LoggerHelper.getResourceBundleString(getProductName(), message); - delegate.warn(MessageUtil.getMessage(null, message), t); - } - } - - @Override - public void warn(String context, String message) { - if (isWarnEnabled()) { - message = LoggerHelper.getResourceBundleString(getProductName(), message); - delegate.warn(MessageUtil.getMessage(context, message)); - } - } - - @Override - public void warn(String context, String format, Object... args) { - if (isWarnEnabled()) { - format = LoggerHelper.getResourceBundleString(getProductName(), format); - delegate.warn(MessageUtil.getMessage(context, MessageUtil.formatMessage(format, args))); - } - } - - @Override - public void error(String context, String errorCode, String message) { - if (isErrorEnabled()) { - message = LoggerHelper.getResourceBundleString(getProductName(), message); - delegate.error(MessageUtil.getMessage(context, errorCode, message)); - } - } - - @Override - public void error(String context, String errorCode, String message, Throwable t) { - if (isErrorEnabled()) { - message = LoggerHelper.getResourceBundleString(getProductName(), message); - delegate.error(MessageUtil.getMessage(context, errorCode, message), t); - } - } - - @Override - public void error(String context, String errorCode, String format, Object... args) { - if (isErrorEnabled()) { - format = LoggerHelper.getResourceBundleString(getProductName(), format); - delegate.error(MessageUtil.getMessage(context, errorCode, MessageUtil.formatMessage(format, args))); - } - } - - @Override - public boolean isDebugEnabled() { - return delegate.isDebugEnabled(); - } - - @Override - public boolean isInfoEnabled() { - return delegate.isInfoEnabled(); - } - - @Override - public boolean isWarnEnabled() { - return delegate.isEnabledFor(Level.WARN); - } - - @Override - public boolean isErrorEnabled() { - return delegate.isEnabledFor(Level.ERROR); - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/log4j/Log4jLoggerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/log4j/Log4jLoggerFactory.java deleted file mode 100644 index 86ca0aa9f..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/logger/log4j/Log4jLoggerFactory.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.client.logger.log4j; - -import org.apache.log4j.LogManager; - -import com.alibaba.nacos.client.logger.Logger; -import com.alibaba.nacos.client.logger.nop.NopLogger; -import com.alibaba.nacos.client.logger.support.ILoggerFactory; -import com.alibaba.nacos.client.logger.support.LogLog; - -/** - * Log4jLogger Factory - * @author Nacos - * - */ -public class Log4jLoggerFactory implements ILoggerFactory { - - public Log4jLoggerFactory() throws ClassNotFoundException { - Class.forName("org.apache.log4j.Level"); - } - - public Logger getLogger(Class clazz) { - try { - return new Log4jLogger(LogManager.getLogger(clazz)); - } catch (Throwable t) { - LogLog.error("Failed to get Log4jLogger", t); - return new NopLogger(); - } - } - - public Logger getLogger(String name) { - try { - return new Log4jLogger(LogManager.getLogger(name)); - } catch (Throwable t) { - LogLog.error("Failed to get Log4jLogger", t); - return new NopLogger(); - } - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2Logger.java b/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2Logger.java index 2333d06fc..2c301f791 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2Logger.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2Logger.java @@ -21,11 +21,10 @@ import com.alibaba.nacos.client.logger.support.LoggerHelper; import com.alibaba.nacos.client.logger.support.LoggerSupport; import com.alibaba.nacos.client.logger.util.MessageUtil; - /** * Log4j2Logger - * @author Nacos * + * @author Nacos */ public class Log4j2Logger extends LoggerSupport implements Logger { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2LoggerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2LoggerFactory.java index 11863cc3a..ee4703eb9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2LoggerFactory.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/log4j2/Log4j2LoggerFactory.java @@ -24,8 +24,8 @@ import com.alibaba.nacos.client.logger.support.LogLog; /** * Log4j2Logger Factory - * @author Nacos * + * @author Nacos */ public class Log4j2LoggerFactory implements ILoggerFactory { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLogger.java b/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLogger.java index 48d12f694..9463d58c5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLogger.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLogger.java @@ -17,14 +17,15 @@ package com.alibaba.nacos.client.logger.nop; import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.support.LoggerSupport; + /** * NopLogger - * @author Nacos * + * @author Nacos */ public class NopLogger extends LoggerSupport implements Logger { - public NopLogger(){ + public NopLogger() { super(null); } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLoggerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLoggerFactory.java index 9bd6a9979..7072eea2d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLoggerFactory.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/nop/NopLoggerFactory.java @@ -17,10 +17,11 @@ package com.alibaba.nacos.client.logger.nop; import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.support.ILoggerFactory; + /** * NopLogger Factory - * @author Nacos * + * @author Nacos */ public class NopLoggerFactory implements ILoggerFactory { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/AbstractActiveOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/AbstractActiveOption.java index 209054e68..6e8673164 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/AbstractActiveOption.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/AbstractActiveOption.java @@ -15,8 +15,6 @@ */ package com.alibaba.nacos.client.logger.option; -import org.apache.log4j.AsyncAppender; - import com.alibaba.nacos.client.logger.Level; import com.alibaba.nacos.client.logger.support.LogLog; @@ -26,8 +24,8 @@ import java.util.List; /** * AbstractActiveOption - * @author Nacos * + * @author Nacos */ public abstract class AbstractActiveOption implements ActivateOption { @@ -55,7 +53,7 @@ public abstract class AbstractActiveOption implements ActivateOption { for (Object[] arg : args) { if (arg != null && arg.length == 3) { try { - Method m = object.getClass().getMethod((String) arg[0], (Class[]) arg[1]); + Method m = object.getClass().getMethod((String)arg[0], (Class[])arg[1]); m.invoke(object, arg[2]); } catch (NoSuchMethodException e) { LogLog.info("Can't find method for " + object.getClass() + " " + arg[0] + " " + arg[2]); diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/ActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/ActivateOption.java index 6c8826829..0ef26de82 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/ActivateOption.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/ActivateOption.java @@ -140,8 +140,7 @@ public interface ActivateOption { int maxBackupIndex); /** - * 将当前logger对象的appender设置为异步Appender - * 注意:此logger需要提前进行Appender的初始化 + * 将当前logger对象的appender设置为异步Appender 注意:此logger需要提前进行Appender的初始化 * * @param queueSize 等待队列大小 * @param discardingThreshold discardingThreshold,该参数仅对logback实现有效,log4j和log4j2无效 @@ -150,11 +149,10 @@ public interface ActivateOption { void activateAsync(int queueSize, int discardingThreshold); /** - * 将当前logger对象的appender设置为异步Appender - * 注意:此logger需要提前进行Appender的初始化 + * 将当前logger对象的appender设置为异步Appender 注意:此logger需要提前进行Appender的初始化 * - * @param args AsyncAppender配置参数,请自行保证参数的正确性,要求每个Object[]有3个元素,第一个为set方法名,第二个为方法类型数组,第三个为对应的参数值,如 - * args.add(new Object[] { "setBufferSize", new Class[] { int.class }, queueSize }); + * @param args AsyncAppender配置参数,请自行保证参数的正确性,要求每个Object[]有3个元素,第一个为set方法名,第二个为方法类型数组,第三个为对应的参数值,如 args.add(new + * Object[] { "setBufferSize", new Class[] { int.class }, queueSize }); * @since 0.2.3 */ void activateAsync(List args); @@ -176,6 +174,7 @@ public interface ActivateOption { /** * 获取日志级别 + * * @return level */ Level getLevel(); @@ -189,6 +188,7 @@ public interface ActivateOption { /** * 获取所属的产品名 + * * @return 所属的产品名 */ String getProductName(); diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4j2ActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4j2ActivateOption.java index 0dbccc188..e0ec6ac07 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4j2ActivateOption.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4j2ActivateOption.java @@ -47,11 +47,12 @@ public class Log4j2ActivateOption extends AbstractActiveOption { public Log4j2ActivateOption(org.apache.logging.log4j.Logger logger) { if (logger != null) { if (logger instanceof org.apache.logging.log4j.core.Logger) { - this.logger = (org.apache.logging.log4j.core.Logger) logger; + this.logger = (org.apache.logging.log4j.core.Logger)logger; configuration = this.logger.getContext().getConfiguration(); } else { - throw new RuntimeException("logger must instanceof org.apache.logging.log4j.core.Logger, " + logger.getClass().getName()); + throw new RuntimeException( + "logger must instanceof org.apache.logging.log4j.core.Logger, " + logger.getClass().getName()); } } } @@ -59,12 +60,13 @@ public class Log4j2ActivateOption extends AbstractActiveOption { @Override public void activateConsoleAppender(String target, String encoding) { org.apache.logging.log4j.core.Layout layout = org.apache.logging.log4j.core.layout.PatternLayout.newBuilder(). - withConfiguration(configuration) - .withPattern(LoggerHelper.getPattern()) - .withCharset(Charset.forName(encoding)) - .build(); + withConfiguration(configuration) + .withPattern(LoggerHelper.getPattern()) + .withCharset(Charset.forName(encoding)) + .build(); org.apache.logging.log4j.core.appender.ConsoleAppender appender = ConsoleAppender.createAppender(layout, null, - ConsoleAppender.Target.valueOf(target.toUpperCase().replace(".", "_")), "LoggerApiConsoleAppender", false, false, true); + ConsoleAppender.Target.valueOf(target.toUpperCase().replace(".", "_")), "LoggerApiConsoleAppender", false, + false, true); appender.start(); removeAllAppenders(logger); logger.addAppender(appender); @@ -75,17 +77,17 @@ public class Log4j2ActivateOption extends AbstractActiveOption { @Override public void activateAppender(String productName, String file, String encoding) { org.apache.logging.log4j.core.appender.RollingFileAppender appender = RollingFileAppender.newBuilder() - .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") - .withFileName(LoggerHelper.getLogFileP(productName, file)) - .withAppend(true) - .withBufferedIo(true) - .setConfiguration(configuration) - .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%d{yyyy-MM-dd}") - .withLayout(buildLayout(encoding)) - .withCreateOnDemand(false) - .withPolicy(TimeBasedTriggeringPolicy.createPolicy("1", "true")) - .withStrategy(DefaultRolloverStrategy.createStrategy(null, null, "nomax", null, null, false, configuration)) - .build(); + .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") + .withFileName(LoggerHelper.getLogFileP(productName, file)) + .withAppend(true) + .withBufferedIo(true) + .setConfiguration(configuration) + .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%d{yyyy-MM-dd}") + .withLayout(buildLayout(encoding)) + .withCreateOnDemand(false) + .withPolicy(TimeBasedTriggeringPolicy.createPolicy("1", "true")) + .withStrategy(DefaultRolloverStrategy.createStrategy(null, null, "nomax", null, null, false, configuration)) + .build(); appender.start(); removeAllAppenders(logger); @@ -100,7 +102,8 @@ public class Log4j2ActivateOption extends AbstractActiveOption { } @Override - public void activateAsyncAppender(String productName, String file, String encoding, int queueSize, int discardingThreshold) { + public void activateAsyncAppender(String productName, String file, String encoding, int queueSize, + int discardingThreshold) { activateAppender(productName, file, encoding); activateAsync(queueSize, discardingThreshold); } @@ -111,19 +114,21 @@ public class Log4j2ActivateOption extends AbstractActiveOption { } @Override - public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, String datePattern) { + public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, + String datePattern) { org.apache.logging.log4j.core.appender.RollingFileAppender appender = RollingFileAppender.newBuilder() - .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") - .withFileName(LoggerHelper.getLogFileP(productName, file)) - .withAppend(true) - .withBufferedIo(true) - .setConfiguration(configuration) - .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%d{" + datePattern + "}") - .withLayout(buildLayout(encoding)) - .withCreateOnDemand(false) - .withPolicy(CompositeTriggeringPolicy.createPolicy(TimeBasedTriggeringPolicy.createPolicy("1", "true"), SizeBasedTriggeringPolicy.createPolicy(size))) - .withStrategy(DefaultRolloverStrategy.createStrategy(null, null, "nomax", null, null, false, configuration)) - .build(); + .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") + .withFileName(LoggerHelper.getLogFileP(productName, file)) + .withAppend(true) + .withBufferedIo(true) + .setConfiguration(configuration) + .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%d{" + datePattern + "}") + .withLayout(buildLayout(encoding)) + .withCreateOnDemand(false) + .withPolicy(CompositeTriggeringPolicy.createPolicy(TimeBasedTriggeringPolicy.createPolicy("1", "true"), + SizeBasedTriggeringPolicy.createPolicy(size))) + .withStrategy(DefaultRolloverStrategy.createStrategy(null, null, "nomax", null, null, false, configuration)) + .build(); appender.start(); removeAllAppenders(logger); @@ -133,19 +138,22 @@ public class Log4j2ActivateOption extends AbstractActiveOption { } @Override - public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, String datePattern, int maxBackupIndex) { + public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, + String datePattern, int maxBackupIndex) { org.apache.logging.log4j.core.appender.RollingFileAppender appender = RollingFileAppender.newBuilder() - .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") - .withFileName(LoggerHelper.getLogFileP(productName, file)) - .withAppend(true) - .withBufferedIo(true) - .setConfiguration(configuration) - .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%d{" + datePattern + "}.%i") - .withLayout(buildLayout(encoding)) - .withCreateOnDemand(false) - .withPolicy(CompositeTriggeringPolicy.createPolicy(TimeBasedTriggeringPolicy.createPolicy("1", "true"), SizeBasedTriggeringPolicy.createPolicy(size))) - .withStrategy(DefaultRolloverStrategy.createStrategy(String.valueOf(maxBackupIndex), "1", "max", null, null, false, configuration)) - .build(); + .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") + .withFileName(LoggerHelper.getLogFileP(productName, file)) + .withAppend(true) + .withBufferedIo(true) + .setConfiguration(configuration) + .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%d{" + datePattern + "}.%i") + .withLayout(buildLayout(encoding)) + .withCreateOnDemand(false) + .withPolicy(CompositeTriggeringPolicy.createPolicy(TimeBasedTriggeringPolicy.createPolicy("1", "true"), + SizeBasedTriggeringPolicy.createPolicy(size))) + .withStrategy(DefaultRolloverStrategy + .createStrategy(String.valueOf(maxBackupIndex), "1", "max", null, null, false, configuration)) + .build(); appender.start(); removeAllAppenders(logger); @@ -155,19 +163,21 @@ public class Log4j2ActivateOption extends AbstractActiveOption { } @Override - public void activateAppenderWithSizeRolling(String productName, String file, String encoding, String size, int maxBackupIndex) { + public void activateAppenderWithSizeRolling(String productName, String file, String encoding, String size, + int maxBackupIndex) { org.apache.logging.log4j.core.appender.RollingFileAppender appender = RollingFileAppender.newBuilder() - .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") - .withFileName(LoggerHelper.getLogFileP(productName, file)) - .withAppend(true) - .withBufferedIo(true) - .setConfiguration(configuration) - .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%i") - .withLayout(buildLayout(encoding)) - .withCreateOnDemand(false) - .withPolicy(SizeBasedTriggeringPolicy.createPolicy(size)) - .withStrategy(DefaultRolloverStrategy.createStrategy(String.valueOf(maxBackupIndex), "1", "max", null, null, false, configuration)) - .build(); + .withName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender") + .withFileName(LoggerHelper.getLogFileP(productName, file)) + .withAppend(true) + .withBufferedIo(true) + .setConfiguration(configuration) + .withFilePattern(LoggerHelper.getLogFile(productName, file) + ".%i") + .withLayout(buildLayout(encoding)) + .withCreateOnDemand(false) + .withPolicy(SizeBasedTriggeringPolicy.createPolicy(size)) + .withStrategy(DefaultRolloverStrategy + .createStrategy(String.valueOf(maxBackupIndex), "1", "max", null, null, false, configuration)) + .build(); appender.start(); removeAllAppenders(logger); @@ -181,7 +191,7 @@ public class Log4j2ActivateOption extends AbstractActiveOption { List args = new ArrayList(); if (queueSize != Integer.MIN_VALUE) { - args.add(new Object[] { "setBufferSize", new Class[] { int.class }, queueSize }); + args.add(new Object[] {"setBufferSize", new Class[] {int.class}, queueSize}); } activateAsync(args); } @@ -201,10 +211,10 @@ public class Log4j2ActivateOption extends AbstractActiveOption { } AsyncAppender.Builder builder = AsyncAppender.newBuilder() - .setName(productName + "." + logger.getName() + ".AsyncAppender") - .setConfiguration(configuration) - .setAppenderRefs(refs) - .setBlockingQueueFactory(ArrayBlockingQueueFactory.createFactory()); + .setName(productName + "." + logger.getName() + ".AsyncAppender") + .setConfiguration(configuration) + .setAppenderRefs(refs) + .setBlockingQueueFactory(ArrayBlockingQueueFactory.createFactory()); invokeMethod(builder, args); @@ -221,10 +231,10 @@ public class Log4j2ActivateOption extends AbstractActiveOption { public void activateAppender(Logger logger) { if (!(logger.getDelegate() instanceof org.apache.logging.log4j.core.Logger)) { throw new IllegalArgumentException("logger must be org.apache.logging.log4j.core.Logger, but it's " - + logger.getDelegate().getClass()); + + logger.getDelegate().getClass()); } - activateAppender(((org.apache.logging.log4j.core.Logger) logger.getDelegate())); + activateAppender(((org.apache.logging.log4j.core.Logger)logger.getDelegate())); setProductName(logger.getProductName()); } @@ -244,7 +254,8 @@ public class Log4j2ActivateOption extends AbstractActiveOption { public void setLevel(Level level) { this.level = level; - org.apache.logging.log4j.Level l = org.apache.logging.log4j.Level.toLevel(level.getName(), org.apache.logging.log4j.Level.ERROR); + org.apache.logging.log4j.Level l = org.apache.logging.log4j.Level.toLevel(level.getName(), + org.apache.logging.log4j.Level.ERROR); logger.setLevel(l); logger.getContext().getConfiguration().getLoggerConfig(this.logger.getName()).setLevel(l); } @@ -256,10 +267,10 @@ public class Log4j2ActivateOption extends AbstractActiveOption { protected org.apache.logging.log4j.core.Layout buildLayout(String encoding) { org.apache.logging.log4j.core.Layout layout = org.apache.logging.log4j.core.layout.PatternLayout.newBuilder(). - withConfiguration(configuration) - .withPattern(LoggerHelper.getPattern()) - .withCharset(Charset.forName(encoding)) - .build(); + withConfiguration(configuration) + .withPattern(LoggerHelper.getPattern()) + .withCharset(Charset.forName(encoding)) + .build(); return layout; } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4jActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4jActivateOption.java deleted file mode 100644 index ca3e3a6f9..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/Log4jActivateOption.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.client.logger.option; - -import org.apache.log4j.*; - -import com.alibaba.nacos.client.logger.Logger; -import com.alibaba.nacos.client.logger.support.LoggerHelper; - -import java.io.File; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * ActivateOption的Log4j实现 - * - * @author zhuyong 2014年3月20日 上午10:24:36 - */ -public class Log4jActivateOption extends AbstractActiveOption { - - protected org.apache.log4j.Logger logger; - - public Log4jActivateOption(org.apache.log4j.Logger logger) { - this.logger = logger; - } - - @Override - public void activateConsoleAppender(String target, String encoding) { - org.apache.log4j.ConsoleAppender appender = new org.apache.log4j.ConsoleAppender(); - appender.setLayout(new PatternLayout(LoggerHelper.getPattern())); - appender.setTarget(target); - appender.setEncoding(encoding); - appender.activateOptions(); - - logger.removeAllAppenders(); - logger.addAppender(appender); - } - - @Override - public void activateAppender(String productName, String file, String encoding) { - org.apache.log4j.Appender appender = getLog4jDailyRollingFileAppender(productName, file, encoding); - logger.removeAllAppenders(); - logger.addAppender(appender); - - setProductName(productName); - } - - @Override - public void activateAsyncAppender(String productName, String file, String encoding) { - activateAsyncAppender(productName, file, encoding, Integer.MIN_VALUE, Integer.MIN_VALUE); - } - - @Override - public void activateAsyncAppender(String productName, String file, String encoding, int queueSize, - int discardingThreshold) { - activateAppender(productName, file, encoding); - activateAsync(queueSize, discardingThreshold); - } - - @Override - public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size) { - activateAppender(productName, file, encoding); - } - - @Override - public void setLevel(com.alibaba.nacos.client.logger.Level level) { - this.level = level; - logger.setLevel(org.apache.log4j.Level.toLevel(level.getName())); - } - - @Override - public void setAdditivity(boolean additivity) { - logger.setAdditivity(additivity); - } - - protected org.apache.log4j.Appender getLog4jDailyRollingFileAppender(String productName, String file, - String encoding) { - DailyRollingFileAppender appender = new DailyRollingFileAppender(); - appender.setName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender"); - appender.setLayout(new PatternLayout(LoggerHelper.getPattern(productName))); - appender.setAppend(true); - appender.setFile(LoggerHelper.getLogFileP(productName, file)); - appender.setEncoding(encoding); - appender.activateOptions(); - - return appender; - } - - @Override - public void activateAppender(Logger logger) { - if (!(logger.getDelegate() instanceof org.apache.log4j.Logger)) { - throw new IllegalArgumentException( - "logger must be org.apache.log4j.Logger, but it's " + logger.getDelegate().getClass()); - } - activateAppender((org.apache.log4j.Logger) logger.getDelegate()); - - setProductName(logger.getProductName()); - } - - protected void activateAppender(org.apache.log4j.Logger logger) { - this.logger.removeAllAppenders(); - - Enumeration enums = logger.getAllAppenders(); - while (enums != null && enums.hasMoreElements()) { - this.logger.addAppender((Appender) enums.nextElement()); - } - } - - @Override - public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, - String datePattern) { - Appender appender = getLog4jRollingFileAppender(productName, file, encoding, size, datePattern, -1); - - logger.removeAllAppenders(); - logger.addAppender(appender); - - setProductName(productName); - } - - @Override - public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, - String datePattern, int maxBackupIndex) { - Appender appender = getLog4jRollingFileAppender(productName, file, encoding, size, datePattern, maxBackupIndex); - logger.removeAllAppenders(); - logger.addAppender(appender); - - setProductName(productName); - } - - protected org.apache.log4j.Appender getLog4jRollingFileAppender(String productName, String file, String encoding, - String size, String datePattern, - int maxBackupIndex) { - RollingFileAppender appender = new RollingFileAppender(); - appender.setName(productName + "." + file.replace(File.separatorChar, '.') + ".Appender"); - appender.setLayout(new PatternLayout(LoggerHelper.getPattern(productName))); - appender.setAppend(true); - appender.setFile(LoggerHelper.getLogFileP(productName, file)); - appender.setEncoding(encoding); - appender.setMaxFileSize(size); - if (maxBackupIndex >= 0) { - // 等于0表示直接truck - appender.setMaxBackupIndex(maxBackupIndex); - } - appender.activateOptions(); - - return appender; - } - - @Override - public void activateAppenderWithSizeRolling(String productName, String file, String encoding, String size, - int maxBackupIndex) { - Appender appender = getLog4jRollingFileAppender(productName, file, encoding, size, null, maxBackupIndex); - logger.removeAllAppenders(); - logger.addAppender(appender); - - setProductName(productName); - } - - @Override - public void activateAsync(int queueSize, int discardingThreshold) { - // discardingThreshold is unused for log4j - List args = new ArrayList(); - - if (queueSize != Integer.MIN_VALUE) { - args.add(new Object[] { "setBufferSize", new Class[] { int.class }, queueSize }); - } - activateAsync(args); - } - - @Override - public void activateAsync(List args) { - AsyncAppender asyncAppender = new AsyncAppender(); - - invokeMethod(asyncAppender, args); - - asyncAppender.setName(productName + "." + logger.getName() + ".AsyncAppender"); - Enumeration appenders = logger.getAllAppenders(); - - if (appenders == null) { - throw new IllegalStateException("Activate async appender failed, no appender exist."); - } - - while (appenders.hasMoreElements()) { - asyncAppender.addAppender(appenders.nextElement()); - } - - appenders = logger.getAllAppenders(); - while (appenders.hasMoreElements()) { - logger.removeAppender(appenders.nextElement()); - } - - logger.addAppender(asyncAppender); - - setProductName(productName); - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/Logback918ActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/Logback918ActivateOption.java index 319f31a59..93dbf35dc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/Logback918ActivateOption.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/Logback918ActivateOption.java @@ -39,14 +39,14 @@ import com.alibaba.nacos.client.logger.support.LoggerHelper; * * @author zhuyong 2014年3月20日 上午11:16:26 */ -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) public class Logback918ActivateOption extends AbstractActiveOption { private ch.qos.logback.classic.Logger logger; public Logback918ActivateOption(Object logger) { if (logger instanceof ch.qos.logback.classic.Logger) { - this.logger = (ch.qos.logback.classic.Logger) logger; + this.logger = (ch.qos.logback.classic.Logger)logger; } else { throw new IllegalArgumentException("logger must be instanceof ch.qos.logback.classic.Logger"); } @@ -102,7 +102,7 @@ public class Logback918ActivateOption extends AbstractActiveOption { @Override public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size) { ch.qos.logback.core.Appender appender = getLogbackDailyAndSizeRollingFileAppender(productName, file, encoding, - size); + size); logger.detachAndStopAllAppenders(); logger.addAppender(appender); @@ -146,11 +146,12 @@ public class Logback918ActivateOption extends AbstractActiveOption { public void activateAppender(Logger logger) { if (!(logger.getDelegate() instanceof ch.qos.logback.classic.Logger)) { throw new IllegalArgumentException( - "logger must be ch.qos.logback.classic.Logger, but it's " + logger.getDelegate().getClass()); + "logger must be ch.qos.logback.classic.Logger, but it's " + logger.getDelegate().getClass()); } this.logger.detachAndStopAllAppenders(); - Iterator> iter = ((ch.qos.logback.classic.Logger) logger.getDelegate()).iteratorForAppenders(); + Iterator> iter = ((ch.qos.logback.classic.Logger)logger + .getDelegate()).iteratorForAppenders(); while (iter.hasNext()) { ch.qos.logback.core.Appender appender = iter.next(); this.logger.addAppender(appender); @@ -161,7 +162,7 @@ public class Logback918ActivateOption extends AbstractActiveOption { public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, String datePattern) { ch.qos.logback.core.Appender appender = getLogbackDailyAndSizeRollingFileAppender(productName, file, encoding, - size, datePattern, -1); + size, datePattern, -1); logger.detachAndStopAllAppenders(); logger.addAppender(appender); @@ -211,8 +212,8 @@ public class Logback918ActivateOption extends AbstractActiveOption { public void activateAppenderWithTimeAndSizeRolling(String productName, String file, String encoding, String size, String datePattern, int maxBackupIndex) { ch.qos.logback.core.Appender appender = getLogbackDailyAndSizeRollingFileAppender(productName, file, encoding, - size, datePattern, - maxBackupIndex); + size, datePattern, + maxBackupIndex); logger.detachAndStopAllAppenders(); logger.addAppender(appender); @@ -223,7 +224,7 @@ public class Logback918ActivateOption extends AbstractActiveOption { public void activateAppenderWithSizeRolling(String productName, String file, String encoding, String size, int maxBackupIndex) { ch.qos.logback.core.Appender appender = getSizeRollingAppender(productName, file, encoding, size, - maxBackupIndex); + maxBackupIndex); logger.detachAndStopAllAppenders(); logger.addAppender(appender); @@ -235,11 +236,11 @@ public class Logback918ActivateOption extends AbstractActiveOption { List args = new ArrayList(); if (queueSize != Integer.MIN_VALUE) { - args.add(new Object[] { "setQueueSize", new Class[] { int.class }, queueSize }); + args.add(new Object[] {"setQueueSize", new Class[] {int.class}, queueSize}); } if (discardingThreshold != Integer.MIN_VALUE) { - args.add(new Object[] { "setDiscardingThreshold", new Class[] { int.class }, discardingThreshold }); + args.add(new Object[] {"setDiscardingThreshold", new Class[] {int.class}, discardingThreshold}); } activateAsync(args); diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackActivateOption.java index b217e9b6c..55f199435 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackActivateOption.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackActivateOption.java @@ -29,10 +29,10 @@ import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; /** * ActivateOption的Logback 0.9.19及后续版本的实现 - * + * * @author zhuyong 2014年3月20日 上午10:24:58 */ -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) public class LogbackActivateOption extends Logback918ActivateOption { public LogbackActivateOption(Object logger) { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackLoggerContextUtil.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackLoggerContextUtil.java index 2c947ed62..626cc211e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackLoggerContextUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/LogbackLoggerContextUtil.java @@ -20,10 +20,11 @@ import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.core.LogbackException; + /** * Logback Context Util - * @author Nacos * + * @author Nacos */ public class LogbackLoggerContextUtil { @@ -35,11 +36,11 @@ public class LogbackLoggerContextUtil { if (!(lcObject instanceof LoggerContext)) { throw new LogbackException( - "Expected LOGBACK binding with SLF4J, but another log system has taken the place: " - + lcObject.getClass().getSimpleName()); + "Expected LOGBACK binding with SLF4J, but another log system has taken the place: " + + lcObject.getClass().getSimpleName()); } - loggerContext = (LoggerContext) lcObject; + loggerContext = (LoggerContext)lcObject; } return loggerContext; diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4j2AdapterActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4j2AdapterActivateOption.java index 397d7d348..05bd5eb93 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4j2AdapterActivateOption.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4j2AdapterActivateOption.java @@ -41,7 +41,8 @@ public class Slf4jLog4j2AdapterActivateOption extends Log4j2ActivateOption { super(null); try { - org.apache.logging.log4j.core.Logger log4j2Logger = (org.apache.logging.log4j.core.Logger) loggerField.get(logger); + org.apache.logging.log4j.core.Logger log4j2Logger = (org.apache.logging.log4j.core.Logger)loggerField.get( + logger); super.logger = log4j2Logger; super.configuration = super.logger.getContext().getConfiguration(); } catch (Exception e) { @@ -54,12 +55,13 @@ public class Slf4jLog4j2AdapterActivateOption extends Log4j2ActivateOption { public void activateAppender(Logger logger) { if (!(logger.getDelegate() instanceof org.apache.logging.slf4j.Log4jLogger)) { throw new IllegalArgumentException( - "logger must be org.apache.logging.slf4j.Log4jLogger, but it's " - + logger.getDelegate().getClass()); + "logger must be org.apache.logging.slf4j.Log4jLogger, but it's " + + logger.getDelegate().getClass()); } try { - org.apache.logging.log4j.core.Logger log4j2Logger = (org.apache.logging.log4j.core.Logger) loggerField.get(logger.getDelegate()); + org.apache.logging.log4j.core.Logger log4j2Logger = (org.apache.logging.log4j.core.Logger)loggerField.get( + logger.getDelegate()); super.activateAppender(log4j2Logger); } catch (Exception e) { throw new RuntimeException("activateAppender error, ", e); diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4jAdapterActivateOption.java b/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4jAdapterActivateOption.java deleted file mode 100644 index 71c0a1956..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/logger/option/Slf4jLog4jAdapterActivateOption.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.client.logger.option; - -import java.lang.reflect.Field; - -import com.alibaba.nacos.client.logger.Logger; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -/** - * Slf4j-log4j12架构下的ActivateOption实现 - * - * @author zhuyong 2014年3月20日 上午10:26:04 - */ -public class Slf4jLog4jAdapterActivateOption extends Log4jActivateOption { - - private static Field loggerField = null; - - static { - try { - loggerField = org.slf4j.impl.Log4jLoggerAdapter.class.getDeclaredField("logger"); - loggerField.setAccessible(true); - } catch (Exception e) { - throw new RuntimeException("logger must be instanceof org.slf4j.impl.Log4jLoggerAdapter", e); - } - } - - public Slf4jLog4jAdapterActivateOption(Object logger) { - super(null); - - try { - org.apache.log4j.Logger log4jLogger = (org.apache.log4j.Logger) loggerField.get(logger); - super.logger = log4jLogger; - } catch (Exception e) { - throw new RuntimeException("logger must be instanceof org.slf4j.impl.Log4jLoggerAdapter", e); - } - } - - @Override - @SuppressFBWarnings("NM_WRONG_PACKAGE") - public void activateAppender(Logger logger) { - if (!(logger.getDelegate() instanceof org.slf4j.impl.Log4jLoggerAdapter)) { - throw new IllegalArgumentException( - "logger must be org.slf4j.impl.Log4jLoggerAdapter, but it's " - + logger.getDelegate().getClass()); - } - - try { - org.apache.log4j.Logger log4jLogger = - (org.apache.log4j.Logger) loggerField.get(logger.getDelegate()); - super.activateAppender(log4jLogger); - setProductName(logger.getProductName()); - } catch (Exception e) { - throw new RuntimeException("activateAppender error, ", e); - } - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLogger.java b/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLogger.java index 9d5fd03c2..e70453143 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLogger.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLogger.java @@ -15,24 +15,25 @@ */ package com.alibaba.nacos.client.logger.slf4j; -import java.lang.reflect.Constructor; - import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.option.ActivateOption; import com.alibaba.nacos.client.logger.support.LoggerHelper; import com.alibaba.nacos.client.logger.support.LoggerSupport; import com.alibaba.nacos.client.logger.util.MessageUtil; + +import java.lang.reflect.Constructor; + /** * slf4j logger - * @author Nacos * + * @author Nacos */ public class Slf4jLogger extends LoggerSupport implements Logger { - private static boolean CanUseEncoder = false; + private static boolean CanUseEncoder = false; private static final String LOGBACK_CLASSNAME = "ch.qos.logback.classic.Logger"; - private static final String SLF4J_CLASSNAME = "org.slf4j.impl.Log4jLoggerAdapter"; private static final String SLF4JLOG4J_CLASSNAME = "org.apache.logging.slf4j.Log4jLogger"; + static { try { // logback从0.9.19开始采用encoder,@see http://logback.qos.ch/manual/encoders.html @@ -46,8 +47,7 @@ public class Slf4jLogger extends LoggerSupport implements Logger { private org.slf4j.Logger delegate; @SuppressWarnings("unchecked") - public - Slf4jLogger(org.slf4j.Logger delegate){ + public Slf4jLogger(org.slf4j.Logger delegate) { super(delegate); if (delegate == null) { throw new IllegalArgumentException("delegate Logger is null"); @@ -61,14 +61,16 @@ public class Slf4jLogger extends LoggerSupport implements Logger { } else { activateOptionClass = "com.alibaba.nacos.client.logger.option.Logback918ActivateOption"; } - } else if (SLF4J_CLASSNAME.equals(delegate.getClass().getName())) { - activateOptionClass = "com.alibaba.nacos.client.logger.option.Slf4jLog4jAdapterActivateOption"; } else if (SLF4JLOG4J_CLASSNAME.equals(delegate.getClass().getName())) { activateOptionClass = "com.alibaba.nacos.client.logger.option.Slf4jLog4j2AdapterActivateOption"; } + if (activateOptionClass == null) { + throw new IllegalArgumentException("delegate must be logback impl or slf4j-log4j impl"); + } + try { - Class clazz = (Class) Class.forName(activateOptionClass); + Class clazz = (Class)Class.forName(activateOptionClass); Constructor c = clazz.getConstructor(Object.class); this.activateOption = c.newInstance(delegate); } catch (Exception e) { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLoggerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLoggerFactory.java index 95484e1df..e471d7fbe 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLoggerFactory.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/slf4j/Slf4jLoggerFactory.java @@ -15,15 +15,15 @@ */ package com.alibaba.nacos.client.logger.slf4j; - import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.nop.NopLogger; import com.alibaba.nacos.client.logger.support.ILoggerFactory; import com.alibaba.nacos.client.logger.support.LogLog; + /** * Slf4jLogger Factory - * @author Nacos * + * @author Nacos */ public class Slf4jLoggerFactory implements ILoggerFactory { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/AppenderInfo.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/AppenderInfo.java index 074853eaa..d3faf336e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/AppenderInfo.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/AppenderInfo.java @@ -27,7 +27,7 @@ public class AppenderInfo extends HashMap { private static String file = "file"; public String getName() { - return (String) get(AppenderInfo.name); + return (String)get(AppenderInfo.name); } public void setName(String name) { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/DepthThrowableRenderer.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/DepthThrowableRenderer.java deleted file mode 100644 index 59e4299a4..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/DepthThrowableRenderer.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 2014 Alibaba.com All right reserved. This software is the - * confidential and proprietary information of Alibaba.com ("Confidential - * Information"). You shall not disclose such Confidential Information and shall - * use it only in accordance with the terms of the license agreement you entered - * into with Alibaba.com. - */ -package com.alibaba.nacos.client.logger.support; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.LineNumberReader; -import java.io.PrintWriter; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; - -import org.apache.log4j.spi.ThrowableRenderer; - -/** - * 针对 Log4j 1.2.16 及以上版本,提供对异常栈的深度控制 - * - * @author zhuyong 2014年9月19日 上午10:31:48 - */ -public final class DepthThrowableRenderer implements ThrowableRenderer { - - private int depth = -1; - - public DepthThrowableRenderer(int depth) { - this.depth = depth; - } - - public void setDepth(int depth) { - this.depth = depth; - } - - public String[] doRender(final Throwable throwable) { - return render(throwable, depth); - } - - /** - * Render throwable using Throwable.printStackTrace. - * - * @param throwable throwable, may not be null. - * @param depth stack depth - * @return string representation. - */ - public static String[] render(final Throwable throwable, final int depth) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - try { - throwable.printStackTrace(pw); - } catch (RuntimeException ex) { - } - pw.flush(); - LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString())); - ArrayList lines = new ArrayList(); - try { - String line = reader.readLine(); - int count = 0; - while (line != null && (depth == -1 || count++ <= depth)) { - lines.add(line); - line = reader.readLine(); - } - } catch (IOException ex) { - if (ex instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - lines.add(ex.toString()); - } - String[] tempRep = new String[lines.size()]; - lines.toArray(tempRep); - return tempRep; - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/ErrorLog.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/ErrorLog.java index 9a6ec14f7..1691e2f98 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/ErrorLog.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/ErrorLog.java @@ -24,7 +24,7 @@ package com.alibaba.nacos.client.logger.support; /** * 兼容老的ErrorLog,后续请使用{@link LoggerHelper} - * + * * @author zhuyong 2014年7月1日 上午11:41:22 */ public class ErrorLog { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/ILoggerFactory.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/ILoggerFactory.java index 1c7663e75..3bd7e1dae 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/ILoggerFactory.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/ILoggerFactory.java @@ -19,26 +19,23 @@ import com.alibaba.nacos.client.logger.Logger; /** * logger factory interface - * - * @author Nacos * + * @author Nacos */ public interface ILoggerFactory { - /** - * get logger - * - * @param clazz - * class - * @return logger - */ - Logger getLogger(Class clazz); + /** + * get logger + * + * @param clazz class + * @return logger + */ + Logger getLogger(Class clazz); - /** - * get logger - * - * @param name - * logger name - * @return logger - */ - Logger getLogger(String name); + /** + * get logger + * + * @param name logger name + * @return logger + */ + Logger getLogger(String name); } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/Log4jHelper.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/Log4jHelper.java deleted file mode 100644 index 2597a9ba6..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/Log4jHelper.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.client.logger.support; - -import org.apache.log4j.Appender; -import org.apache.log4j.AsyncAppender; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.FileAppender; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.ThrowableRenderer; -import org.apache.log4j.spi.ThrowableRendererSupport; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author zhuyong on 2017/6/28. - */ -@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") -public class Log4jHelper { - - private static boolean Log4j = false, Log4jGT1216 = false; - - static { - try { - Class loggerClass = Class.forName("org.apache.log4j.Logger"); - // 这里可能会加载到应用中依赖的log4j,因此需要判断classloader - if (loggerClass.getClassLoader().equals(Log4jHelper.class.getClassLoader())) { - LogManager.getLoggerRepository(); - try { - Class throwableRendererClass = Class.forName("org.apache.log4j.spi.ThrowableRenderer"); - // 这里可能会加载到应用中依赖的log4j 1.2.16版本的类,因此需要额外判断 - if (loggerClass.getClassLoader().equals(throwableRendererClass.getClassLoader()) - && throwableRendererClass.getClassLoader().equals(Log4jHelper.class.getClassLoader())) { - Log4jGT1216 = true; - } - } catch (Throwable t) { - LogLog.warn("log4j must >= 1.2.16 for change throwable depth"); - } - Log4j = true; - } - } catch (Throwable t) { - } - } - @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL") - public static Boolean setDepth(int depth) { - if (Log4j && Log4jGT1216) { - try { - LoggerRepository repo = LogManager.getLoggerRepository(); - doSetDepth(repo, depth); - return Boolean.TRUE; - } catch (Throwable t) { - // ignore - LogLog.error("failed to set depth for log4j", t); - return Boolean.FALSE; - } - } - - return null; - } - @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL") - public static Boolean changeLevel(String name, String level) { - if (Log4j) { - Level l = Level.toLevel(level, Level.ERROR); - Logger logger = LogManager.getLoggerRepository().exists(name); - if (logger != null) { - logger.setLevel(l); - LogLog.info("set log4j log level success, " + name + ": " + l); - return true; - } else { - Logger root = LogManager.getLoggerRepository().getRootLogger(); - if (root.getName().equals(name)) { - root.setLevel(l); - LogLog.info("set log4j log level success, " + name + ": " + l); - return true; - } - } - LogLog.info("set log4j log level fail, no logger name exists: " + name); - return false; - } - return null; - } - - public static Map getLoggers(String name) { - Map appenders = new HashMap(10); - if (!Log4j) { - return appenders; - } - - if (name != null && !"".equals(name.trim())) { - Logger logger = LogManager.getLoggerRepository().exists(name); - if (logger != null) { - appenders.put(name, doGetLoggerInfo(logger)); - } - } else { - // 获取所有logger时,如果没有appender则忽略 - Enumeration loggers = LogManager.getLoggerRepository().getCurrentLoggers(); - - if (loggers != null) { - while (loggers.hasMoreElements()) { - Logger logger = loggers.nextElement(); - LoggerInfo info = doGetLoggerInfo(logger); - if (info.getAppenders() == null || !info.getAppenders().isEmpty()) { - appenders.put(logger.getName(), info); - } - } - } - - Logger root = LogManager.getLoggerRepository().getRootLogger(); - if (root != null) { - LoggerInfo info = doGetLoggerInfo(root); - if (info.getAppenders() == null || !info.getAppenders().isEmpty()) { - appenders.put(root.getName(), info); - } - } - } - - return appenders; - } - - private static LoggerInfo doGetLoggerInfo(Logger logger) { - LoggerInfo info = new LoggerInfo(logger.getName(), logger.getAdditivity()); - Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel(); - if (level != null) { - info.setLevel(level.toString()); - } - if (effectiveLevel != null) { - info.setEffectiveLevel(effectiveLevel.toString()); - } - - List result = doGetLoggerAppenders(logger.getAllAppenders()); - info.setAppenders(result); - return info; - } - - private static List doGetLoggerAppenders(Enumeration appenders) { - List result = new ArrayList(); - - while (appenders.hasMoreElements()) { - AppenderInfo info = new AppenderInfo(); - Appender appender = appenders.nextElement(); - - info.setName(appender.getName()); - info.setType(appender.getClass().getName()); - - result.add(info); - if (appender instanceof FileAppender) { - info.setFile(((FileAppender) appender).getFile()); - } else if (appender instanceof ConsoleAppender) { - info.withDetail("target", ((ConsoleAppender) appender).getTarget()); - } else if (appender instanceof AsyncAppender) { - List asyncs = doGetLoggerAppenders(((AsyncAppender) appender).getAllAppenders()); - // 标明异步appender - List nestedNames = new ArrayList(); - for (AppenderInfo a : asyncs) { - nestedNames.add(a.getName()); - result.add(a); - } - info.withDetail("nestedNames", nestedNames); - } - } - - return result; - } - - private static void doSetDepth(LoggerRepository repo, int depth) { - if (repo instanceof ThrowableRendererSupport) { - Object tr = ((ThrowableRendererSupport) repo).getThrowableRenderer(); - if (tr == null || !(tr instanceof DepthThrowableRenderer)) { - Object ctr = new DepthThrowableRenderer(depth); - // 自定义ThrowableRender,栈深度设置 - ((ThrowableRendererSupport) repo).setThrowableRenderer((ThrowableRenderer) ctr); - LogLog.info("set log4j log depth success, depth: " + depth); - } else { - ((DepthThrowableRenderer) tr).setDepth(depth); - LogLog.info("set log4j log depth success, depth: " + depth); - } - } - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/LogLog.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/LogLog.java index 9c7779ac2..474130547 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/LogLog.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/LogLog.java @@ -33,10 +33,11 @@ package com.alibaba.nacos.client.logger.support; import java.io.PrintStream; import java.util.Calendar; + /** - * logger log - * @author Nacos + * logger log * + * @author Nacos */ public class LogLog { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/LogbackHelper.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/LogbackHelper.java index 8028f3ee6..bd9c0595a 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/LogbackHelper.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/LogbackHelper.java @@ -60,7 +60,9 @@ public class LogbackHelper { ILoggerFactory lc = org.slf4j.LoggerFactory.getILoggerFactory(); if (!(lc instanceof LoggerContext)) { - LogLog.warn("expected logback binding with SLF4J, but another log system has taken the place: " + lcObject.getClass().getSimpleName()); + LogLog.warn( + "expected logback binding with SLF4J, but another log system has taken the place: " + lcObject + .getClass().getSimpleName()); } else { lcObject = lc; @@ -77,6 +79,7 @@ public class LogbackHelper { LogLog.error("failed to init LogbackHelper, " + t.getMessage()); } } + @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL") public static Boolean setDepth(int depth) { if (Logback) { @@ -84,7 +87,7 @@ public class LogbackHelper { depth = Integer.MAX_VALUE; } try { - LoggerContext loggerContext = (LoggerContext) lcObject; + LoggerContext loggerContext = (LoggerContext)lcObject; List loggers = loggerContext.getLoggerList(); for (ch.qos.logback.classic.Logger logger : loggers) { @@ -100,12 +103,13 @@ public class LogbackHelper { } return null; } + @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL") public static Boolean changeLevel(String name, String level) { if (Logback) { try { Level l = Level.toLevel(level, Level.ERROR); - LoggerContext loggerContext = (LoggerContext) lcObject; + LoggerContext loggerContext = (LoggerContext)lcObject; Logger logger = loggerContext.exists(name); if (logger != null) { @@ -126,7 +130,7 @@ public class LogbackHelper { Map appenders = new HashMap(10); if (Logback) { - LoggerContext loggerContext = (LoggerContext) lcObject; + LoggerContext loggerContext = (LoggerContext)lcObject; if (name != null && !"".equals(name.trim())) { Logger logger = loggerContext.exists(name); if (logger != null) { @@ -148,24 +152,24 @@ public class LogbackHelper { } private static void doSetDepth(Iterator> iter, int depth) - throws IllegalAccessException { + throws IllegalAccessException { while (iter.hasNext()) { Appender a = iter.next(); if (a instanceof AsyncAppenderBase) { - Iterator> aiter = ((AsyncAppenderBase) a).iteratorForAppenders(); + Iterator> aiter = ((AsyncAppenderBase)a).iteratorForAppenders(); doSetDepth(aiter, depth); } else if (a instanceof OutputStreamAppender) { - OutputStreamAppender oa = (OutputStreamAppender) a; + OutputStreamAppender oa = (OutputStreamAppender)a; Encoder e = oa.getEncoder(); Layout l = null; if (e instanceof PatternLayoutEncoder) { - l = ((PatternLayoutEncoder) e).getLayout(); + l = ((PatternLayoutEncoder)e).getLayout(); } else if (e instanceof LayoutWrappingEncoder) { - l = ((LayoutWrappingEncoder) e).getLayout(); + l = ((LayoutWrappingEncoder)e).getLayout(); } if (l != null) { if (l instanceof PatternLayoutBase) { - Converter c = (Converter) f.get(l); + Converter c = (Converter)f.get(l); while (c != null) { if (c instanceof ThrowableProxyConverter) { f1.set(c, depth); @@ -183,10 +187,10 @@ public class LogbackHelper { LoggerInfo info = new LoggerInfo(logger.getName(), logger.isAdditive()); Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel(); if (level != null) { - info.setLevel(level.toString()); + info.setLevel(level.toString()); } if (effectiveLevel != null) { - info.setEffectiveLevel(effectiveLevel.toString()); + info.setEffectiveLevel(effectiveLevel.toString()); } List result = doGetLoggerAppenders(logger.iteratorForAppenders()); @@ -203,9 +207,9 @@ public class LogbackHelper { info.setName(appender.getName()); info.setType(appender.getClass().getName()); if (appender instanceof FileAppender) { - info.setFile(((FileAppender) appender).getFile()); + info.setFile(((FileAppender)appender).getFile()); } else if (appender instanceof AsyncAppender) { - AsyncAppender aa = (AsyncAppender) appender; + AsyncAppender aa = (AsyncAppender)appender; Iterator> iter = aa.iteratorForAppenders(); List asyncs = doGetLoggerAppenders(iter); // 标明异步appender @@ -216,7 +220,7 @@ public class LogbackHelper { } info.withDetail("nestedNames", nestedNames); } else if (appender instanceof ConsoleAppender) { - info.withDetail("target", ((ConsoleAppender) appender).getTarget()); + info.withDetail("target", ((ConsoleAppender)appender).getTarget()); } result.add(info); } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerHelper.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerHelper.java index 01a7bba86..199584654 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerHelper.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerHelper.java @@ -31,20 +31,20 @@ import com.alibaba.nacos.client.logger.Logger; /** * logger help - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") public abstract class LoggerHelper { - private static final String MORE_URL_POSFIX = ".ERROR_CODE_MORE_URL"; - private static final String DEFAULT_MORE_URL = "http://console.taobao.net/help/"; + private static final String MORE_URL_POSFIX = ".ERROR_CODE_MORE_URL"; + private static final String DEFAULT_MORE_URL = "http://console.taobao.net/help/"; - private static String LOG_PATH = null; - private static final String CONVERSION_PATTERN = "01 %d{yyyy-MM-dd HH:mm:ss.SSS} %p [%-5t:%c{2}] %m%n"; + private static String LOG_PATH = null; + private static final String CONVERSION_PATTERN = "01 %d{yyyy-MM-dd HH:mm:ss.SSS} %p [%-5t:%c{2}] %m%n"; private static Map Product_Logger_Info; - private static Map Product_Logger_Pattern; + private static Map Product_Logger_Pattern; private static Map Product_Resource_Bundle; @@ -55,7 +55,7 @@ public abstract class LoggerHelper { LOG_PATH = defaultPath + File.separator + "logs" + File.separator; } else { if (!new File(dpath).isAbsolute()) { -// throw new RuntimeException("-DJM.LOG.PATH must be an absolute path."); + // throw new RuntimeException("-DJM.LOG.PATH must be an absolute path."); String defaultPath = System.getProperty("user.home"); dpath = defaultPath + File.separator + dpath; } @@ -83,15 +83,15 @@ public abstract class LoggerHelper { /** *

      * 获取中间件产品日志路径
-     * 
+     *
      * 优先使用-DJM.LOG.PATH参数,且必须是绝对路径
      * 其次是{user.home}/logs/
-     * 
+     *
      * 比如hsf调用:LoggerHelper.getLogFile("hsf", "hsf.log"),则返回{user.home}/logs/hsf/hsf.log
      * 
- * + * * @param productName 中间件产品名,如hsf, tddl - * @param fileName 日志文件名,如hsf.log,如需要二级子目录,可以传 subDir + File.separator + *.log + * @param fileName 日志文件名,如hsf.log,如需要二级子目录,可以传 subDir + File.separator + *.log */ public static String getLogFile(String productName, String fileName) { String file = LOG_PATH + productName + File.separator + fileName; @@ -125,9 +125,9 @@ public abstract class LoggerHelper { /** * 设置特定中间件产品的日志格式,注意,这里的格式需要自己保证在 log4j/logback 下都兼容,框架不做校验,同时控制台输出仍会采用中间件的特定格式 - * + * * @param productName 中间件产品名,如hsf, tddl - * @param pattern 日志格式 + * @param pattern 日志格式 */ public static void setPattern(String productName, String pattern) { Product_Logger_Pattern.put(productName, pattern); @@ -137,7 +137,7 @@ public abstract class LoggerHelper { * 设置产品的日志国际化properties文件 * * @param productName 中间件产品名,如hsf, tddl - * @param bundleName bundleName + * @param bundleName bundleName */ public static void setResourceBundle(String productName, String bundleName) { try { @@ -152,7 +152,7 @@ public abstract class LoggerHelper { * 获取国际化的message,如果找不到,则返回原始的code * * @param productName 中间件产品名,如hsf, tddl - * @param code code + * @param code code */ public static String getResourceBundleString(String productName, String code) { if (Product_Resource_Bundle.isEmpty() || code == null || productName == null) { @@ -174,7 +174,7 @@ public abstract class LoggerHelper { /** * 获取统一格式的ErrorCode输出 - * + * * @param errorCode */ @Deprecated @@ -184,11 +184,11 @@ public abstract class LoggerHelper { /** * 根据productName获取统一格式的ErrorCode输出 - * + * * @param productName 如 HSF,会根据 HSF.ErrorCodeMoreUrl 从 System属性中获取 more url 前缀,如http://console.taobao.net/jm/ - * @param errorCode 错误码,如HSF-001 - * @param errorType 错误类型 - * @param message 出错异常信息 + * @param errorCode 错误码,如HSF-001 + * @param errorType 错误类型 + * @param message 出错异常信息 */ public static String getErrorCodeStr(String productName, String errorCode, String errorType, String message) { String moreUrl = DEFAULT_MORE_URL; @@ -214,18 +214,17 @@ public abstract class LoggerHelper { return sb.toString(); } - @SuppressFBWarnings(value = { "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" }) - public static String getLogFileP(String productName, String fileName) { - String file = getLogFile(productName, fileName); - File logfile = new File(file); - logfile.getParentFile().mkdirs(); - return file; - } + @SuppressFBWarnings(value = {"RV_RETURN_VALUE_IGNORED_BAD_PRACTICE"}) + public static String getLogFileP(String productName, String fileName) { + String file = getLogFile(productName, fileName); + File logfile = new File(file); + logfile.getParentFile().mkdirs(); + return file; + } /** - * When prudent is set to true, file appenders from multiple JVMs can safely - * write to the same file. - * + * When prudent is set to true, file appenders from multiple JVMs can safely write to the same file. + *

* Only support by logback * * @param prudent @@ -235,14 +234,15 @@ public abstract class LoggerHelper { if (logger != null && logger.getDelegate() != null) { if (!(logger.getDelegate() instanceof ch.qos.logback.classic.Logger)) { throw new IllegalArgumentException("logger must be ch.qos.logback.classic.Logger, but it's " - + logger.getDelegate().getClass()); + + logger.getDelegate().getClass()); } - Iterator> iter = ((ch.qos.logback.classic.Logger) logger.getDelegate()).iteratorForAppenders(); + Iterator> iter = ((ch.qos.logback.classic.Logger)logger.getDelegate()) + .iteratorForAppenders(); while (iter.hasNext()) { ch.qos.logback.core.Appender appender = iter.next(); if (appender instanceof FileAppender) { - ((FileAppender) appender).setPrudent(prudent); + ((FileAppender)appender).setPrudent(prudent); } else { continue; } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerInfo.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerInfo.java index 9f484e07c..5e6eb3c0f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerInfo.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerInfo.java @@ -41,11 +41,11 @@ public class LoggerInfo extends HashMap { } public String getLevel() { - return (String) get(level); + return (String)get(level); } public List getAppenders() { - return (List) get(appenders); + return (List)get(appenders); } public void setAppenders(List appenders) { diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerSupport.java b/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerSupport.java index 6d459666b..55fd4f3c4 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerSupport.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/support/LoggerSupport.java @@ -21,15 +21,16 @@ import java.util.Map; import com.alibaba.nacos.client.logger.Level; import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.logger.option.ActivateOption; + /** * Logger Support - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") public abstract class LoggerSupport implements Logger { - protected Object delegateLogger; + protected Object delegateLogger; protected ActivateOption activateOption; public LoggerSupport(Object delegate) { @@ -137,7 +138,8 @@ public abstract class LoggerSupport implements Logger { } @Override - public void activateAsyncAppender(String productName, String file, String encoding, int queueSize, int discardingThreshold) { + public void activateAsyncAppender(String productName, String file, String encoding, int queueSize, + int discardingThreshold) { if (activateOption != null) { activateOption.activateAsyncAppender(productName, file, encoding, queueSize, discardingThreshold); } @@ -170,7 +172,7 @@ public abstract class LoggerSupport implements Logger { String datePattern, int maxBackupIndex) { if (activateOption != null) { activateOption.activateAppenderWithTimeAndSizeRolling(productName, file, encoding, size, datePattern, - maxBackupIndex); + maxBackupIndex); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/util/FormattingTuple.java b/client/src/main/java/com/alibaba/nacos/client/logger/util/FormattingTuple.java index 91c4a3783..7c925b73f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/util/FormattingTuple.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/util/FormattingTuple.java @@ -17,52 +17,51 @@ package com.alibaba.nacos.client.logger.util; /** * Holds the results of formatting done by {@link MessageFormatter}. - * + * * @author Joern Huxhorn */ public class FormattingTuple { - - - static public FormattingTuple NULL = new FormattingTuple(null); - - private String message; - private Throwable throwable; - private Object[] argArray; - - public FormattingTuple(String message) { - this(message, null, null); - } - public FormattingTuple(String message, Object[] argArray, Throwable throwable) { - this.message = message; - this.throwable = throwable; - if(throwable == null) { - this.argArray = argArray.clone(); - } else { - this.argArray = trimmedCopy(argArray); + static public FormattingTuple NULL = new FormattingTuple(null); + + private String message; + private Throwable throwable; + private Object[] argArray; + + public FormattingTuple(String message) { + this(message, null, null); } - } - static Object[] trimmedCopy(Object[] argArray) { - if(argArray == null || argArray.length == 0) { - throw new IllegalStateException("non-sensical empty or null argument array"); + public FormattingTuple(String message, Object[] argArray, Throwable throwable) { + this.message = message; + this.throwable = throwable; + if (throwable == null) { + this.argArray = argArray.clone(); + } else { + this.argArray = trimmedCopy(argArray); + } } - final int trimemdLen = argArray.length -1; - Object[] trimmed = new Object[trimemdLen]; - System.arraycopy(argArray, 0, trimmed, 0, trimemdLen); - return trimmed; - } - - public String getMessage() { - return message; - } - public Object[] getArgArray() { - return argArray.clone(); - } + static Object[] trimmedCopy(Object[] argArray) { + if (argArray == null || argArray.length == 0) { + throw new IllegalStateException("non-sensical empty or null argument array"); + } + final int trimemdLen = argArray.length - 1; + Object[] trimmed = new Object[trimemdLen]; + System.arraycopy(argArray, 0, trimmed, 0, trimemdLen); + return trimmed; + } - public Throwable getThrowable() { - return throwable; - } + public String getMessage() { + return message; + } + + public Object[] getArgArray() { + return argArray.clone(); + } + + public Throwable getThrowable() { + return throwable; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageFormatter.java b/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageFormatter.java index 0faa5c9d3..e418c45ce 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageFormatter.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageFormatter.java @@ -21,401 +21,380 @@ import java.util.Map; // contributors: lizongbo: proposed special treatment of array parameter values // Joern Huxhorn: pointed out double[] omission, suggested deep array copy + /** - * Formats messages according to very simple substitution rules. Substitutions - * can be made 1, 2 or more arguments. - * + * Formats messages according to very simple substitution rules. Substitutions can be made 1, 2 or more arguments. + *

*

* For example, - * + *

*

  * MessageFormatter.format("Hi {}.", "there")
  * 
- * + *

* will return the string "Hi there.". *

- * The {} pair is called the formatting anchor. It serves to designate - * the location where arguments need to be substituted within the message - * pattern. + * The {} pair is called the formatting anchor. It serves to designate the location where arguments need to be + * substituted within the message pattern. + *

+ * In case your message contains the '{' or the '}' character, you do not have to do anything special unless the '}' + * character immediately follows '{'. For example, *

- * In case your message contains the '{' or the '}' character, you do not have - * to do anything special unless the '}' character immediately follows '{'. For - * example, - * *

  * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
  * 
- * - * will return the string "Set {1,2,3} is not equal to 1,2.". - * *

- * If for whatever reason you need to place the string "{}" in the message - * without its formatting anchor meaning, then you need to escape the - * '{' character with '\', that is the backslash character. Only the '{' - * character should be escaped. There is no need to escape the '}' character. - * For example, - * + * will return the string "Set {1,2,3} is not equal to 1,2.". + *

+ *

+ * If for whatever reason you need to place the string "{}" in the message without its formatting anchor + * meaning, then you need to escape the '{' character with '\', that is the backslash character. Only the '{' character + * should be escaped. There is no need to escape the '}' character. For example, + *

*

  * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
  * 
- * - * will return the string "Set {} is not equal to 1,2.". - * *

- * The escaping behavior just described can be overridden by escaping the escape - * character '\'. Calling - * + * will return the string "Set {} is not equal to 1,2.". + *

+ *

+ * The escaping behavior just described can be overridden by escaping the escape character '\'. Calling + *

*

  * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
  * 
- * + *

* will return the string "File name is C:\file.zip". - * *

- * The formatting conventions are different than those of {@link MessageFormat} - * which ships with the Java platform. This is justified by the fact that - * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. - * This local performance difference is both measurable and significant in the - * larger context of the complete logging processing chain. - * *

- * See also {@link #format(String, Object)}, - * {@link #format(String, Object, Object)} and - * {@link #arrayFormat(String, Object[])} methods for more details. - * + * The formatting conventions are different than those of {@link MessageFormat} which ships with the Java platform. This + * is justified by the fact that SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. This + * local performance difference is both measurable and significant in the larger context of the complete logging + * processing chain. + *

+ *

+ * See also {@link #format(String, Object)}, {@link #format(String, Object, Object)} and {@link #arrayFormat(String, + * Object[])} methods for more details. + * * @author Ceki Gülcü * @author Joern Huxhorn */ final public class MessageFormatter { - static final char DELIM_START = '{'; - static final char DELIM_STOP = '}'; - static final String DELIM_STR = "{}"; - private static final char ESCAPE_CHAR = '\\'; - private static int DELIMETER_START_INDEX = 2; - /** - * Performs single argument substitution for the 'messagePattern' passed as - * parameter. - *

- * For example, - * - *

-   * MessageFormatter.format("Hi {}.", "there");
-   * 
- * - * will return the string "Hi there.". - *

- * - * @param messagePattern - * The message pattern which will be parsed and formatted - * @param argument - * The argument to be substituted in place of the formatting anchor - * @return The formatted message - */ - final public static FormattingTuple format(String messagePattern, Object arg) { - return arrayFormat(messagePattern, new Object[] { arg }); - } + static final char DELIM_START = '{'; + static final char DELIM_STOP = '}'; + static final String DELIM_STR = "{}"; + private static final char ESCAPE_CHAR = '\\'; + private static int DELIMETER_START_INDEX = 2; - /** - * - * Performs a two argument substitution for the 'messagePattern' passed as - * parameter. - *

- * For example, - * - *

-   * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
-   * 
- * - * will return the string "Hi Alice. My name is Bob.". - * - * @param messagePattern - * The message pattern which will be parsed and formatted - * @param arg1 - * The argument to be substituted in place of the first formatting - * anchor - * @param arg2 - * The argument to be substituted in place of the second formatting - * anchor - * @return The formatted message - */ - final public static FormattingTuple format(final String messagePattern, - Object arg1, Object arg2) { - return arrayFormat(messagePattern, new Object[] { arg1, arg2 }); - } - - static final Throwable getThrowableCandidate(Object[] argArray) { - if (argArray == null || argArray.length == 0) { - return null; + /** + * Performs single argument substitution for the 'messagePattern' passed as parameter. + *

+ * For example, + *

+ *

+     * MessageFormatter.format("Hi {}.", "there");
+     * 
+ *

+ * will return the string "Hi there.". + *

+ * + * @param messagePattern The message pattern which will be parsed and formatted + * @param argument The argument to be substituted in place of the formatting anchor + * @return The formatted message + */ + final public static FormattingTuple format(String messagePattern, Object arg) { + return arrayFormat(messagePattern, new Object[] {arg}); } - final Object lastEntry = argArray[argArray.length - 1]; - if (lastEntry instanceof Throwable) { - return (Throwable) lastEntry; - } - return null; - } - - /** - * Same principle as the {@link #format(String, Object)} and - * {@link #format(String, Object, Object)} methods except that any number of - * arguments can be passed in an array. - * - * @param messagePattern - * The message pattern which will be parsed and formatted - * @param argArray - * An array of arguments to be substituted in place of formatting - * anchors - * @return The formatted message - */ - final public static FormattingTuple arrayFormat(final String messagePattern, - final Object[] argArray) { - - Throwable throwableCandidate = getThrowableCandidate(argArray); - - if (messagePattern == null) { - return new FormattingTuple(null, argArray, throwableCandidate); + /** + * Performs a two argument substitution for the 'messagePattern' passed as parameter. + *

+ * For example, + *

+ *

+     * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
+     * 
+ *

+ * will return the string "Hi Alice. My name is Bob.". + * + * @param messagePattern The message pattern which will be parsed and formatted + * @param arg1 The argument to be substituted in place of the first formatting anchor + * @param arg2 The argument to be substituted in place of the second formatting anchor + * @return The formatted message + */ + final public static FormattingTuple format(final String messagePattern, + Object arg1, Object arg2) { + return arrayFormat(messagePattern, new Object[] {arg1, arg2}); } - if (argArray == null) { - return new FormattingTuple(messagePattern); + static final Throwable getThrowableCandidate(Object[] argArray) { + if (argArray == null || argArray.length == 0) { + return null; + } + + final Object lastEntry = argArray[argArray.length - 1]; + if (lastEntry instanceof Throwable) { + return (Throwable)lastEntry; + } + return null; } - int i = 0; - int j; - StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50); + /** + * Same principle as the {@link #format(String, Object)} and {@link #format(String, Object, Object)} methods except + * that any number of arguments can be passed in an array. + * + * @param messagePattern The message pattern which will be parsed and formatted + * @param argArray An array of arguments to be substituted in place of formatting anchors + * @return The formatted message + */ + final public static FormattingTuple arrayFormat(final String messagePattern, + final Object[] argArray) { - int lenTmp; - for (lenTmp = 0; lenTmp < argArray.length; lenTmp++) { + Throwable throwableCandidate = getThrowableCandidate(argArray); - j = messagePattern.indexOf(DELIM_STR, i); + if (messagePattern == null) { + return new FormattingTuple(null, argArray, throwableCandidate); + } - if (j == -1) { - // no more variables - if (i == 0) { - return new FormattingTuple(messagePattern, argArray, - throwableCandidate); + if (argArray == null) { + return new FormattingTuple(messagePattern); + } + + int i = 0; + int j; + StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50); + + int lenTmp; + for (lenTmp = 0; lenTmp < argArray.length; lenTmp++) { + + j = messagePattern.indexOf(DELIM_STR, i); + + if (j == -1) { + // no more variables + if (i == 0) { + return new FormattingTuple(messagePattern, argArray, + throwableCandidate); + } else { + sbuf.append(messagePattern.substring(i, messagePattern.length())); + return new FormattingTuple(sbuf.toString(), argArray, + throwableCandidate); + } + } else { + if (isEscapedDelimeter(messagePattern, j)) { + if (!isDoubleEscaped(messagePattern, j)) { + lenTmp--; // DELIM_START was escaped, thus should not be incremented + sbuf.append(messagePattern.substring(i, j - 1)); + sbuf.append(DELIM_START); + i = j + 1; + } else { + // The escape character preceding the delimiter start is + // itself escaped: "abc x:\\{}" + // we have to consume one backward slash + sbuf.append(messagePattern.substring(i, j - 1)); + deeplyAppendParameter(sbuf, argArray[lenTmp], new HashMap(10)); + i = j + 2; + } + } else { + // normal case + sbuf.append(messagePattern.substring(i, j)); + deeplyAppendParameter(sbuf, argArray[lenTmp], new HashMap(10)); + i = j + 2; + } + } + } + // append the characters following the last {} pair. + sbuf.append(messagePattern.substring(i, messagePattern.length())); + if (lenTmp < argArray.length - 1) { + return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate); } else { - sbuf.append(messagePattern.substring(i, messagePattern.length())); - return new FormattingTuple(sbuf.toString(), argArray, - throwableCandidate); + return new FormattingTuple(sbuf.toString(), argArray, null); } - } else { - if (isEscapedDelimeter(messagePattern, j)) { - if (!isDoubleEscaped(messagePattern, j)) { - lenTmp--; // DELIM_START was escaped, thus should not be incremented - sbuf.append(messagePattern.substring(i, j - 1)); - sbuf.append(DELIM_START); - i = j + 1; - } else { - // The escape character preceding the delimiter start is - // itself escaped: "abc x:\\{}" - // we have to consume one backward slash - sbuf.append(messagePattern.substring(i, j - 1)); - deeplyAppendParameter(sbuf, argArray[lenTmp], new HashMap(10)); - i = j + 2; - } + } + + final static boolean isEscapedDelimeter(String messagePattern, + int delimeterStartIndex) { + + if (delimeterStartIndex == 0) { + return false; + } + char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); + if (potentialEscape == ESCAPE_CHAR) { + return true; } else { - // normal case - sbuf.append(messagePattern.substring(i, j)); - deeplyAppendParameter(sbuf, argArray[lenTmp], new HashMap(10)); - i = j + 2; + return false; } - } - } - // append the characters following the last {} pair. - sbuf.append(messagePattern.substring(i, messagePattern.length())); - if (lenTmp < argArray.length - 1) { - return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate); - } else { - return new FormattingTuple(sbuf.toString(), argArray, null); - } - } - - final static boolean isEscapedDelimeter(String messagePattern, - int delimeterStartIndex) { - - if (delimeterStartIndex == 0) { - return false; - } - char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); - if (potentialEscape == ESCAPE_CHAR) { - return true; - } else { - return false; - } - } - - final static boolean isDoubleEscaped(String messagePattern, - int delimeterStartIndex) { - if (delimeterStartIndex >= DELIMETER_START_INDEX - && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { - return true; - } else { - return false; - } - } - - - private static void deeplyAppendParameter(StringBuffer sbuf, Object o, - Map seenMap) { - if (o == null) { - sbuf.append("null"); - return; - } - if (!o.getClass().isArray()) { - safeObjectAppend(sbuf, o); - } else { - // check for primitive array types because they - // unfortunately cannot be cast to Object[] - if (o instanceof boolean[]) { - booleanArrayAppend(sbuf, (boolean[]) o); - } else if (o instanceof byte[]) { - byteArrayAppend(sbuf, (byte[]) o); - } else if (o instanceof char[]) { - charArrayAppend(sbuf, (char[]) o); - } else if (o instanceof short[]) { - shortArrayAppend(sbuf, (short[]) o); - } else if (o instanceof int[]) { - intArrayAppend(sbuf, (int[]) o); - } else if (o instanceof long[]) { - longArrayAppend(sbuf, (long[]) o); - } else if (o instanceof float[]) { - floatArrayAppend(sbuf, (float[]) o); - } else if (o instanceof double[]) { - doubleArrayAppend(sbuf, (double[]) o); - } else { - objectArrayAppend(sbuf, (Object[]) o, seenMap); - } - } - } - - private static void safeObjectAppend(StringBuffer sbuf, Object o) { - try { - String oAsString = o.toString(); - sbuf.append(oAsString); - } catch (Throwable t) { - System.err - .println("SLF4J: Failed toString() invocation on an object of type [" - + o.getClass().getName() + "]"); - t.printStackTrace(); - sbuf.append("[FAILED toString()]"); } - } - - private static void objectArrayAppend(StringBuffer sbuf, Object[] a, - Map seenMap) { - sbuf.append('['); - if (!seenMap.containsKey(a)) { - seenMap.put(a, null); - final int len = a.length; - for (int i = 0; i < len; i++) { - deeplyAppendParameter(sbuf, a[i], seenMap); - if (i != len - 1) { - sbuf.append(", "); + final static boolean isDoubleEscaped(String messagePattern, + int delimeterStartIndex) { + if (delimeterStartIndex >= DELIMETER_START_INDEX + && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { + return true; + } else { + return false; } - } - // allow repeats in siblings - seenMap.remove(a); - } else { - sbuf.append("..."); } - sbuf.append(']'); - } - private static void booleanArrayAppend(StringBuffer sbuf, boolean[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } + private static void deeplyAppendParameter(StringBuffer sbuf, Object o, + Map seenMap) { + if (o == null) { + sbuf.append("null"); + return; + } + if (!o.getClass().isArray()) { + safeObjectAppend(sbuf, o); + } else { + // check for primitive array types because they + // unfortunately cannot be cast to Object[] + if (o instanceof boolean[]) { + booleanArrayAppend(sbuf, (boolean[])o); + } else if (o instanceof byte[]) { + byteArrayAppend(sbuf, (byte[])o); + } else if (o instanceof char[]) { + charArrayAppend(sbuf, (char[])o); + } else if (o instanceof short[]) { + shortArrayAppend(sbuf, (short[])o); + } else if (o instanceof int[]) { + intArrayAppend(sbuf, (int[])o); + } else if (o instanceof long[]) { + longArrayAppend(sbuf, (long[])o); + } else if (o instanceof float[]) { + floatArrayAppend(sbuf, (float[])o); + } else if (o instanceof double[]) { + doubleArrayAppend(sbuf, (double[])o); + } else { + objectArrayAppend(sbuf, (Object[])o, seenMap); + } + } } - sbuf.append(']'); - } - private static void byteArrayAppend(StringBuffer sbuf, byte[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } - } - sbuf.append(']'); - } + private static void safeObjectAppend(StringBuffer sbuf, Object o) { + try { + String oAsString = o.toString(); + sbuf.append(oAsString); + } catch (Throwable t) { + System.err + .println("SLF4J: Failed toString() invocation on an object of type [" + + o.getClass().getName() + "]"); + t.printStackTrace(); + sbuf.append("[FAILED toString()]"); + } - private static void charArrayAppend(StringBuffer sbuf, char[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } } - sbuf.append(']'); - } - private static void shortArrayAppend(StringBuffer sbuf, short[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } + private static void objectArrayAppend(StringBuffer sbuf, Object[] a, + Map seenMap) { + sbuf.append('['); + if (!seenMap.containsKey(a)) { + seenMap.put(a, null); + final int len = a.length; + for (int i = 0; i < len; i++) { + deeplyAppendParameter(sbuf, a[i], seenMap); + if (i != len - 1) { + sbuf.append(", "); + } + } + // allow repeats in siblings + seenMap.remove(a); + } else { + sbuf.append("..."); + } + sbuf.append(']'); } - sbuf.append(']'); - } - private static void intArrayAppend(StringBuffer sbuf, int[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } + private static void booleanArrayAppend(StringBuffer sbuf, boolean[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); } - sbuf.append(']'); - } - private static void longArrayAppend(StringBuffer sbuf, long[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } + private static void byteArrayAppend(StringBuffer sbuf, byte[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); } - sbuf.append(']'); - } - private static void floatArrayAppend(StringBuffer sbuf, float[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } + private static void charArrayAppend(StringBuffer sbuf, char[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); } - sbuf.append(']'); - } - private static void doubleArrayAppend(StringBuffer sbuf, double[] a) { - sbuf.append('['); - final int len = a.length; - for (int i = 0; i < len; i++) { - sbuf.append(a[i]); - if (i != len - 1) { - sbuf.append(", "); - } + private static void shortArrayAppend(StringBuffer sbuf, short[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void intArrayAppend(StringBuffer sbuf, int[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void longArrayAppend(StringBuffer sbuf, long[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void floatArrayAppend(StringBuffer sbuf, float[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void doubleArrayAppend(StringBuffer sbuf, double[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); } - sbuf.append(']'); - } } diff --git a/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageUtil.java b/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageUtil.java index 766b0730c..1941c446b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/logger/util/MessageUtil.java @@ -14,10 +14,11 @@ * limitations under the License. */ package com.alibaba.nacos.client.logger.util; + /** * Error msg format - * @author Nacos * + * @author Nacos */ public class MessageUtil { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index 39d4df6c5..b4f381b64 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.client.naming; import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; @@ -23,6 +24,7 @@ import com.alibaba.nacos.api.naming.pojo.Cluster; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.naming.beat.BeatInfo; import com.alibaba.nacos.client.naming.beat.BeatReactor; import com.alibaba.nacos.client.naming.core.Balancer; @@ -33,6 +35,7 @@ import com.alibaba.nacos.client.naming.utils.CollectionUtils; import com.alibaba.nacos.client.naming.utils.LogUtils; import com.alibaba.nacos.client.naming.utils.StringUtils; import com.alibaba.nacos.client.naming.utils.UtilAndComs; +import org.apache.commons.lang3.BooleanUtils; import java.util.ArrayList; import java.util.Iterator; @@ -40,7 +43,7 @@ import java.util.List; import java.util.Properties; /** - * @author dungu.zpf + * @author nkorange */ @SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") public class NacosNamingService implements NamingService { @@ -99,7 +102,7 @@ public class NacosNamingService implements NamingService { eventDispatcher = new EventDispatcher(); serverProxy = new NamingProxy(namespace, endpoint, serverList); beatReactor = new BeatReactor(serverProxy); - hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir); + hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, false); } public NacosNamingService(Properties properties) { @@ -118,21 +121,27 @@ public class NacosNamingService implements NamingService { if (StringUtils.isNotEmpty(properties.getProperty(PropertyKeyConst.ENDPOINT))) { endpoint = properties.getProperty(PropertyKeyConst.ENDPOINT) + ":" + - properties.getProperty("address.server.port", "8080"); + properties.getProperty("address.server.port", "8080"); } cacheDir = System.getProperty("user.home") + "/nacos/naming/" + namespace; + boolean loadCacheAtStart = false; + if (StringUtils.isNotEmpty(properties.getProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START))) { + loadCacheAtStart = BooleanUtils.toBoolean( + properties.getProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START)); + } + eventDispatcher = new EventDispatcher(); serverProxy = new NamingProxy(namespace, endpoint, serverList); beatReactor = new BeatReactor(serverProxy); - hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir); + hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, loadCacheAtStart); } @Override public void registerInstance(String serviceName, String ip, int port) throws NacosException { - registerInstance(serviceName, ip, port, StringUtils.EMPTY); + registerInstance(serviceName, ip, port, Constants.NAMING_DEFAULT_CLUSTER_NAME); } @Override @@ -141,7 +150,7 @@ public class NacosNamingService implements NamingService { instance.setIp(ip); instance.setPort(port); instance.setWeight(1.0); - instance.setCluster(new Cluster(clusterName)); + instance.setClusterName(clusterName); registerInstance(serviceName, instance); } @@ -153,7 +162,10 @@ public class NacosNamingService implements NamingService { beatInfo.setDom(serviceName); beatInfo.setIp(instance.getIp()); beatInfo.setPort(instance.getPort()); - beatInfo.setCluster(instance.getCluster().getName()); + beatInfo.setCluster(instance.getClusterName()); + beatInfo.setWeight(instance.getWeight()); + beatInfo.setMetadata(instance.getMetadata()); + beatInfo.setScheduled(false); beatReactor.addBeatInfo(serviceName, beatInfo); @@ -162,12 +174,12 @@ public class NacosNamingService implements NamingService { @Override public void deregisterInstance(String serviceName, String ip, int port) throws NacosException { - deregisterInstance(serviceName, ip, port, StringUtils.EMPTY); + deregisterInstance(serviceName, ip, port, Constants.NAMING_DEFAULT_CLUSTER_NAME); } @Override public void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { - beatReactor.removeBeatInfo(serviceName); + beatReactor.removeBeatInfo(serviceName, ip, port); serverProxy.deregisterService(serviceName, ip, port, clusterName); } @@ -179,7 +191,8 @@ public class NacosNamingService implements NamingService { @Override public List getAllInstances(String serviceName, List clusters) throws NacosException { - ServiceInfo serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","), StringUtils.EMPTY, false); + ServiceInfo serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","), + StringUtils.EMPTY, false); List list; if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList(); @@ -193,29 +206,21 @@ public class NacosNamingService implements NamingService { } @Override - public List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException { + public List selectInstances(String serviceName, List clusters, boolean healthy) + throws NacosException { - ServiceInfo serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","), StringUtils.EMPTY, false); + ServiceInfo serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","), + StringUtils.EMPTY, false); List list; if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList(); } - if (healthy) { - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - Instance instance = iterator.next(); - if (!instance.isHealthy()) { - iterator.remove(); - } - } - } else { - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - Instance instance = iterator.next(); - if (instance.isHealthy()) { - iterator.remove(); - } + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + Instance instance = iterator.next(); + if (healthy != instance.isHealthy() || !instance.isEnabled() || instance.getWeight() <= 0) { + iterator.remove(); } } @@ -229,17 +234,20 @@ public class NacosNamingService implements NamingService { @Override public Instance selectOneHealthyInstance(String serviceName, List clusters) { - return Balancer.RandomByWeight.selectHost(hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","))); + return Balancer.RandomByWeight.selectHost( + hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","))); } @Override public void subscribe(String service, EventListener listener) { - eventDispatcher.addListener(hostReactor.getServiceInfo(service, StringUtils.EMPTY), StringUtils.EMPTY, listener); + eventDispatcher.addListener(hostReactor.getServiceInfo(service, StringUtils.EMPTY), StringUtils.EMPTY, + listener); } @Override public void subscribe(String service, List clusters, EventListener listener) { - eventDispatcher.addListener(hostReactor.getServiceInfo(service, StringUtils.join(clusters, ",")), StringUtils.join(clusters, ","), listener); + eventDispatcher.addListener(hostReactor.getServiceInfo(service, StringUtils.join(clusters, ",")), + StringUtils.join(clusters, ","), listener); } @Override @@ -257,6 +265,11 @@ public class NacosNamingService implements NamingService { return serverProxy.getServiceList(pageNo, pageSize); } + @Override + public ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException { + return serverProxy.getServiceList(pageNo, pageSize, selector); + } + @Override public List getSubscribeServices() { return new ArrayList(hostReactor.getServiceInfoMap().values()); @@ -266,4 +279,8 @@ public class NacosNamingService implements NamingService { public String getServerStatus() { return serverProxy.serverHealthy() ? "UP" : "DOWN"; } + + public BeatReactor getBeatReactor() { + return beatReactor; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverReactor.java b/client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverReactor.java index 10b7552c5..673d95738 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverReactor.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverReactor.java @@ -33,7 +33,7 @@ import java.util.*; import java.util.concurrent.*; /** - * @author dungu.zpf + * @author nkorange */ public class FailoverReactor { @@ -114,7 +114,8 @@ public class FailoverReactor { if (lastModifiedMillis < modified) { lastModifiedMillis = modified; - String failover = ConcurrentDiskUtil.getFileContent(failoverDir + UtilAndComs.FAILOVER_SWITCH, Charset.defaultCharset().toString()); + String failover = ConcurrentDiskUtil.getFileContent(failoverDir + UtilAndComs.FAILOVER_SWITCH, + Charset.defaultCharset().toString()); if (!StringUtils.isEmpty(failover)) { List lines = Arrays.asList(failover.split(DiskCache.getLineSeperator())); @@ -171,7 +172,8 @@ public class FailoverReactor { ServiceInfo dom = new ServiceInfo(file.getName()); try { - String dataString = ConcurrentDiskUtil.getFileContent(file, Charset.defaultCharset().toString()); + String dataString = ConcurrentDiskUtil.getFileContent(file, + Charset.defaultCharset().toString()); reader = new BufferedReader(new StringReader(dataString)); String json; @@ -213,10 +215,11 @@ public class FailoverReactor { Map map = hostReactor.getServiceInfoMap(); for (Map.Entry entry : map.entrySet()) { ServiceInfo serviceInfo = entry.getValue(); - if (StringUtils.equals(serviceInfo.getKey(), UtilAndComs.ALL_IPS) || StringUtils.equals(serviceInfo.getName(), UtilAndComs.ENV_LIST_KEY) - || StringUtils.equals(serviceInfo.getName(), "00-00---000-ENV_CONFIGS-000---00-00") - || StringUtils.equals(serviceInfo.getName(), "vipclient.properties") - || StringUtils.equals(serviceInfo.getName(), "00-00---000-ALL_HOSTS-000---00-00")) { + if (StringUtils.equals(serviceInfo.getKey(), UtilAndComs.ALL_IPS) || StringUtils.equals( + serviceInfo.getName(), UtilAndComs.ENV_LIST_KEY) + || StringUtils.equals(serviceInfo.getName(), "00-00---000-ENV_CONFIGS-000---00-00") + || StringUtils.equals(serviceInfo.getName(), "vipclient.properties") + || StringUtils.equals(serviceInfo.getName(), "00-00---000-ALL_HOSTS-000---00-00")) { continue; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatInfo.java b/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatInfo.java index f5a3fa87a..c90062105 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatInfo.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatInfo.java @@ -17,15 +17,20 @@ package com.alibaba.nacos.client.naming.beat; import com.alibaba.fastjson.JSON; +import java.util.Map; + /** - * @author dungu.zpf + * @author nkorange */ public class BeatInfo { private int port; private String ip; + private double weight; private String dom; private String cluster; + private Map metadata; + private boolean scheduled; @Override public String toString() { @@ -63,4 +68,28 @@ public class BeatInfo { public void setPort(int port) { this.port = port; } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } + + public boolean isScheduled() { + return scheduled; + } + + public void setScheduled(boolean scheduled) { + this.scheduled = scheduled; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java b/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java index 21fd9ca7c..fc66a0f33 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/beat/BeatReactor.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.client.naming.beat; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.client.naming.net.NamingProxy; import com.alibaba.nacos.client.naming.utils.LogUtils; import com.alibaba.nacos.client.naming.utils.UtilAndComs; @@ -40,7 +41,7 @@ public class BeatReactor { } }); - private long clientBeatInterval = 10 * 1000; + private long clientBeatInterval = 5 * 1000; private NamingProxy serverProxy; @@ -53,12 +54,16 @@ public class BeatReactor { public void addBeatInfo(String dom, BeatInfo beatInfo) { LogUtils.LOG.info("BEAT", "adding service:" + dom + " to beat map."); - dom2Beat.put(dom, beatInfo); + dom2Beat.put(buildKey(dom, beatInfo.getIp(), beatInfo.getPort()), beatInfo); } - public void removeBeatInfo(String dom) { + public void removeBeatInfo(String dom, String ip, int port) { LogUtils.LOG.info("BEAT", "removing service:" + dom + " from beat map."); - dom2Beat.remove(dom); + dom2Beat.remove(buildKey(dom, ip, port)); + } + + public String buildKey(String dom, String ip, int port) { + return dom + Constants.NAMING_INSTANCE_ID_SPLITTER + ip + Constants.NAMING_INSTANCE_ID_SPLITTER + port; } class BeatProcessor implements Runnable { @@ -68,8 +73,12 @@ public class BeatReactor { try { for (Map.Entry entry : dom2Beat.entrySet()) { BeatInfo beatInfo = entry.getValue(); + if (beatInfo.isScheduled()) { + continue; + } + beatInfo.setScheduled(true); executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS); - LogUtils.LOG.debug("BEAT", "send beat to server: ", beatInfo.toString()); + LogUtils.LOG.info("BEAT", "send beat to server: " + beatInfo.toString()); } } catch (Exception e) { LogUtils.LOG.error("CLIENT-BEAT", "Exception while scheduling beat.", e); @@ -78,6 +87,7 @@ public class BeatReactor { } class BeatTask implements Runnable { + BeatInfo beatInfo; public BeatTask(BeatInfo beatInfo) { @@ -91,6 +101,7 @@ public class BeatReactor { params.put("dom", beatInfo.getDom()); try { + beatInfo.setScheduled(false); String result = serverProxy.callAllServers(UtilAndComs.NACOS_URL_BASE + "/api/clientBeat", params); JSONObject jsonObject = JSON.parseObject(result); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/ConcurrentDiskUtil.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/ConcurrentDiskUtil.java index 114a65158..481e5da08 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/ConcurrentDiskUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/ConcurrentDiskUtil.java @@ -30,209 +30,189 @@ import com.alibaba.nacos.client.logger.Logger; import com.alibaba.nacos.client.naming.utils.LogUtils; /** - * @author dungu.zpf + * @author nkorange */ public class ConcurrentDiskUtil { - /** - * get file content - * - * @param path - * file path - * @param charsetName - * charsetName - * @return content - * @throws IOException - * IOException - */ - public static String getFileContent(String path, String charsetName) - throws IOException { - File file = new File(path); - return getFileContent(file, charsetName); - } + /** + * get file content + * + * @param path file path + * @param charsetName charsetName + * @return content + * @throws IOException IOException + */ + public static String getFileContent(String path, String charsetName) + throws IOException { + File file = new File(path); + return getFileContent(file, charsetName); + } - /** - * get file content - * - * @param file - * file - * @param charsetName - * charsetName - * @return content - * @throws IOException - * IOException - */ - public static String getFileContent(File file, String charsetName) - throws IOException { - RandomAccessFile fis = null; - FileLock rlock = null; - try { - fis = new RandomAccessFile(file, "r"); - FileChannel fcin = fis.getChannel(); - int i = 0; - do { - try { - rlock = fcin.tryLock(0L, Long.MAX_VALUE, true); - } catch (Exception e) { - ++i; - if (i > RETRY_COUNT) { - log.error("NA", "read " + file.getName() + " fail;retryed time: " + i, e); - throw new IOException("read " + file.getAbsolutePath() - + " conflict"); - } - sleep(SLEEP_BASETIME * i); - log.warn("read " + file.getName() + " conflict;retry time: " + i); - } - } while (null == rlock); - int fileSize = (int) fcin.size(); - ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize); - fcin.read(byteBuffer); - byteBuffer.flip(); - return byteBufferToString(byteBuffer, charsetName); - } finally { - if (rlock != null) { - rlock.release(); - rlock = null; - } - if (fis != null) { - fis.close(); - fis = null; - } - } - } + /** + * get file content + * + * @param file file + * @param charsetName charsetName + * @return content + * @throws IOException IOException + */ + public static String getFileContent(File file, String charsetName) + throws IOException { + RandomAccessFile fis = null; + FileLock rlock = null; + try { + fis = new RandomAccessFile(file, "r"); + FileChannel fcin = fis.getChannel(); + int i = 0; + do { + try { + rlock = fcin.tryLock(0L, Long.MAX_VALUE, true); + } catch (Exception e) { + ++i; + if (i > RETRY_COUNT) { + log.error("NA", "read " + file.getName() + " fail;retryed time: " + i, e); + throw new IOException("read " + file.getAbsolutePath() + + " conflict"); + } + sleep(SLEEP_BASETIME * i); + log.warn("read " + file.getName() + " conflict;retry time: " + i); + } + } while (null == rlock); + int fileSize = (int)fcin.size(); + ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize); + fcin.read(byteBuffer); + byteBuffer.flip(); + return byteBufferToString(byteBuffer, charsetName); + } finally { + if (rlock != null) { + rlock.release(); + rlock = null; + } + if (fis != null) { + fis.close(); + fis = null; + } + } + } - /** - * write file content - * - * @param path - * file path - * @param content - * content - * @param charsetName - * charsetName - * @return whether write ok - * @throws IOException - * IOException - */ - public static Boolean writeFileContent(String path, String content, - String charsetName) throws IOException { - File file = new File(path); - return writeFileContent(file, content, charsetName); - } + /** + * write file content + * + * @param path file path + * @param content content + * @param charsetName charsetName + * @return whether write ok + * @throws IOException IOException + */ + public static Boolean writeFileContent(String path, String content, + String charsetName) throws IOException { + File file = new File(path); + return writeFileContent(file, content, charsetName); + } - /** - * write file content - * - * @param file - * file - * @param content - * content - * @param charsetName - * charsetName - * @return whether write ok - * @throws IOException - * IOException - */ - public static Boolean writeFileContent(File file, String content, - String charsetName) throws IOException { + /** + * write file content + * + * @param file file + * @param content content + * @param charsetName charsetName + * @return whether write ok + * @throws IOException IOException + */ + public static Boolean writeFileContent(File file, String content, + String charsetName) throws IOException { - if (!file.exists() && !file.createNewFile()) { - return false; - } - FileChannel channel = null; - FileLock lock = null; - RandomAccessFile raf = null; - try { - raf = new RandomAccessFile(file, "rw"); - channel = raf.getChannel(); - int i = 0; - do { - try { - lock = channel.tryLock(); - } catch (Exception e) { - ++i; - if (i > RETRY_COUNT) { - log.error("NA","write " + file.getName() + " fail;retryed time: " + i); - throw new IOException("write " + file.getAbsolutePath() - + " conflict", e); - } - sleep(SLEEP_BASETIME * i); - log.warn("write " + file.getName() + " conflict;retry time: " + i); - } - } while (null == lock); + if (!file.exists() && !file.createNewFile()) { + return false; + } + FileChannel channel = null; + FileLock lock = null; + RandomAccessFile raf = null; + try { + raf = new RandomAccessFile(file, "rw"); + channel = raf.getChannel(); + int i = 0; + do { + try { + lock = channel.tryLock(); + } catch (Exception e) { + ++i; + if (i > RETRY_COUNT) { + log.error("NA", "write " + file.getName() + " fail;retryed time: " + i); + throw new IOException("write " + file.getAbsolutePath() + + " conflict", e); + } + sleep(SLEEP_BASETIME * i); + log.warn("write " + file.getName() + " conflict;retry time: " + i); + } + } while (null == lock); - ByteBuffer sendBuffer = ByteBuffer.wrap(content - .getBytes(charsetName)); - while (sendBuffer.hasRemaining()) { - channel.write(sendBuffer); - } - channel.truncate(content.length()); - } catch (FileNotFoundException e) { - throw new IOException("file not exist"); - } finally { - if (lock != null) { - try { - lock.release(); - lock = null; - } catch (IOException e) { - log.warn("close wrong", e); - } - } - if (channel != null) { - try { - channel.close(); - channel = null; - } catch (IOException e) { - log.warn("close wrong", e); - } - } - if (raf != null) { - try { - raf.close(); - raf = null; - } catch (IOException e) { - log.warn("close wrong", e); - } - } + ByteBuffer sendBuffer = ByteBuffer.wrap(content + .getBytes(charsetName)); + while (sendBuffer.hasRemaining()) { + channel.write(sendBuffer); + } + channel.truncate(content.length()); + } catch (FileNotFoundException e) { + throw new IOException("file not exist"); + } finally { + if (lock != null) { + try { + lock.release(); + lock = null; + } catch (IOException e) { + log.warn("close wrong", e); + } + } + if (channel != null) { + try { + channel.close(); + channel = null; + } catch (IOException e) { + log.warn("close wrong", e); + } + } + if (raf != null) { + try { + raf.close(); + raf = null; + } catch (IOException e) { + log.warn("close wrong", e); + } + } - } - return true; - } + } + return true; + } - /** - * transfer ByteBuffer to String - * - * @param buffer - * buffer - * @param charsetName - * charsetName - * @return String - * @throws IOException - * IOException - */ - public static String byteBufferToString(ByteBuffer buffer, - String charsetName) throws IOException { - Charset charset = null; - CharsetDecoder decoder = null; - CharBuffer charBuffer = null; - charset = Charset.forName(charsetName); - decoder = charset.newDecoder(); - charBuffer = decoder.decode(buffer.asReadOnlyBuffer()); - return charBuffer.toString(); - } + /** + * transfer ByteBuffer to String + * + * @param buffer buffer + * @param charsetName charsetName + * @return String + * @throws IOException IOException + */ + public static String byteBufferToString(ByteBuffer buffer, + String charsetName) throws IOException { + Charset charset = null; + CharsetDecoder decoder = null; + CharBuffer charBuffer = null; + charset = Charset.forName(charsetName); + decoder = charset.newDecoder(); + charBuffer = decoder.decode(buffer.asReadOnlyBuffer()); + return charBuffer.toString(); + } - private static void sleep(int time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - log.warn("sleep wrong", e); - } - } + private static void sleep(int time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + log.warn("sleep wrong", e); + } + } - public static void main(String[] args) { - } - - static final public Logger log = LogUtils.LOG; - static final int RETRY_COUNT = 10; - static final int SLEEP_BASETIME = 10; + static final public Logger log = LogUtils.LOG; + static final int RETRY_COUNT = 10; + static final int SLEEP_BASETIME = 10; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/DiskCache.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/DiskCache.java index 5204e9571..396eb86f6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/DiskCache.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/DiskCache.java @@ -25,6 +25,7 @@ import com.alibaba.nacos.client.naming.utils.StringUtils; import java.io.BufferedReader; import java.io.File; import java.io.StringReader; +import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; @@ -41,7 +42,9 @@ public class DiskCache { try { makeSureCacheDirExists(dir); - File file = new File(dir, dom.getKey()); + + + File file = new File(dir, dom.getKeyEncoded()); if (!file.exists()) { // add another !file.exists() to avoid conflicted creating-new-file from multi-instances if (!file.createNewFile() && !file.exists()) { @@ -87,15 +90,19 @@ public class DiskCache { continue; } - if (!(file.getName().endsWith(ServiceInfo.SPLITER + "meta") || file.getName().endsWith(ServiceInfo.SPLITER + "special-url"))) { - ServiceInfo dom = new ServiceInfo(file.getName()); + String fileName = URLDecoder.decode(file.getName(), "UTF-8"); + + if (!(fileName.endsWith(ServiceInfo.SPLITER + "meta") || fileName.endsWith( + ServiceInfo.SPLITER + "special-url"))) { + ServiceInfo dom = new ServiceInfo(fileName); List ips = new ArrayList(); dom.setHosts(ips); ServiceInfo newFormat = null; try { - String dataString = ConcurrentDiskUtil.getFileContent(file, Charset.defaultCharset().toString()); + String dataString = ConcurrentDiskUtil.getFileContent(file, + Charset.defaultCharset().toString()); reader = new BufferedReader(new StringReader(dataString)); String json; @@ -125,7 +132,8 @@ public class DiskCache { //ignore } } - if (newFormat != null && !StringUtils.isEmpty(newFormat.getName()) && !CollectionUtils.isEmpty(newFormat.getHosts())) { + if (newFormat != null && !StringUtils.isEmpty(newFormat.getName()) && !CollectionUtils.isEmpty( + newFormat.getHosts())) { domMap.put(dom.getKey(), newFormat); } else if (!CollectionUtils.isEmpty(dom.getHosts())) { domMap.put(dom.getKey(), dom); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/EventDispatcher.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/EventDispatcher.java index 080f877bf..1d720592e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/EventDispatcher.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/EventDispatcher.java @@ -38,7 +38,8 @@ public class EventDispatcher { private BlockingQueue changedServices = new LinkedBlockingQueue(); - private ConcurrentMap> observerMap = new ConcurrentHashMap>(); + private ConcurrentMap> observerMap + = new ConcurrentHashMap>(); public EventDispatcher() { @@ -124,7 +125,7 @@ public class EventDispatcher { } catch (Exception e) { LogUtils.LOG.error("NA", "notify error for service: " - + serviceInfo.getName() + ", clusters: " + serviceInfo.getClusters(), e); + + serviceInfo.getName() + ", clusters: " + serviceInfo.getClusters(), e); } } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java index 179220fd0..bf8727e9e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/HostReactor.java @@ -43,6 +43,8 @@ public class HostReactor { private Map serviceInfoMap; + private Map updatingMap; + private PushRecver pushRecver; private EventDispatcher eventDispatcher; @@ -53,11 +55,18 @@ public class HostReactor { private String cacheDir; - public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, String cacheDir) { + public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, String cacheDir, + boolean loadCacheAtStart) { this.eventDispatcher = eventDispatcher; this.serverProxy = serverProxy; this.cacheDir = cacheDir; - this.serviceInfoMap = new ConcurrentHashMap(DiskCache.read(this.cacheDir)); + if (loadCacheAtStart) { + this.serviceInfoMap = new ConcurrentHashMap(DiskCache.read(this.cacheDir)); + } else { + this.serviceInfoMap = new ConcurrentHashMap(16); + } + + this.updatingMap = new ConcurrentHashMap(); this.failoverReactor = new FailoverReactor(this, cacheDir); this.pushRecver = new PushRecver(this); } @@ -65,7 +74,7 @@ public class HostReactor { private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "com.vipserver.client.updater"); + Thread thread = new Thread(r, "com.alibaba.nacos.client.naming.updater"); thread.setDaemon(true); return thread; @@ -91,7 +100,7 @@ public class HostReactor { if (oldService != null) { if (oldService.getLastRefTime() > serviceInfo.getLastRefTime()) { LogUtils.LOG.warn("out of date data received, old-t: " + oldService.getLastRefTime() - + ", new-t: " + serviceInfo.getLastRefTime()); + + ", new-t: " + serviceInfo.getLastRefTime()); } serviceInfoMap.put(serviceInfo.getKey(), serviceInfo); @@ -110,11 +119,13 @@ public class HostReactor { Set newHosts = new HashSet(); Set remvHosts = new HashSet(); - List> newServiceHosts = new ArrayList>(newHostMap.entrySet()); + List> newServiceHosts = new ArrayList>( + newHostMap.entrySet()); for (Map.Entry entry : newServiceHosts) { Instance host = entry.getValue(); String key = entry.getKey(); - if (oldHostMap.containsKey(key) && !StringUtils.equals(host.toString(), oldHostMap.get(key).toString())) { + if (oldHostMap.containsKey(key) && !StringUtils.equals(host.toString(), + oldHostMap.get(key).toString())) { modHosts.add(host); continue; } @@ -142,20 +153,19 @@ public class HostReactor { if (newHosts.size() > 0) { LogUtils.LOG.info("new ips(" + newHosts.size() + ") service: " - + serviceInfo.getName() + " -> " + JSON.toJSONString(newHosts)); + + serviceInfo.getName() + " -> " + JSON.toJSONString(newHosts)); } if (remvHosts.size() > 0) { LogUtils.LOG.info("removed ips(" + remvHosts.size() + ") service: " - + serviceInfo.getName() + " -> " + JSON.toJSONString(remvHosts)); + + serviceInfo.getName() + " -> " + JSON.toJSONString(remvHosts)); } if (modHosts.size() > 0) { LogUtils.LOG.info("modified ips(" + modHosts.size() + ") service: " - + serviceInfo.getName() + " -> " + JSON.toJSONString(modHosts)); + + serviceInfo.getName() + " -> " + JSON.toJSONString(modHosts)); } - serviceInfo.setJsonFromServer(json); if (newHosts.size() > 0 || remvHosts.size() > 0 || modHosts.size() > 0) { @@ -164,7 +174,8 @@ public class HostReactor { } } else { - LogUtils.LOG.info("new ips(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getName() + " -> " + JSON.toJSONString(serviceInfo.getHosts())); + LogUtils.LOG.info("new ips(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getName() + " -> " + JSON + .toJSONString(serviceInfo.getHosts())); serviceInfoMap.put(serviceInfo.getKey(), serviceInfo); eventDispatcher.serviceChanged(serviceInfo); serviceInfo.setJsonFromServer(json); @@ -172,7 +183,7 @@ public class HostReactor { } LogUtils.LOG.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getName() + - " -> " + JSON.toJSONString(serviceInfo.getHosts())); + " -> " + JSON.toJSONString(serviceInfo.getHosts())); return serviceInfo; } @@ -199,7 +210,8 @@ public class HostReactor { return getServiceInfo(serviceName, clusters, env, false); } - public ServiceInfo getServiceInfo(final String serviceName, final String clusters, final String env, final boolean allIPs) { + public ServiceInfo getServiceInfo(final String serviceName, final String clusters, final String env, + final boolean allIPs) { LogUtils.LOG.debug("failover-mode: " + failoverReactor.isFailoverSwitch()); String key = ServiceInfo.getKey(serviceName, clusters, env, allIPs); @@ -218,12 +230,16 @@ public class HostReactor { serviceInfoMap.put(serviceObj.getKey(), serviceObj); + updatingMap.put(serviceName, new Object()); + if (allIPs) { updateService4AllIPNow(serviceName, clusters, env); } else { updateServiceNow(serviceName, clusters, env); } - } else if (serviceObj.getHosts().isEmpty()) { + updatingMap.remove(serviceName); + + } else if (updatingMap.containsKey(serviceName)) { if (updateHoldInterval > 0) { // hold a moment waiting for update finish @@ -231,7 +247,8 @@ public class HostReactor { try { serviceObj.wait(updateHoldInterval); } catch (InterruptedException e) { - LogUtils.LOG.error("[getServiceInfo]", "serviceName:" + serviceName + ", clusters:" + clusters + ", allIPs:" + allIPs, e); + LogUtils.LOG.error("[getServiceInfo]", + "serviceName:" + serviceName + ", clusters:" + clusters + ", allIPs:" + allIPs, e); } } } @@ -369,7 +386,6 @@ public class HostReactor { } } - public class UpdateTask implements Runnable { long lastRefTime = Long.MAX_VALUE; private String clusters; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/ProtectMode.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/ProtectMode.java index 5125c9cc0..e6dfc2333 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/ProtectMode.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/ProtectMode.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.client.naming.core; /** - * @author dungu.zpf + * @author nkorange */ public class ProtectMode { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java b/client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java index 0e3013bb6..d6012048f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/core/PushRecver.java @@ -81,24 +81,24 @@ public class PushRecver implements Runnable { // send ack to server ack = "{\"type\": \"push-ack\"" - + ", \"lastRefTime\":\"" + pushPacket.lastRefTime - + "\", \"data\":" + "\"\"}"; + + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"\"}"; } else if ("dump".equals(pushPacket.type)) { // dump data to server ack = "{\"type\": \"dump-ack\"" - + ", \"lastRefTime\": \"" + pushPacket.lastRefTime - + "\", \"data\":" + "\"" - + StringUtils.escapeJavaScript(JSON.toJSONString(hostReactor.getServiceInfoMap())) - + "\"}"; + + ", \"lastRefTime\": \"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"" + + StringUtils.escapeJavaScript(JSON.toJSONString(hostReactor.getServiceInfoMap())) + + "\"}"; } else { // do nothing send ack only ack = "{\"type\": \"unknown-ack\"" - + ", \"lastRefTime\":\"" + pushPacket.lastRefTime - + "\", \"data\":" + "\"\"}"; + + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + + "\", \"data\":" + "\"\"}"; } udpSocket.send(new DatagramPacket(ack.getBytes(Charset.forName("UTF-8")), - ack.getBytes(Charset.forName("UTF-8")).length, packet.getSocketAddress())); + ack.getBytes(Charset.forName("UTF-8")).length, packet.getSocketAddress())); } catch (Exception e) { LogUtils.LOG.error("NA", "error while receiving push data", e); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/net/HttpClient.java b/client/src/main/java/com/alibaba/nacos/client/naming/net/HttpClient.java index b49b559bf..fad93fe3d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/net/HttpClient.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/net/HttpClient.java @@ -31,13 +31,13 @@ import java.util.*; import java.util.zip.GZIPInputStream; /** - * @author dungu.zpf + * @author nkorange */ public class HttpClient { - public static final int TIME_OUT_MILLIS = Integer.parseInt(System.getProperty("com.taobao.vipserver.ctimeout", "50000")); - public static final int CON_TIME_OUT_MILLIS = Integer.parseInt(System.getProperty("com.taobao.vipserver.ctimeout", "3000")); - private static final boolean ENABLE_HTTPS = Boolean.parseBoolean(System.getProperty("tls.enable", "false")); + public static final int TIME_OUT_MILLIS = Integer.getInteger("com.alibaba.nacos.client.naming.ctimeout", 50000); + public static final int CON_TIME_OUT_MILLIS = Integer.getInteger("com.alibaba.nacos.client.naming.ctimeout", 3000); + private static final boolean ENABLE_HTTPS = Boolean.getBoolean("com.alibaba.nacos.client.naming.tls.enable"); static { // limit max redirection @@ -175,12 +175,6 @@ public class HttpClient { return sb.toString(); } - public static void main(String[] args) throws UnsupportedEncodingException { - Map params = new HashMap(2); - params.put("s", "Wms+rkGG8jlaBBbpl8FIDxxNQGA="); - System.out.println(encodingParams(params, "utf-8")); - } - public static class HttpResult { final public int code; final public String content; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java index 7d0471239..aff9176b8 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java @@ -21,8 +21,11 @@ import com.alibaba.fastjson.TypeReference; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.selector.AbstractSelector; +import com.alibaba.nacos.api.selector.SelectorType; +import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.client.naming.utils.*; -import com.alibaba.nacos.common.util.UuidUtil; +import com.alibaba.nacos.common.util.UuidUtils; import java.io.IOException; import java.io.StringReader; @@ -34,7 +37,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** - * @author dungu.zpf + * @author nkorange */ public class NamingProxy { @@ -71,7 +74,7 @@ public class NamingProxy { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); - t.setName("com.taobao.vipserver.serverlist.updater"); + t.setName("com.alibaba.nacos.client.naming.serverlist.updater"); t.setDaemon(true); return t; } @@ -90,17 +93,17 @@ public class NamingProxy { public List getServerListFromEndpoint() { try { - String urlString = "http://" + endpoint + "/vipserver/serverlist"; + String urlString = "http://" + endpoint + "/nacos/serverlist"; List headers = Arrays.asList("Client-Version", UtilAndComs.VERSION, "Accept-Encoding", "gzip,deflate,sdch", "Connection", "Keep-Alive", - "RequestId", UuidUtil.generateUuid()); + "RequestId", UuidUtils.generateUuid()); HttpClient.HttpResult result = HttpClient.httpGet(urlString, headers, null, UtilAndComs.ENCODING); if (HttpURLConnection.HTTP_OK != result.code) { throw new IOException("Error while requesting: " + urlString + "'. Server returned: " - + result.code); + + result.code); } String content = result.content; @@ -134,8 +137,8 @@ public class NamingProxy { List list = getServerListFromEndpoint(); - if (list.isEmpty()) { - throw new Exception("Can not acquire vipserver list"); + if (CollectionUtils.isEmpty(list)) { + throw new Exception("Can not acquire Nacos list"); } if (!CollectionUtils.isEqualCollection(list, serversFromEndpoint)) { @@ -158,14 +161,11 @@ public class NamingProxy { params.put("ip", instance.getIp()); params.put("port", String.valueOf(instance.getPort())); params.put("weight", String.valueOf(instance.getWeight())); + params.put("enable", String.valueOf(instance.isEnabled())); params.put("healthy", String.valueOf(instance.isHealthy())); params.put("metadata", JSON.toJSONString(instance.getMetadata())); - if (instance.getService() == null) { - params.put("serviceName", serviceName); - } else { - params.put("service", JSON.toJSONString(instance.getService())); - } - params.put("cluster", JSON.toJSONString(instance.getCluster())); + params.put("serviceName", serviceName); + params.put("clusterName", instance.getClusterName()); reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, "PUT"); } @@ -173,7 +173,7 @@ public class NamingProxy { public void deregisterService(String serviceName, String ip, int port, String cluster) throws NacosException { LogUtils.LOG.info("DEREGISTER-SERVICE", "deregistering service " + serviceName - + " with instance:" + ip + ":" + port + "@" + cluster); + + " with instance:" + ip + ":" + port + "@" + cluster); final Map params = new HashMap(8); params.put("tenant", namespace); @@ -196,11 +196,6 @@ public class NamingProxy { return reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/list", params, "GET"); } - private String doRegDom(Map params) throws Exception { - String api = UtilAndComs.NACOS_URL_BASE + "/api/regService"; - return reqAPI(api, params); - } - public boolean serverHealthy() { try { @@ -213,11 +208,28 @@ public class NamingProxy { } public ListView getServiceList(int pageNo, int pageSize) throws NacosException { + return getServiceList(pageNo, pageSize, null); + } + + public ListView getServiceList(int pageNo, int pageSize, AbstractSelector selector) throws NacosException { Map params = new HashMap(4); params.put("pageNo", String.valueOf(pageNo)); params.put("pageSize", String.valueOf(pageSize)); + if (selector != null) { + switch (SelectorType.valueOf(selector.getType())) { + case none: + break; + case label: + ExpressionSelector expressionSelector = (ExpressionSelector) selector; + params.put("selector", JSON.toJSONString(expressionSelector)); + break; + default: + break; + } + } + String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/service/list", params); JSONObject json = JSON.parseObject(result); @@ -275,12 +287,13 @@ public class NamingProxy { return callServer(api, params, curServer, "GET"); } - public String callServer(String api, Map params, String curServer, String method) throws NacosException { + public String callServer(String api, Map params, String curServer, String method) + throws NacosException { List headers = Arrays.asList("Client-Version", UtilAndComs.VERSION, - "Accept-Encoding", "gzip,deflate,sdch", - "Connection", "Keep-Alive", - "RequestId", UuidUtil.generateUuid()); + "Accept-Encoding", "gzip,deflate,sdch", + "Connection", "Keep-Alive", + "RequestId", UuidUtils.generateUuid()); String url; @@ -301,12 +314,12 @@ public class NamingProxy { } LogUtils.LOG.error("CALL-SERVER", "failed to req API:" + HttpClient.getPrefix() + curServer - + api + ". code:" - + result.code + " msg: " + result.content); + + api + ". code:" + + result.code + " msg: " + result.content); throw new NacosException(NacosException.SERVER_ERROR, "failed to req API:" + HttpClient.getPrefix() + curServer - + api + ". code:" - + result.code + " msg: " + result.content); + + api + ". code:" + + result.code + " msg: " + result.content); } public String reqAPI(String api, Map params, List servers) { @@ -338,7 +351,6 @@ public class NamingProxy { throw new IllegalStateException("failed to req API:" + api + " after all servers(" + servers + ") tried"); } - for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) { try { return callServer(api, params, nacosDomain); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/Chooser.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/Chooser.java index 3cc4a68c1..f3d08bc27 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/Chooser.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/Chooser.java @@ -20,7 +20,7 @@ import java.util.Arrays; import java.util.List; /** - * @author dungu.zpf + * @author nkorange */ public class Chooser { @@ -72,7 +72,6 @@ public class Chooser { this.ref = ref; } - public K getUniqueKey() { return uniqueKey; } @@ -100,7 +99,7 @@ public class Chooser { } public void refresh() { - Double originWeightSum = (double) 0; + Double originWeightSum = (double)0; for (Pair item : itemsWithWeight) { @@ -140,7 +139,8 @@ public class Chooser { double doublePrecisionDelta = 0.0001; if (index != 0 && !(Math.abs(weights[index - 1] - 1) < doublePrecisionDelta)) { - throw new IllegalStateException("Cumulative Weight caculate wrong , the sum of probabilities does not equals 1."); + throw new IllegalStateException( + "Cumulative Weight caculate wrong , the sum of probabilities does not equals 1."); } } @@ -164,7 +164,7 @@ public class Chooser { if (!(other.getClass().getGenericInterfaces()[0].equals(this.getClass().getGenericInterfaces()[0]))) { return false; } - Ref otherRef = (Ref) other; + Ref otherRef = (Ref)other; if (itemsWithWeight == null) { if (otherRef.itemsWithWeight != null) { return false; @@ -197,7 +197,7 @@ public class Chooser { return false; } - Chooser otherChooser = (Chooser) other; + Chooser otherChooser = (Chooser)other; if (this.uniqueKey == null) { if (otherChooser.getUniqueKey() != null) { return false; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/CollectionUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/CollectionUtils.java index 438013664..55a601ef6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/CollectionUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/CollectionUtils.java @@ -1,23 +1,3 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.client.naming.utils; - -/** - * Created by harold on 2015/12/7. - */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -34,15 +14,17 @@ package com.alibaba.nacos.client.naming.utils; * See the License for the specific language governing permissions and * limitations under the License. */ +package com.alibaba.nacos.client.naming.utils; + +/** + * Created by harold on 2015/12/7. + */ import java.util.*; /** * Provides utility methods and decorators for {@link Collection} instances. * - * @since Commons Collections 1.0 - * @version $Revision: 1713167 $ $Date: 2015-11-07 20:44:03 +0100 (Sat, 07 Nov 2015) $ - * * @author Rodney Waldhoff * @author Paul Jack * @author Stephen Colebourne @@ -56,10 +38,14 @@ import java.util.*; * @author Jon Schewe * @author Neil O'Toole * @author Stephen Smith + * @version $Revision: 1713167 $ $Date: 2015-11-07 20:44:03 +0100 (Sat, 07 Nov 2015) $ + * @since Commons Collections 1.0 */ public class CollectionUtils { - /** Constant to avoid repeated object creation */ + /** + * Constant to avoid repeated object creation + */ private static Integer INTEGER_ONE = 1; /** @@ -69,75 +55,70 @@ public class CollectionUtils { } /** - * Returns a new {@link Collection} containing a - b. - * The cardinality of each element e in the returned {@link Collection} - * will be the cardinality of e in a minus the cardinality + * Returns a new {@link Collection} containing a - b. The cardinality of each element + * e in the returned {@link Collection} will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * - * @param a the collection to subtract from, must not be null - * @param b the collection to subtract, must not be null + * @param a the collection to subtract from, must not be null + * @param b the collection to subtract, must not be null * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(final Collection a, final Collection b) { - ArrayList list = new ArrayList( a ); - for (Iterator it = b.iterator(); it.hasNext();) { + ArrayList list = new ArrayList(a); + for (Iterator it = b.iterator(); it.hasNext(); ) { list.remove(it.next()); } return list; } /** - * Returns a {@link Map} mapping each unique element in the given - * {@link Collection} to an {@link Integer} representing the number - * of occurrences of that element in the {@link Collection}. + * Returns a {@link Map} mapping each unique element in the given {@link Collection} to an {@link Integer} + * representing the number of occurrences of that element in the {@link Collection}. *

- * Only those elements present in the collection will appear as - * keys in the map. + * Only those elements present in the collection will appear as keys in the map. * - * @param coll the collection to get the cardinality map for, must not be null + * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Collection coll) { Map count = new HashMap(coll.size()); - for (Iterator it = coll.iterator(); it.hasNext();) { + for (Iterator it = coll.iterator(); it.hasNext(); ) { Object obj = it.next(); - Integer c = (Integer) (count.get(obj)); + Integer c = (Integer)(count.get(obj)); if (c == null) { - count.put(obj,INTEGER_ONE); + count.put(obj, INTEGER_ONE); } else { - count.put(obj,c + 1); + count.put(obj, c + 1); } } return count; } - /** - * Returns true iff the given {@link Collection}s contain - * exactly the same elements with exactly the same cardinalities. + * Returns true iff the given {@link Collection}s contain exactly the same elements with exactly the same + * cardinalities. *

- * That is, iff the cardinality of e in a is - * equal to the cardinality of e in b, - * for each element e in a or b. + * That is, iff the cardinality of e in a is equal to the cardinality of e in b, for + * each element e in a or b. * - * @param a the first collection, must not be null - * @param b the second collection, must not be null + * @param a the first collection, must not be null + * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { - if(a.size() != b.size()) { + if (a.size() != b.size()) { return false; } else { Map mapa = getCardinalityMap(a); Map mapb = getCardinalityMap(b); - if(mapa.size() != mapb.size()) { + if (mapa.size() != mapb.size()) { return false; } else { Iterator it = mapa.keySet().iterator(); - while(it.hasNext()) { + while (it.hasNext()) { Object obj = it.next(); - if(getFreq(obj,mapa) != getFreq(obj,mapb)) { + if (getFreq(obj, mapa) != getFreq(obj, mapb)) { return false; } } @@ -147,12 +128,13 @@ public class CollectionUtils { } //----------------------------------------------------------------------- + /** * Null-safe check if the specified collection is empty. *

* Null returns true. * - * @param coll the collection to check, may be null + * @param coll the collection to check, may be null * @return true if empty or null * @since Commons Collections 3.2 */ @@ -160,9 +142,8 @@ public class CollectionUtils { return (coll == null || coll.isEmpty()); } - private static final int getFreq(final Object obj, final Map freqMap) { - Integer count = (Integer) freqMap.get(obj); + Integer count = (Integer)freqMap.get(obj); if (count != null) { return count.intValue(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/GenericPoller.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/GenericPoller.java index b0fa0438f..d707049e6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/GenericPoller.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/GenericPoller.java @@ -20,14 +20,14 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** - * @author dungu.zpf + * @author nkorange */ public class GenericPoller implements Poller { private AtomicInteger index = new AtomicInteger(0); private List items = new ArrayList(); - public GenericPoller(List items){ + public GenericPoller(List items) { this.items = items; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/IoUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/IoUtils.java index 1bb980277..a71a413da 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/IoUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/IoUtils.java @@ -24,13 +24,13 @@ import java.util.List; import java.util.zip.GZIPInputStream; /** - * @author dungu.zpf + * @author nkorange */ public class IoUtils { static public String toString(InputStream input, String encoding) throws IOException { return (null == encoding) ? toString(new InputStreamReader(input, "UTF-8")) - : toString(new InputStreamReader(input, encoding)); + : toString(new InputStreamReader(input, encoding)); } static public String toString(Reader reader) throws IOException { @@ -62,7 +62,6 @@ public class IoUtils { return totalBytes; } - static public List readLines(Reader input) throws IOException { BufferedReader reader = toBufferedReader(input); List list = new ArrayList(); @@ -79,11 +78,10 @@ public class IoUtils { } static private BufferedReader toBufferedReader(Reader reader) { - return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader( - reader); + return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader( + reader); } - static public void copyFile(String source, String target) throws IOException { File sf = new File(source); if (!sf.exists()) { @@ -127,7 +125,6 @@ public class IoUtils { } } - public static void cleanDirectory(File directory) throws IOException { if (!directory.exists()) { String message = directory + " does not exist"; @@ -169,16 +166,15 @@ public class IoUtils { return GZIPInputStream.GZIP_MAGIC == ((bytes[1] << 8 | bytes[0]) & 0xFFFF); } - public static byte[] tryDecompress(byte[] raw) throws Exception { if (!isGzipStream(raw)) { return raw; } GZIPInputStream gis - = new GZIPInputStream(new ByteArrayInputStream(raw)); + = new GZIPInputStream(new ByteArrayInputStream(raw)); ByteArrayOutputStream out - = new ByteArrayOutputStream(); + = new ByteArrayOutputStream(); IoUtils.copy(gis, out); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/JvmRandom.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/JvmRandom.java index 71a8b578a..b9c9092a1 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/JvmRandom.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/JvmRandom.java @@ -1,18 +1,3 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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. - */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -36,22 +21,18 @@ package com.alibaba.nacos.client.naming.utils; import java.util.Random; /** - *

JVMRandom is a wrapper that supports all possible - * Random methods via the {@link java.lang.Math#random()} method - * and its system-wide {@link Random} object.

+ *

JVMRandom is a wrapper that supports all possible Random methods via the {@link + * java.lang.Math#random()} method and its system-wide {@link Random} object.

*

- * It does this to allow for a Random class in which the seed is - * shared between all members of the class - a better name would - * have been SharedSeedRandom. + * It does this to allow for a Random class in which the seed is shared between all members of the class - a better name + * would have been SharedSeedRandom. *

- * N.B. the current implementation overrides the methods - * {@link Random#nextInt(int)} and {@link Random#nextLong()} - * to produce positive numbers ranging from 0 (inclusive) - * to MAX_VALUE (exclusive). + * N.B. the current implementation overrides the methods {@link Random#nextInt(int)} and {@link + * Random#nextLong()} to produce positive numbers ranging from 0 (inclusive) to MAX_VALUE (exclusive). * - * @since 2.0 - * @version $Id: JVMRandom.java 911986 2010-02-19 21:19:05Z niallp $ * @author unknown + * @version $Id: JVMRandom.java 911986 2010-02-19 21:19:05Z niallp $ + * @since 2.0 */ public final class JvmRandom extends Random { @@ -109,12 +90,9 @@ public final class JvmRandom extends Random { } /** - *

Returns the next pseudorandom, uniformly distributed int value - * from the Math.random() sequence.

- * Identical to nextInt(Integer.MAX_VALUE) - *

- * N.B. All values are >= 0. - *

+ *

Returns the next pseudorandom, uniformly distributed int value from the Math.random() sequence.

Identical + * to nextInt(Integer.MAX_VALUE)

N.B. All values are >= 0.

+ * * @return the random int */ public int nextInt() { @@ -122,11 +100,10 @@ public final class JvmRandom extends Random { } /** - *

Returns a pseudorandom, uniformly distributed int value between - * 0 (inclusive) and the specified value (exclusive), from - * the Math.random() sequence.

+ *

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified + * value (exclusive), from the Math.random() sequence.

* - * @param n the specified exclusive max-value + * @param n the specified exclusive max-value * @return the random int * @throws IllegalArgumentException when n <= 0 */ @@ -135,32 +112,27 @@ public final class JvmRandom extends Random { } /** - *

Returns the next pseudorandom, uniformly distributed long value - * from the Math.random() sequence.

- * Identical to nextLong(Long.MAX_VALUE) - *

- * N.B. All values are >= 0. - *

+ *

Returns the next pseudorandom, uniformly distributed long value from the Math.random() sequence.

Identical + * to nextLong(Long.MAX_VALUE)

N.B. All values are >= 0.

+ * * @return the random long */ public long nextLong() { return nextLong(Long.MAX_VALUE); } - /** - *

Returns a pseudorandom, uniformly distributed long value between - * 0 (inclusive) and the specified value (exclusive), from - * the Math.random() sequence.

+ *

Returns a pseudorandom, uniformly distributed long value between 0 (inclusive) and the specified + * value (exclusive), from the Math.random() sequence.

* - * @param n the specified exclusive max-value + * @param n the specified exclusive max-value * @return the random long * @throws IllegalArgumentException when n <= 0 */ public static long nextLong(long n) { if (n <= 0) { throw new IllegalArgumentException( - "Upper bound for nextInt must be positive" + "Upper bound for nextInt must be positive" ); } // Code adapted from Harmony Random#nextInt(int) @@ -168,7 +140,7 @@ public final class JvmRandom extends Random { if ((n & -n) == n) { // dropping lower order bits improves behaviour for low values of n return next63bits() >> 63 - - bitsRequired(n-1); + - bitsRequired(n - 1); } // Not a power of two long val; @@ -182,8 +154,7 @@ public final class JvmRandom extends Random { } /** - *

Returns the next pseudorandom, uniformly distributed boolean value - * from the Math.random() sequence.

+ *

Returns the next pseudorandom, uniformly distributed boolean value from the Math.random() sequence.

* * @return the random boolean */ @@ -192,9 +163,8 @@ public final class JvmRandom extends Random { } /** - *

Returns the next pseudorandom, uniformly distributed float value - * between 0.0 and 1.0 from the Math.random() - * sequence.

+ *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the Math.random() sequence.

* * @return the random float */ @@ -213,9 +183,10 @@ public final class JvmRandom extends Random { /** * Get the next unsigned random long + * * @return unsigned random long */ - private static long next63bits(){ + private static long next63bits() { // drop the sign bit to leave 63 random bits return SHARED_RANDOM.nextLong() & 0x7fffffffffffffffL; } @@ -226,21 +197,21 @@ public final class JvmRandom extends Random { * @param num long number * @return number of bits required */ - private static int bitsRequired(long num){ + private static int bitsRequired(long num) { // Derived from Hacker's Delight, Figure 5-9 - long y=num; - int n=0; - while(true){ + long y = num; + int n = 0; + while (true) { // 64 = number of bits in a long if (num < 0) { - return 64-n; + return 64 - n; } if (y == 0) { return n; } n++; - num=num << 1; - y=y >> 1; + num = num << 1; + y = y >> 1; } } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/LogUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/LogUtils.java index bd7543383..d6b506d3f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/LogUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/LogUtils.java @@ -42,7 +42,7 @@ public class LogUtils { // logger init LOG = LoggerFactory.getLogger("com.alibaba.nacos.client.naming"); - LOG.setLevel(Level.INFO); + LOG.setLevel(Level.DEBUG); LOG.setAdditivity(false); LOG.activateAppenderWithSizeRolling("nacos", "naming.log", "UTF-8", JM_LOG_FILE_SIZE, JM_LOG_RETAIN_COUNT); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/NetUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/NetUtils.java index 8a0775843..ddc449e8b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/NetUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/NetUtils.java @@ -27,10 +27,10 @@ public class NetUtils { public static String localIP() { try { if (!StringUtils.isEmpty(LOCAL_IP)) { - return LOCAL_IP; + return LOCAL_IP; } - String ip = System.getProperty("com.taobao.vipserver.localIP", InetAddress.getLocalHost().getHostAddress()); + String ip = System.getProperty("com.alibaba.nacos.client.naming.local.ip", InetAddress.getLocalHost().getHostAddress()); return LOCAL_IP = ip; } catch (UnknownHostException e) { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/Pair.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/Pair.java index d6616d9f3..657be7fca 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/Pair.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/Pair.java @@ -16,23 +16,23 @@ package com.alibaba.nacos.client.naming.utils; /** - * @author dungu.zpf + * @author nkorange */ public class Pair { private T item; private double weight; - public Pair(T item,double weight){ + public Pair(T item, double weight) { this.item = item; this.weight = weight; } - public T item(){ + public T item() { return item; } - public double weight(){ + public double weight() { return weight; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/Poller.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/Poller.java index 242e553f1..67d39b6ab 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/Poller.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/Poller.java @@ -18,7 +18,7 @@ package com.alibaba.nacos.client.naming.utils; import java.util.List; /** - * @author dungu.zpf + * @author nkorange */ public interface Poller { /** diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/RandomUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/RandomUtils.java index 443225749..19c84a23f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/RandomUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/RandomUtils.java @@ -1,18 +1,3 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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. - */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -36,13 +21,12 @@ package com.alibaba.nacos.client.naming.utils; import java.util.Random; /** - *

RandomUtils is a wrapper that supports all possible - * {@link java.util.Random} methods via the {@link java.lang.Math#random()} - * method and its system-wide Random object. + *

RandomUtils is a wrapper that supports all possible {@link java.util.Random} methods via the {@link + * java.lang.Math#random()} method and its system-wide Random object. * * @author Gary D. Gregory - * @since 2.0 * @version $Id: RandomUtils.java 906320 2010-02-04 01:41:10Z sebb $ + * @since 2.0 */ public class RandomUtils { @@ -51,15 +35,15 @@ public class RandomUtils { */ public static final Random JVM_RANDOM = new JvmRandom(); -// should be possible for JVM_RANDOM? -// public static void nextBytes(byte[]) { -// public synchronized double nextGaussian(); -// } + // should be possible for JVM_RANDOM? + // public static void nextBytes(byte[]) { + // public synchronized double nextGaussian(); + // } /** - *

Returns the next pseudorandom, uniformly distributed int value - * from the Math.random() sequence.

- * N.B. All values are >= 0. + *

Returns the next pseudorandom, uniformly distributed int value from the Math.random() sequence.

N.B. + * All values are >= 0. + * * @return the random int */ public static int nextInt() { @@ -67,8 +51,8 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed int value - * from the given random sequence.

+ *

Returns the next pseudorandom, uniformly distributed int value from the given random + * sequence.

* * @param random the Random sequence generator. * @return the random int @@ -78,11 +62,10 @@ public class RandomUtils { } /** - *

Returns a pseudorandom, uniformly distributed int value - * between 0 (inclusive) and the specified value - * (exclusive), from the Math.random() sequence.

+ *

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified + * value (exclusive), from the Math.random() sequence.

* - * @param n the specified exclusive max-value + * @param n the specified exclusive max-value * @return the random int */ public static int nextInt(int n) { @@ -90,12 +73,11 @@ public class RandomUtils { } /** - *

Returns a pseudorandom, uniformly distributed int value - * between 0 (inclusive) and the specified value - * (exclusive), from the given Random sequence.

+ *

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified + * value (exclusive), from the given Random sequence.

* * @param random the Random sequence generator. - * @param n the specified exclusive max-value + * @param n the specified exclusive max-value * @return the random int */ public static int nextInt(Random random, int n) { @@ -104,9 +86,9 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed long value - * from the Math.random() sequence.

- * N.B. All values are >= 0. + *

Returns the next pseudorandom, uniformly distributed long value from the Math.random() sequence.

N.B. + * All values are >= 0. + * * @return the random long */ public static long nextLong() { @@ -114,8 +96,7 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed long value - * from the given Random sequence.

+ *

Returns the next pseudorandom, uniformly distributed long value from the given Random sequence.

* * @param random the Random sequence generator. * @return the random long @@ -125,8 +106,7 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed boolean value - * from the Math.random() sequence.

+ *

Returns the next pseudorandom, uniformly distributed boolean value from the Math.random() sequence.

* * @return the random boolean */ @@ -135,8 +115,7 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed boolean value - * from the given random sequence.

+ *

Returns the next pseudorandom, uniformly distributed boolean value from the given random sequence.

* * @param random the Random sequence generator. * @return the random boolean @@ -146,9 +125,8 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed float value - * between 0.0 and 1.0 from the Math.random() - * sequence.

+ *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the Math.random() sequence.

* * @return the random float */ @@ -157,9 +135,8 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed float value - * between 0.0 and 1.0 from the given Random - * sequence.

+ *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the given Random sequence.

* * @param random the Random sequence generator. * @return the random float @@ -169,9 +146,8 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed float value - * between 0.0 and 1.0 from the Math.random() - * sequence.

+ *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the Math.random() sequence.

* * @return the random double */ @@ -180,9 +156,8 @@ public class RandomUtils { } /** - *

Returns the next pseudorandom, uniformly distributed float value - * between 0.0 and 1.0 from the given Random - * sequence.

+ *

Returns the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + * from the given Random sequence.

* * @param random the Random sequence generator. * @return the random double diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/StringUtils.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/StringUtils.java index 59df2cd74..1fd834238 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/StringUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/StringUtils.java @@ -22,7 +22,7 @@ import java.util.Collection; import java.util.Locale; /** - * @author dungu.zpf + * @author nkorange */ public class StringUtils { public static boolean isEmpty(String str) { @@ -80,7 +80,6 @@ public class StringUtils { return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH); } - private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote, boolean escapeForwardSlash) throws IOException { if (out == null) { @@ -103,27 +102,27 @@ public class StringUtils { out.write("\\u00" + hex(ch)); } else if (ch < 32) { switch (ch) { - case '\b' : + case '\b': out.write('\\'); out.write('b'); break; - case '\n' : + case '\n': out.write('\\'); out.write('n'); break; - case '\t' : + case '\t': out.write('\\'); out.write('t'); break; - case '\f' : + case '\f': out.write('\\'); out.write('f'); break; - case '\r' : + case '\r': out.write('\\'); out.write('r'); break; - default : + default: if (ch > 0xf) { out.write("\\u00" + hex(ch)); } else { @@ -133,27 +132,27 @@ public class StringUtils { } } else { switch (ch) { - case '\'' : + case '\'': if (escapeSingleQuote) { out.write('\\'); } out.write('\''); break; - case '"' : + case '"': out.write('\\'); out.write('"'); break; - case '\\' : + case '\\': out.write('\\'); out.write('\\'); break; - case '/' : + case '/': if (escapeForwardSlash) { out.write('\\'); } out.write('/'); break; - default : + default: out.write(ch); break; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/utils/ThreadLocalRandom.java b/client/src/main/java/com/alibaba/nacos/client/naming/utils/ThreadLocalRandom.java index ee41f3ee7..aec6d42a0 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/utils/ThreadLocalRandom.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/utils/ThreadLocalRandom.java @@ -26,18 +26,18 @@ import java.util.concurrent.atomic.AtomicLong; * A random number generator isolated to the current thread. Like the global {@link java.util.Random} generator used by * the {@link java.lang.Math} class, a {@code ThreadLocalRandom} is initialized with an internally generated seed that * may not otherwise be modified. When applicable, use of {@code ThreadLocalRandom} rather than shared {@code Random} - * objects in concurrent programs will typically encounter much less overhead and contention. Use of - * {@code ThreadLocalRandom} is particularly appropriate when multiple tasks (for example, each a - * {@link io.netty.util.internal.chmv8.ForkJoinTask}) use random numbers in parallel in thread pools. - * + * objects in concurrent programs will typically encounter much less overhead and contention. Use of {@code + * ThreadLocalRandom} is particularly appropriate when multiple tasks (for example, each a {@link + * io.netty.util.internal.chmv8.ForkJoinTask}) use random numbers in parallel in thread pools. + *

*

* Usages of this class should typically be of the form: {@code ThreadLocalRandom.current().nextX(...)} (where {@code X} * is {@code Int}, {@code Long}, etc). When all usages are of this form, it is never possible to accidently share a * {@code ThreadLocalRandom} across multiple threads. - * + *

*

* This class also provides additional commonly used bounded random generation methods. - * + *

* //since 1.7 //author Doug Lea */ @SuppressWarnings("all") @@ -71,7 +71,7 @@ public class ThreadLocalRandom extends Random { // Get the random seed from the thread with timeout. final long timeoutSeconds = 3; final long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds); - for (;;) { + for (; ; ) { long waitTime = deadLine - System.nanoTime(); if (waitTime <= 0) { break; @@ -99,7 +99,7 @@ public class ThreadLocalRandom extends Random { } private static long newSeed() { - for (;;) { + for (; ; ) { final long current = seedUniquifier.get(); final long actualCurrent = current != 0 ? current : getInitialSeedUniquifier(); @@ -163,8 +163,7 @@ public class ThreadLocalRandom extends Random { /** * Throws {@code UnsupportedOperationException}. Setting seeds in this generator is not supported. * - * @throws UnsupportedOperationException - * always + * @throws UnsupportedOperationException always */ public void setSeed(long seed) { if (initialized) { @@ -175,20 +174,17 @@ public class ThreadLocalRandom extends Random { protected int next(int bits) { rnd = (rnd * multiplier + addend) & mask; - return (int) (rnd >>> (48 - bits)); + return (int)(rnd >>> (48 - bits)); } /** * Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound * (exclusive). * - * @param least - * the least value returned - * @param bound - * the upper bound (exclusive) - * @throws IllegalArgumentException - * if least greater than or equal to bound + * @param least the least value returned + * @param bound the upper bound (exclusive) * @return the next value + * @throws IllegalArgumentException if least greater than or equal to bound */ public int nextInt(int least, int bound) { if (least >= bound) { @@ -200,11 +196,9 @@ public class ThreadLocalRandom extends Random { /** * Returns a pseudorandom, uniformly distributed value between 0 (inclusive) and the specified value (exclusive). * - * @param n - * the bound on the random number to be returned. Must be positive. + * @param n the bound on the random number to be returned. Must be positive. * @return the next value - * @throws IllegalArgumentException - * if n is not positive + * @throws IllegalArgumentException if n is not positive */ public long nextLong(long n) { if (n <= 0) { @@ -226,20 +220,17 @@ public class ThreadLocalRandom extends Random { } n = nextn; } - return offset + nextInt((int) n); + return offset + nextInt((int)n); } /** * Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound * (exclusive). * - * @param least - * the least value returned - * @param bound - * the upper bound (exclusive) + * @param least the least value returned + * @param bound the upper bound (exclusive) * @return the next value - * @throws IllegalArgumentException - * if least greater than or equal to bound + * @throws IllegalArgumentException if least greater than or equal to bound */ public long nextLong(long least, long bound) { if (least >= bound) { @@ -252,11 +243,9 @@ public class ThreadLocalRandom extends Random { * Returns a pseudorandom, uniformly distributed {@code double} value between 0 (inclusive) and the specified value * (exclusive). * - * @param n - * the bound on the random number to be returned. Must be positive. + * @param n the bound on the random number to be returned. Must be positive. * @return the next value - * @throws IllegalArgumentException - * if n is not positive + * @throws IllegalArgumentException if n is not positive */ public double nextDouble(double n) { if (n <= 0) { @@ -269,13 +258,10 @@ public class ThreadLocalRandom extends Random { * Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) and bound * (exclusive). * - * @param least - * the least value returned - * @param bound - * the upper bound (exclusive) + * @param least the least value returned + * @param bound the upper bound (exclusive) * @return the next value - * @throws IllegalArgumentException - * if least greater than or equal to bound + * @throws IllegalArgumentException if least greater than or equal to bound */ public double nextDouble(double least, double bound) { if (least >= bound) { diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/AppNameUtils.java b/client/src/main/java/com/alibaba/nacos/client/utils/AppNameUtils.java index deec47c82..1da7563dc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/AppNameUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/AppNameUtils.java @@ -19,8 +19,8 @@ import java.io.File; /** * appName util - * @author Nacos * + * @author Nacos */ public class AppNameUtils { @@ -34,38 +34,34 @@ public class AppNameUtils { private static final String SERVER_JETTY = "jetty"; private static final String SERVER_TOMCAT = "tomcat"; private static final String SERVER_UNKNOWN = "unknown server"; - - public static String getAppName() { - String appName = null; - appName = getAppNameByProjectName(); - if (appName != null) { - return appName; - } + public static String getAppName() { + String appName = null; - appName = getAppNameByServerHome(); - if (appName != null) { - return appName; - } + appName = getAppNameByProjectName(); + if (appName != null) { + return appName; + } - return "unknown"; - } + appName = getAppNameByServerHome(); + if (appName != null) { + return appName; + } + return "unknown"; + } private static String getAppNameByProjectName() { return System.getProperty(PARAM_MARKING_PROJECT); } - private static String getAppNameByServerHome() { String serverHome = null; if (SERVER_JBOSS.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_JBOSS); - } - else if (SERVER_JETTY.equals(getServerType())) { + } else if (SERVER_JETTY.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_JETTY); - } - else if (SERVER_TOMCAT.equals(getServerType())) { + } else if (SERVER_TOMCAT.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_TOMCAT); } @@ -80,14 +76,11 @@ public class AppNameUtils { String serverType = null; if (System.getProperty(PARAM_MARKING_JBOSS) != null) { serverType = SERVER_JBOSS; - } - else if (System.getProperty(PARAM_MARKING_JETTY) != null) { + } else if (System.getProperty(PARAM_MARKING_JETTY) != null) { serverType = SERVER_JETTY; - } - else if (System.getProperty(PARAM_MARKING_TOMCAT) != null) { + } else if (System.getProperty(PARAM_MARKING_TOMCAT) != null) { serverType = SERVER_TOMCAT; - } - else { + } else { serverType = SERVER_UNKNOWN; } return serverType; diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/EnvUtil.java b/client/src/main/java/com/alibaba/nacos/client/utils/EnvUtil.java index 51f54af3d..f6929d6d7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/EnvUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/EnvUtil.java @@ -23,93 +23,92 @@ import com.alibaba.nacos.client.logger.Logger; /** * env util. - * - * @author Nacos * + * @author Nacos */ public class EnvUtil { - final static public Logger log = LogUtils.logger(EnvUtil.class); - - public static void setSelfEnv(Map> headers) { - if (headers != null) { - List amorayTagTmp = headers.get(AMORY_TAG); - if (amorayTagTmp == null) { - if (selfAmorayTag != null) { - selfAmorayTag = null; - log.warn("selfAmoryTag:null"); - } - } else { - String amorayTagTmpStr = listToString(amorayTagTmp); - if (!amorayTagTmpStr.equals(selfAmorayTag)) { - selfAmorayTag = amorayTagTmpStr; - log.warn("selfAmoryTag:{}", selfAmorayTag); - } - } + final static public Logger log = LogUtils.logger(EnvUtil.class); - List vipserverTagTmp = headers.get(VIPSERVER_TAG); - if (vipserverTagTmp == null) { - if (selfVipserverTag != null) { - selfVipserverTag = null; - log.warn("selfVipserverTag:null"); - } - } else { - String vipserverTagTmpStr = listToString(vipserverTagTmp); - if (!vipserverTagTmpStr.equals(selfVipserverTag)) { - selfVipserverTag = vipserverTagTmpStr; - log.warn("selfVipserverTag:{}", selfVipserverTag); - } - } - List locationTagTmp = headers.get(LOCATION_TAG); - if (locationTagTmp == null) { - if (selfLocationTag != null) { - selfLocationTag = null; - log.warn("selfLocationTag:null"); - } - } else { - String locationTagTmpStr = listToString(locationTagTmp); - if (!locationTagTmpStr.equals(selfLocationTag)) { - selfLocationTag = locationTagTmpStr; - log.warn("selfLocationTag:{}", selfLocationTag); - } - } - } - } + public static void setSelfEnv(Map> headers) { + if (headers != null) { + List amorayTagTmp = headers.get(AMORY_TAG); + if (amorayTagTmp == null) { + if (selfAmorayTag != null) { + selfAmorayTag = null; + log.warn("selfAmoryTag:null"); + } + } else { + String amorayTagTmpStr = listToString(amorayTagTmp); + if (!amorayTagTmpStr.equals(selfAmorayTag)) { + selfAmorayTag = amorayTagTmpStr; + log.warn("selfAmoryTag:{}", selfAmorayTag); + } + } - public static String getSelfAmorayTag() { - return selfAmorayTag; - } + List vipserverTagTmp = headers.get(VIPSERVER_TAG); + if (vipserverTagTmp == null) { + if (selfVipserverTag != null) { + selfVipserverTag = null; + log.warn("selfVipserverTag:null"); + } + } else { + String vipserverTagTmpStr = listToString(vipserverTagTmp); + if (!vipserverTagTmpStr.equals(selfVipserverTag)) { + selfVipserverTag = vipserverTagTmpStr; + log.warn("selfVipserverTag:{}", selfVipserverTag); + } + } + List locationTagTmp = headers.get(LOCATION_TAG); + if (locationTagTmp == null) { + if (selfLocationTag != null) { + selfLocationTag = null; + log.warn("selfLocationTag:null"); + } + } else { + String locationTagTmpStr = listToString(locationTagTmp); + if (!locationTagTmpStr.equals(selfLocationTag)) { + selfLocationTag = locationTagTmpStr; + log.warn("selfLocationTag:{}", selfLocationTag); + } + } + } + } - public static String getSelfVipserverTag() { - return selfVipserverTag; - } + public static String getSelfAmorayTag() { + return selfAmorayTag; + } - public static String getSelfLocationTag() { - return selfLocationTag; - } - - public static String listToString(List list) { - if (list == null) { - return null; - } - StringBuilder result = new StringBuilder(); - boolean first = true; - // 第一个前面不拼接"," - for (String string : list) { - if (first) { - first = false; - } else { - result.append(","); - } - result.append(string); - } - return result.toString(); - } + public static String getSelfVipserverTag() { + return selfVipserverTag; + } - private static String selfAmorayTag; - private static String selfVipserverTag; - private static String selfLocationTag; - public final static String AMORY_TAG = "Amory-Tag"; - public final static String VIPSERVER_TAG = "Vipserver-Tag"; - public final static String LOCATION_TAG = "Location-Tag"; + public static String getSelfLocationTag() { + return selfLocationTag; + } + + public static String listToString(List list) { + if (list == null) { + return null; + } + StringBuilder result = new StringBuilder(); + boolean first = true; + // 第一个前面不拼接"," + for (String string : list) { + if (first) { + first = false; + } else { + result.append(","); + } + result.append(string); + } + return result.toString(); + } + + private static String selfAmorayTag; + private static String selfVipserverTag; + private static String selfLocationTag; + public final static String AMORY_TAG = "Amory-Tag"; + public final static String VIPSERVER_TAG = "Vipserver-Tag"; + public final static String LOCATION_TAG = "Location-Tag"; } diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/IPUtil.java b/client/src/main/java/com/alibaba/nacos/client/utils/IPUtil.java index 39ff2d496..0c5425b94 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/IPUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/IPUtil.java @@ -17,39 +17,40 @@ package com.alibaba.nacos.client.utils; import java.util.regex.Matcher; import java.util.regex.Pattern; + /** * ip tool - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class IPUtil { - public static boolean isIPV4(String addr) { - if (null == addr) { - return false; - } - String rexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"; + public static boolean isIPV4(String addr) { + if (null == addr) { + return false; + } + String rexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"; - Pattern pat = Pattern.compile(rexp); + Pattern pat = Pattern.compile(rexp); - Matcher mat = pat.matcher(addr); + Matcher mat = pat.matcher(addr); - boolean ipAddress = mat.find(); - return ipAddress; - } + boolean ipAddress = mat.find(); + return ipAddress; + } - public static boolean isIPV6(String addr) { - if (null == addr) { - return false; - } - String rexp = "^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"; + public static boolean isIPV6(String addr) { + if (null == addr) { + return false; + } + String rexp = "^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"; - Pattern pat = Pattern.compile(rexp); + Pattern pat = Pattern.compile(rexp); - Matcher mat = pat.matcher(addr); + Matcher mat = pat.matcher(addr); - boolean ipAddress = mat.find(); - return ipAddress; - } + boolean ipAddress = mat.find(); + return ipAddress; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/JSONUtils.java b/client/src/main/java/com/alibaba/nacos/client/utils/JSONUtils.java index 6b5092a38..2a75beaf9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/JSONUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/JSONUtils.java @@ -24,28 +24,28 @@ import org.codehaus.jackson.type.TypeReference; /** * Json tool - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class JSONUtils { static ObjectMapper mapper = new ObjectMapper(); - + static { - mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES); + mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES); } - public static String serializeObject(Object o) throws IOException { - return mapper.writeValueAsString(o); - } + public static String serializeObject(Object o) throws IOException { + return mapper.writeValueAsString(o); + } public static Object deserializeObject(String s, Class clazz) throws IOException { return mapper.readValue(s, clazz); } public static Object deserializeObject(String s, TypeReference typeReference) - throws IOException { + throws IOException { return mapper.readValue(s, typeReference); } diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java index 8b5c54ffb..8511ea92c 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java @@ -25,138 +25,124 @@ import com.alibaba.nacos.client.logger.Logger; /** * manage param tool - * @author nacos * + * @author nacos */ public class ParamUtil { - final static public Logger log = LogUtils.logger(ParamUtils.class); - - private static String defaultContextPath = "nacos"; - private static String defaultNodesPath = "serverlist"; - private static String appKey; - private static String appName; - private static String defaultServerPort; + final static public Logger log = LogUtils.logger(ParamUtils.class); + + private static String defaultContextPath = "nacos"; + private static String defaultNodesPath = "serverlist"; + private static String appKey; + private static String appName; + private static String defaultServerPort; private static String clientVersion = "unknown"; private static int connectTimeout; private static double perTaskConfigSize = 3000; - + static { // 客户端身份信息 appKey = System.getProperty("nacos.client.appKey", ""); - + appName = AppNameUtils.getAppName(); - - String defaultServerPortTmp = "8848"; - defaultServerPort = System.getProperty("nacos.server.port", defaultServerPortTmp); - log.info("settings", "[req-serv] nacos-server port:{}", defaultServerPort); + String defaultServerPortTmp = "8848"; - String tmp = "1000"; + defaultServerPort = System.getProperty("nacos.server.port", defaultServerPortTmp); + log.info("settings", "[req-serv] nacos-server port:{}", defaultServerPort); + + String tmp = "1000"; try { - tmp = System.getProperty("NACOS.CONNECT.TIMEOUT","1000"); + tmp = System.getProperty("NACOS.CONNECT.TIMEOUT", "1000"); connectTimeout = Integer.parseInt(tmp); } catch (NumberFormatException e) { final String msg = "[http-client] invalid connect timeout:" + tmp; log.error("settings", "NACOS-XXXX", msg, e); throw new IllegalArgumentException(msg, e); } - log.info("settings","[http-client] connect timeout:{}", connectTimeout); - - try { - InputStream in = HttpSimpleClient.class.getClassLoader() - .getResourceAsStream("application.properties"); - Properties props = new Properties(); - props.load(in); - String val = null; - val = props.getProperty("version"); - if (val != null) { - clientVersion = val; - } - log.info("NACOS_CLIENT_VERSION:{}", clientVersion); - } catch (Exception e) { - log.error("500", "read application.properties", e); - } - - try { - perTaskConfigSize = Double.valueOf(System.getProperty("PER_TASK_CONFIG_SIZE", "3000")); - log.warn("PER_TASK_CONFIG_SIZE:", perTaskConfigSize); - } catch (Throwable t) { - log.error("PER_TASK_CONFIG_SIZE", "PER_TASK_CONFIG_SIZE invalid", t); - } + log.info("settings", "[http-client] connect timeout:{}", connectTimeout); + + try { + InputStream in = HttpSimpleClient.class.getClassLoader() + .getResourceAsStream("application.properties"); + Properties props = new Properties(); + props.load(in); + String val = null; + val = props.getProperty("version"); + if (val != null) { + clientVersion = val; + } + log.info("NACOS_CLIENT_VERSION:{}", clientVersion); + } catch (Exception e) { + log.error("500", "read application.properties", e); + } + + try { + perTaskConfigSize = Double.valueOf(System.getProperty("PER_TASK_CONFIG_SIZE", "3000")); + log.warn("PER_TASK_CONFIG_SIZE:", perTaskConfigSize); + } catch (Throwable t) { + log.error("PER_TASK_CONFIG_SIZE", "PER_TASK_CONFIG_SIZE invalid", t); + } } + public static String getAppKey() { + return appKey; + } - public static String getAppKey() { - return appKey; - } + public static void setAppKey(String appKey) { + ParamUtil.appKey = appKey; + } + public static String getAppName() { + return appName; + } - public static void setAppKey(String appKey) { - ParamUtil.appKey = appKey; - } + public static void setAppName(String appName) { + ParamUtil.appName = appName; + } + public static String getDefaultContextPath() { + return defaultContextPath; + } - public static String getAppName() { - return appName; - } + public static void setDefaultContextPath(String defaultContextPath) { + ParamUtil.defaultContextPath = defaultContextPath; + } + public static String getClientVersion() { + return clientVersion; + } - public static void setAppName(String appName) { - ParamUtil.appName = appName; - } + public static void setClientVersion(String clientVersion) { + ParamUtil.clientVersion = clientVersion; + } + public static int getConnectTimeout() { + return connectTimeout; + } - public static String getDefaultContextPath() { - return defaultContextPath; - } + public static void setConnectTimeout(int connectTimeout) { + ParamUtil.connectTimeout = connectTimeout; + } + public static double getPerTaskConfigSize() { + return perTaskConfigSize; + } - public static void setDefaultContextPath(String defaultContextPath) { - ParamUtil.defaultContextPath = defaultContextPath; - } + public static void setPerTaskConfigSize(double perTaskConfigSize) { + ParamUtil.perTaskConfigSize = perTaskConfigSize; + } + public static String getDefaultServerPort() { + return defaultServerPort; + } - public static String getClientVersion() { - return clientVersion; - } + public static String getDefaultNodesPath() { + return defaultNodesPath; + } + public static void setDefaultNodesPath(String defaultNodesPath) { + ParamUtil.defaultNodesPath = defaultNodesPath; + } - public static void setClientVersion(String clientVersion) { - ParamUtil.clientVersion = clientVersion; - } - - - public static int getConnectTimeout() { - return connectTimeout; - } - - - public static void setConnectTimeout(int connectTimeout) { - ParamUtil.connectTimeout = connectTimeout; - } - - - public static double getPerTaskConfigSize() { - return perTaskConfigSize; - } - - - public static void setPerTaskConfigSize(double perTaskConfigSize) { - ParamUtil.perTaskConfigSize = perTaskConfigSize; - } - - - public static String getDefaultServerPort() { - return defaultServerPort; - } - - public static String getDefaultNodesPath() { - return defaultNodesPath; - } - - - public static void setDefaultNodesPath(String defaultNodesPath) { - ParamUtil.defaultNodesPath = defaultNodesPath; - } - } diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/StringUtils.java b/client/src/main/java/com/alibaba/nacos/client/utils/StringUtils.java index 608a2f75f..b59a49cdc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/StringUtils.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/StringUtils.java @@ -17,13 +17,13 @@ package com.alibaba.nacos.client.utils; /** * string util - * @author Nacos * + * @author Nacos */ public class StringUtils { public static final int INDEX_NOT_FOUND = -1; - + public static boolean isBlank(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { @@ -36,34 +36,34 @@ public class StringUtils { } return true; } - + public static boolean isNotEmpty(String str) { return !StringUtils.isEmpty(str); } - + public static boolean isEmpty(String str) { return str == null || str.length() == 0; } - + public static String defaultIfEmpty(String str, String defaultStr) { return StringUtils.isEmpty(str) ? defaultStr : str; } - + public static boolean equals(String str1, String str2) { return str1 == null ? str2 == null : str1.equals(str2); } - - public static String substringBetween(String str, String open, String close) { - if (str == null || open == null || close == null) { - return null; - } - int start = str.indexOf(open); - if (start != INDEX_NOT_FOUND) { - int end = str.indexOf(close, start + open.length()); - if (end != INDEX_NOT_FOUND) { - return str.substring(start + open.length(), end); - } - } - return null; - } + + public static String substringBetween(String str, String open, String close) { + if (str == null || open == null || close == null) { + return null; + } + int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) { + int end = str.indexOf(close, start + open.length()); + if (end != INDEX_NOT_FOUND) { + return str.substring(start + open.length(), end); + } + } + return null; + } } diff --git a/client/src/test/java/com/alibaba/nacos/client/NamingTest.java b/client/src/test/java/com/alibaba/nacos/client/NamingTest.java index e876bf103..a57f13ae0 100644 --- a/client/src/test/java/com/alibaba/nacos/client/NamingTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/NamingTest.java @@ -15,16 +15,44 @@ */ package com.alibaba.nacos.client; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.selector.ExpressionSelector; +import org.junit.Ignore; import org.junit.Test; +import java.util.HashMap; +import java.util.Map; + /** - * @author dungu.zpf + * @author nkorange */ public class NamingTest { @Test - public void testServiceList() { + @Ignore + public void testServiceList() throws Exception { + NamingService namingService = NacosFactory.createNamingService("127.0.0.1:8848"); + + Instance instance = new Instance(); + instance.setIp("1.1.1.1"); + instance.setPort(80); + instance.setWeight(2); + Map map = new HashMap(); + map.put("netType", "external"); + map.put("version", "2.0"); + instance.setMetadata(map); + + namingService.registerInstance("nacos.test.1", instance); + + ExpressionSelector expressionSelector = new ExpressionSelector(); + expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'dubbo'"); + ListView serviceList = namingService.getServicesOfServer(1, 10, expressionSelector); + + Thread.sleep(1000000000L); } diff --git a/cmdb/pom.xml b/cmdb/pom.xml new file mode 100644 index 000000000..2d355caee --- /dev/null +++ b/cmdb/pom.xml @@ -0,0 +1,106 @@ + + + + + + nacos-all + com.alibaba.nacos + 0.7.0 + ../pom.xml + + 4.0.0 + + nacos-cmdb + jar + + nacos-cmdb ${project.version} + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + test + + + + ${project.groupId} + nacos-core + + + + ${project.groupId} + nacos-api + + + + org.springframework.boot + spring-boot + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + -Dnacos.standalone=true + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + com.alibaba.nacos.cmdb.CmdbApp + + + + jar-with-dependencies + + + + + + + src/main/resources + + application.properties + + + + + diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/CmdbApp.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/CmdbApp.java new file mode 100644 index 000000000..96860e1f3 --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/CmdbApp.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author nkorange + */ +@SpringBootApplication +public class CmdbApp { + + public static void main(String[] args) { + SpringApplication.run(CmdbApp.class, args); + } +} diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/controllers/OperationController.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/controllers/OperationController.java new file mode 100644 index 000000000..9860d8e87 --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/controllers/OperationController.java @@ -0,0 +1,72 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb.controllers; + +import com.alibaba.nacos.cmdb.core.SwitchAndOptions; +import com.alibaba.nacos.cmdb.memory.CmdbProvider; +import com.alibaba.nacos.cmdb.utils.UtilsAndCommons; +import com.alibaba.nacos.core.utils.WebUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author nkorange + */ +@RestController +@RequestMapping(UtilsAndCommons.NACOS_CMDB_CONTEXT + "/ops") +public class OperationController { + + @Autowired + private SwitchAndOptions switches; + + @Autowired + private CmdbProvider cmdbProvider; + + @RequestMapping(value = "/updateSwitch", method = RequestMethod.POST) + public String updateSwitch(HttpServletRequest request) throws Exception { + + String entry = WebUtils.required(request, "entry"); + String value = WebUtils.required(request, "value"); + + switch (entry) { + case "dumpTaskInterval": + switches.setDumpTaskInterval(Integer.parseInt(value)); + break; + case "eventTaskInterval": + switches.setEventTaskInterval(Integer.parseInt(value)); + break; + case "loadDataAtStart": + switches.setLoadDataAtStart(Boolean.parseBoolean(value)); + break; + case "labelTaskInterval": + switches.setLabelTaskInterval(Integer.parseInt(value)); + default: + break; + } + return "ok"; + } + + @RequestMapping(value = "/queryLabel", method = RequestMethod.GET) + public String queryLabel(HttpServletRequest request) throws Exception { + String entry = WebUtils.required(request, "entry"); + String label = WebUtils.required(request, "label"); + return cmdbProvider.queryLabel(entry, "ip", label); + } +} diff --git a/core/src/main/java/com/alibaba/nacos/core/App.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/core/CmdbManager.java similarity index 77% rename from core/src/main/java/com/alibaba/nacos/core/App.java rename to cmdb/src/main/java/com/alibaba/nacos/cmdb/core/CmdbManager.java index b006320b4..597dc1790 100644 --- a/core/src/main/java/com/alibaba/nacos/core/App.java +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/core/CmdbManager.java @@ -13,16 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alibaba.nacos.core; +package com.alibaba.nacos.cmdb.core; /** - * Hello world! - * @author xxc + * @author nkorange */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } +public class CmdbManager { } diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/core/SwitchAndOptions.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/core/SwitchAndOptions.java new file mode 100644 index 000000000..dc199ae27 --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/core/SwitchAndOptions.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb.core; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @author nkorange + */ +@Component +public class SwitchAndOptions { + + @Value("${nacos.cmdb.dumpTaskInterval}") + private int dumpTaskInterval; + + @Value("${nacos.cmdb.eventTaskInterval}") + private int eventTaskInterval; + + @Value("${nacos.cmdb.labelTaskInterval}") + private int labelTaskInterval; + + @Value("${nacos.cmdb.loadDataAtStart}") + private boolean loadDataAtStart; + + public int getDumpTaskInterval() { + return dumpTaskInterval; + } + + public void setDumpTaskInterval(int dumpTaskInterval) { + this.dumpTaskInterval = dumpTaskInterval; + } + + public int getEventTaskInterval() { + return eventTaskInterval; + } + + public void setEventTaskInterval(int eventTaskInterval) { + this.eventTaskInterval = eventTaskInterval; + } + + public int getLabelTaskInterval() { + return labelTaskInterval; + } + + public void setLabelTaskInterval(int labelTaskInterval) { + this.labelTaskInterval = labelTaskInterval; + } + + public boolean isLoadDataAtStart() { + return loadDataAtStart; + } + + public void setLoadDataAtStart(boolean loadDataAtStart) { + this.loadDataAtStart = loadDataAtStart; + } +} diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/memory/CmdbProvider.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/memory/CmdbProvider.java new file mode 100644 index 000000000..5e3b6d59c --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/memory/CmdbProvider.java @@ -0,0 +1,249 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb.memory; + +import com.alibaba.fastjson.JSON; +import com.alibaba.nacos.api.cmdb.spi.CmdbService; +import com.alibaba.nacos.api.cmdb.pojo.Entity; +import com.alibaba.nacos.api.cmdb.pojo.EntityEvent; +import com.alibaba.nacos.api.cmdb.pojo.Label; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.cmdb.core.SwitchAndOptions; +import com.alibaba.nacos.cmdb.service.CmdbReader; +import com.alibaba.nacos.cmdb.service.CmdbWriter; +import com.alibaba.nacos.cmdb.utils.Loggers; +import com.alibaba.nacos.cmdb.utils.UtilsAndCommons; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * @author nkorange + */ +@Component +public class CmdbProvider implements CmdbReader, CmdbWriter { + + @Autowired + private SwitchAndOptions switches; + + private CmdbService cmdbService; + + ServiceLoader serviceLoader = ServiceLoader.load(CmdbService.class); + + private Map> entityMap = new ConcurrentHashMap<>(); + + private Map labelMap = new ConcurrentHashMap<>(); + + private Set entityTypeSet = new HashSet<>(); + + private List eventList = new ArrayList<>(); + + private long eventTimestamp = System.currentTimeMillis(); + + public CmdbProvider() throws NacosException { + } + + private void initCmdbService() throws NacosException { + Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + cmdbService = iterator.next(); + } + + if (cmdbService == null && switches.isLoadDataAtStart()) { + throw new NacosException(NacosException.SERVER_ERROR, "Cannot initialize CmdbService!"); + } + } + + public void load() { + + if (!switches.isLoadDataAtStart()) { + return; + } + + // init label map: + Set labelNames = cmdbService.getLabelNames(); + if (labelNames == null || labelNames.isEmpty()) { + Loggers.MAIN.warn("[LOAD] init label names failed!"); + } else { + for (String labelName : labelNames) { + // If get null label, it's still ok. We will try it later when we meet this label: + labelMap.put(labelName, cmdbService.getLabel(labelName)); + } + } + + // init entity type set: + entityTypeSet = cmdbService.getEntityTypes(); + + // init entity map: + entityMap = cmdbService.getAllEntities(); + } + + @PostConstruct + public void init() throws NacosException { + + initCmdbService(); + load(); + + UtilsAndCommons.GLOBAL_EXECUTOR.schedule(new CmdbDumpTask(), switches.getDumpTaskInterval(), TimeUnit.SECONDS); + UtilsAndCommons.GLOBAL_EXECUTOR.schedule(new CmdbLabelTask(), switches.getLabelTaskInterval(), TimeUnit.SECONDS); + UtilsAndCommons.GLOBAL_EXECUTOR.schedule(new CmdbEventTask(), switches.getEventTaskInterval(), TimeUnit.SECONDS); + } + + @Override + public Entity queryEntity(String entityName, String entityType) { + if (!entityMap.containsKey(entityType)) { + return null; + } + return entityMap.get(entityType).get(entityName); + } + + @Override + public String queryLabel(String entityName, String entityType, String labelName) { + Entity entity = queryEntity(entityName, entityType); + if (entity == null) { + return null; + } + return entity.getLabels().get(labelName); + } + + @Override + public List queryEntitiesByLabel(String labelName, String labelValue) { + throw new UnsupportedOperationException("Not available now!"); + } + + public void removeEntity(String entityName, String entityType) { + if (!entityMap.containsKey(entityType)) { + return; + } + entityMap.get(entityType).remove(entityName); + } + + public void updateEntity(Entity entity) { + if (!entityTypeSet.contains(entity.getType())) { + return; + } + entityMap.get(entity.getType()).put(entity.getName(), entity); + } + + public class CmdbLabelTask implements Runnable { + + @Override + public void run() { + + Loggers.MAIN.debug("LABEL-TASK {}", "start dump."); + + if (cmdbService == null) { + return; + } + + try { + + Map tmpLabelMap = new HashMap<>(16); + + Set labelNames = cmdbService.getLabelNames(); + if (labelNames == null || labelNames.isEmpty()) { + Loggers.MAIN.warn("CMDB-LABEL-TASK {}", "load label names failed!"); + } else { + for (String labelName : labelNames) { + // If get null label, it's still ok. We will try it later when we meet this label: + tmpLabelMap.put(labelName, cmdbService.getLabel(labelName)); + } + + if (Loggers.MAIN.isDebugEnabled()) { + Loggers.MAIN.debug("LABEL-TASK {}", "got label map:" + JSON.toJSONString(tmpLabelMap)); + } + + labelMap = tmpLabelMap; + } + + } catch (Exception e) { + Loggers.MAIN.error("CMDB-LABEL-TASK {}", "dump failed!", e); + } finally { + UtilsAndCommons.GLOBAL_EXECUTOR.schedule(this, switches.getLabelTaskInterval(), TimeUnit.SECONDS); + } + } + } + + public class CmdbDumpTask implements Runnable { + + @Override + public void run() { + + try { + + Loggers.MAIN.debug("DUMP-TASK {}", "start dump."); + + if (cmdbService == null) { + return; + } + // refresh entity map: + entityMap = cmdbService.getAllEntities(); + } catch (Exception e) { + Loggers.MAIN.error("DUMP-TASK {}", "dump failed!", e); + } finally { + UtilsAndCommons.GLOBAL_EXECUTOR.schedule(this, switches.getDumpTaskInterval(), TimeUnit.SECONDS); + } + } + } + + public class CmdbEventTask implements Runnable { + + @Override + public void run() { + try { + + Loggers.MAIN.debug("EVENT-TASK {}", "start dump."); + + if (cmdbService == null) { + return; + } + + long current = System.currentTimeMillis(); + List events = cmdbService.getEntityEvents(eventTimestamp); + eventTimestamp = current; + + if (Loggers.MAIN.isDebugEnabled()) { + Loggers.MAIN.debug("EVENT-TASK {}", "got events size:" + ", events:" + JSON.toJSONString(events)); + } + + if (events != null && !events.isEmpty()) { + + for (EntityEvent event : events) { + switch (event.getType()) { + case ENTITY_REMOVE: + removeEntity(event.getEntityName(), event.getEntityType()); + break; + case ENTITY_ADD_OR_UPDATE: + updateEntity(cmdbService.getEntity(event.getEntityName(), event.getEntityType())); + break; + default: + break; + } + } + } + + } catch (Exception e) { + Loggers.MAIN.error("CMDB-EVENT {}", "event task failed!", e); + } finally { + UtilsAndCommons.GLOBAL_EXECUTOR.schedule(this, switches.getEventTaskInterval(), TimeUnit.SECONDS); + } + } + } +} diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbReader.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbReader.java new file mode 100644 index 000000000..75bebcbc2 --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbReader.java @@ -0,0 +1,55 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb.service; + + +import com.alibaba.nacos.api.cmdb.pojo.Entity; + +import java.util.List; + +/** + * @author nkorange + */ +public interface CmdbReader { + + /** + * Get entity + * + * @param entityName name of entity + * @param entityType type of entity + * @return entity + */ + Entity queryEntity(String entityName, String entityType); + + /** + * Get label of entity + * + * @param entityName name of entity + * @param entityType type of entity + * @param labelName label name + * @return label value + */ + String queryLabel(String entityName, String entityType, String labelName); + + /** + * Get entities of selected label + * + * @param labelName name of label + * @param labelValue value of label + * @return list of entiy + */ + List queryEntitiesByLabel(String labelName, String labelValue); +} diff --git a/test/src/main/java/com/alibaba/nacos/test/App.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbWriter.java similarity index 77% rename from test/src/main/java/com/alibaba/nacos/test/App.java rename to cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbWriter.java index bf3ea7e9c..ccd44c441 100644 --- a/test/src/main/java/com/alibaba/nacos/test/App.java +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbWriter.java @@ -13,16 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alibaba.nacos.test; +package com.alibaba.nacos.cmdb.service; /** - * Hello world! - * @author xxc + * @author nkorange */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } +public interface CmdbWriter { } diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/Loggers.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/Loggers.java new file mode 100644 index 000000000..05df9e167 --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/Loggers.java @@ -0,0 +1,27 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author nacos + */ +public class Loggers { + + public static final Logger MAIN = LoggerFactory.getLogger("com.alibaba.nacos.cmdb.main"); +} diff --git a/cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/UtilsAndCommons.java b/cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/UtilsAndCommons.java new file mode 100644 index 000000000..ca1c51315 --- /dev/null +++ b/cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/UtilsAndCommons.java @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.cmdb.utils; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; + +/** + * @author nkorange + */ +public class UtilsAndCommons { + + public static final String NACOS_SERVER_VERSION = "/v1"; + + public static final String NACOS_CMDB_CONTEXT = NACOS_SERVER_VERSION + "/cmdb"; + + public static final ScheduledExecutorService GLOBAL_EXECUTOR; + + static { + + GLOBAL_EXECUTOR + = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("nacos.cmdb.global.executor"); + t.setDaemon(true); + return t; + } + }); + } +} diff --git a/cmdb/src/main/resources/application.properties b/cmdb/src/main/resources/application.properties new file mode 100644 index 000000000..d92634331 --- /dev/null +++ b/cmdb/src/main/resources/application.properties @@ -0,0 +1,6 @@ +server.port=8848 +server.servlet.context-path=/nacos + +nacos.cmdb.dumpTaskInterval=3600 +nacos.cmdb.eventTaskInterval=10 +nacos.cmdb.loadDataAtStart=true \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml index 27684516c..a5d87e979 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -12,40 +12,49 @@ limitations under the License. --> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - com.alibaba.nacos - nacos-all - 0.2.1 - - 4.0.0 + + com.alibaba.nacos + nacos-all + 0.7.0 + ../pom.xml + + 4.0.0 - nacos-common - jar + nacos-common + jar - nacos-common ${project.version} - http://maven.apache.org + nacos-common ${project.version} + http://maven.apache.org - - UTF-8 - + + UTF-8 + - - - commons-io - commons-io - - - junit - junit - test - + + + org.slf4j + slf4j-api + - - org.apache.commons - commons-lang3 - - + + commons-io + commons-io + + + + org.apache.commons + commons-lang3 + + + + + junit + junit + test + + + diff --git a/common/src/main/java/com/alibaba/nacos/common/AppendLicense.java b/common/src/main/java/com/alibaba/nacos/common/AppendLicense.java deleted file mode 100644 index d88e46700..000000000 --- a/common/src/main/java/com/alibaba/nacos/common/AppendLicense.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.common; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import org.apache.commons.io.IOUtils; - -/** - * - * 遍历目标文件夹下的所有文件,在文件头上加上license协议 - * 注意:读取/写入文件默认用utf-8进行 - * @author en.xuze@alipay.com - * @version $Id: AppendLicense.java, v 0.1 2018年7月4日 下午2:31:16 en.xuze@alipay.com Exp $ - */ -public class AppendLicense { - - private static List targetFiles = new LinkedList(); - private static String licenseFile = "/Users/en.xuze/git/nacos/common/license"; - private static String targetDirOrFile = "/Users/en.xuze/git/nacos"; - - public static void main(String[] args) throws Exception { - List licenseContents = IOUtils.readLines(new FileInputStream(new File(licenseFile)), "utf-8"); - readFiles(targetDirOrFile); - for (Iterator iterator = targetFiles.iterator(); iterator.hasNext();) { - File file = (File) iterator.next(); - List srcFileContents = IOUtils.readLines(new FileInputStream(file), "utf-8"); - List writeContents = new ArrayList(); - writeContents.addAll(licenseContents); - writeContents.addAll(srcFileContents); - IOUtils.writeLines(writeContents, "\n", new FileOutputStream(file)); - System.out.println("append license to file:" + file.getAbsolutePath()); - } - } - - private static void readFiles(String filePath) { - if (filePath == null) { - return; - } - File temp = new File(filePath); - File[] files = null; - if (temp.isFile()) { - if (needAppend(temp.getName())) { - targetFiles.add(temp); - } - } else { - files = temp.listFiles(); - } - if (files == null) { - return; - } - for (File f : files) { - if (f.isFile()) { - if (needAppend(f.getName())) { - targetFiles.add(f); - } - } else if (f.isDirectory()) { - readFiles(f.getPath()); - } - } - } - - private static boolean needAppend(String fileName) { - return (fileName.endsWith(".java")); - } -} diff --git a/common/src/main/java/com/alibaba/nacos/common/util/Constants.java b/common/src/main/java/com/alibaba/nacos/common/util/Constants.java new file mode 100644 index 000000000..92b044af5 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/util/Constants.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common.util; + +/** + * Nacos common constants + * + * @author Mercy + * @since 0.2.2 + */ +public interface Constants { + + /** + * Spring Profile : "standalone" + */ + String STANDALONE_SPRING_PROFILE = "standalone"; +} diff --git a/common/src/main/java/com/alibaba/nacos/common/util/IoUtils.java b/common/src/main/java/com/alibaba/nacos/common/util/IoUtils.java index f9f489dc0..09beef788 100644 --- a/common/src/main/java/com/alibaba/nacos/common/util/IoUtils.java +++ b/common/src/main/java/com/alibaba/nacos/common/util/IoUtils.java @@ -15,6 +15,7 @@ */ package com.alibaba.nacos.common.util; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import java.io.*; @@ -29,39 +30,41 @@ import java.util.zip.GZIPInputStream; */ public class IoUtils { - static public String toString(InputStream input, String encoding) throws IOException { - return (null == encoding) ? toString(new InputStreamReader(input, "UTF-8")) - : toString(new InputStreamReader(input, encoding)); - } + public static byte[] tryDecompress(InputStream raw) throws Exception { - static public String toString(Reader reader) throws IOException { - CharArrayWriter sw = new CharArrayWriter(); - copy(reader, sw); - return sw.toString(); - } + try { + GZIPInputStream gis + = new GZIPInputStream(raw); + ByteArrayOutputStream out + = new ByteArrayOutputStream(); + IOUtils.copy(gis, out); - static public long copy(Reader input, Writer output) throws IOException { - char[] buffer = new char[1 << 12]; - long count = 0; - for (int n = 0; (n = input.read(buffer)) >= 0; ) { - output.write(buffer, 0, n); - count += n; - } - return count; - } - - static public long copy(InputStream input, OutputStream output) throws IOException { - byte[] buffer = new byte[1024]; - int bytesRead; - int totalBytes = 0; - while ((bytesRead = input.read(buffer)) != -1) { - output.write(buffer, 0, bytesRead); - - totalBytes += bytesRead; + return out.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); } - return totalBytes; + return null; + } + + static private BufferedReader toBufferedReader(Reader reader) { + return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader( + reader); + } + + public static void writeStringToFile(File file, String data, String encoding) + throws IOException { + OutputStream os = null; + try { + os = new FileOutputStream(file); + os.write(data.getBytes(encoding)); + os.flush(); + } finally { + if (null != os) { + os.close(); + } + } } static public List readLines(Reader input) throws IOException { @@ -81,93 +84,5 @@ public class IoUtils { return list; } - static private BufferedReader toBufferedReader(Reader reader) { - return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader( - reader); - } - - - public static boolean delete(File fileOrDir) throws IOException { - if (fileOrDir == null) { - return false; - } - - if (fileOrDir.isDirectory()) { - cleanDirectory(fileOrDir); - } - - return fileOrDir.delete(); - } - - public static void cleanDirectory(File directory) throws IOException { - if (!directory.exists()) { - String message = directory + " does not exist"; - throw new IllegalArgumentException(message); - } - - if (!directory.isDirectory()) { - String message = directory + " is not a directory"; - throw new IllegalArgumentException(message); - } - - File[] files = directory.listFiles(); - if (files == null) { - throw new IOException("Failed to list contents of " + directory); - } - - IOException exception = null; - for (File file : files) { - try { - delete(file); - } catch (IOException ioe) { - exception = ioe; - } - } - - if (null != exception) { - throw exception; - } - } - - public static void writeStringToFile(File file, String data, String encoding) - throws IOException { - OutputStream os = null; - try { - os = new FileOutputStream(file); - os.write(data.getBytes(encoding)); - os.flush(); - } finally { - if (null != os) { - os.close(); - } - } - } - - public static byte[] tryDecompress(InputStream raw) throws Exception { - - try { - GZIPInputStream gis - = new GZIPInputStream(raw); - ByteArrayOutputStream out - = new ByteArrayOutputStream(); - - - IoUtils.copy(gis, out); - - return out.toByteArray(); - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - public static void main(String[] args) throws IOException { - -// String path = "/Users/zhupengfei/test_write.txt"; - -// writeStringToFile(new File(path), "hello2222", "utf-8"); - } - } diff --git a/common/src/main/java/com/alibaba/nacos/common/util/SystemUtil.java b/common/src/main/java/com/alibaba/nacos/common/util/SystemUtil.java deleted file mode 100644 index 4955187e2..000000000 --- a/common/src/main/java/com/alibaba/nacos/common/util/SystemUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.common.util; - -import java.lang.management.ManagementFactory; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.apache.commons.lang3.StringUtils; -import com.sun.management.OperatingSystemMXBean; - -/** - * @author nacos - */ -public class SystemUtil { - - private static OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - - public static List getIPsBySystemEnv(String key) { - String env = getSystemEnv(key); - List ips = new ArrayList(); - if (StringUtils.isNotEmpty(env)) { - ips = Arrays.asList(env.split(",")); - } - return ips; - } - - public static String getSystemEnv(String key) { - String env = System.getenv(key); - return env; - } - - public static void main(String[] args) throws SQLException { - System.out.println(Boolean.parseBoolean("Tfue")); - } - - public static float getLoad() { - return (float) operatingSystemMXBean.getSystemLoadAverage(); - } - - public static float getCPU() { - return (float) operatingSystemMXBean.getSystemCpuLoad(); - } - - public static float getMem() { - return (float) (1 - (double) operatingSystemMXBean.getFreePhysicalMemorySize() / (double) operatingSystemMXBean.getTotalPhysicalMemorySize()); - } -} diff --git a/common/src/main/java/com/alibaba/nacos/common/util/SystemUtils.java b/common/src/main/java/com/alibaba/nacos/common/util/SystemUtils.java new file mode 100644 index 000000000..36a4d09e2 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/util/SystemUtils.java @@ -0,0 +1,180 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common.util; + +import com.sun.management.OperatingSystemMXBean; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; + +import static org.apache.commons.lang3.CharEncoding.UTF_8; + +/** + * @author nacos + */ +public class SystemUtils { + + private static final Logger logger = LoggerFactory.getLogger(SystemUtils.class); + + /** + * The System property name of Standalone mode + */ + public static final String STANDALONE_MODE_PROPERTY_NAME = "nacos.standalone"; + + /** + * The System property name of prefer hostname over ip + */ + public static final String PREFER_HOSTNAME_OVER_IP_PROPERTY_NAME = "nacos.preferHostnameOverIp"; + /** + * Flag to say that, when guessing a hostname, the hostname of the server should be used in preference to the IP + * address reported by the OS. + */ + public static final boolean PREFER_HOSTNAME_OVER_IP = Boolean.getBoolean(PREFER_HOSTNAME_OVER_IP_PROPERTY_NAME); + + /** + * Standalone mode or not + */ + public static final boolean STANDALONE_MODE = Boolean.getBoolean(STANDALONE_MODE_PROPERTY_NAME); + + private static OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean)ManagementFactory + .getOperatingSystemMXBean(); + + public static final String LOCAL_IP = getHostAddress(); + + /** + * The key of nacos home. + */ + public static final String NACOS_HOME_KEY = "nacos.home"; + + /** + * The home of nacos. + */ + public static final String NACOS_HOME = getNacosHome(); + + /** + * The file path of cluster conf. + */ + public static final String CLUSTER_CONF_FILE_PATH = getClusterConfFilePath(); + + public static List getIPsBySystemEnv(String key) { + String env = getSystemEnv(key); + List ips = new ArrayList(); + if (StringUtils.isNotEmpty(env)) { + ips = Arrays.asList(env.split(",")); + } + return ips; + } + + public static String getSystemEnv(String key) { + return System.getenv(key); + } + + public static float getLoad() { + return (float)operatingSystemMXBean.getSystemLoadAverage(); + } + + public static float getCPU() { + return (float)operatingSystemMXBean.getSystemCpuLoad(); + } + + public static float getMem() { + return (float)(1 - (double)operatingSystemMXBean.getFreePhysicalMemorySize() / (double)operatingSystemMXBean + .getTotalPhysicalMemorySize()); + } + + private static String getHostAddress() { + String address = System.getProperty("nacos.server.ip"); + if (StringUtils.isNotEmpty(address)) { + return address; + } + + address = "127.0.0.1"; + + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + Enumeration inetAddresses = networkInterface.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + InetAddress ip = inetAddresses.nextElement(); + // 兼容不规范网段 + if (!ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) { + return ip.getHostAddress(); + } + } + } + } catch (Exception e) { + logger.error("get local host address error", e); + } + + return address; + } + + private static String getNacosHome() { + String nacosHome = System.getProperty(NACOS_HOME_KEY); + if (StringUtils.isBlank(nacosHome)) { + nacosHome = System.getProperty("user.home") + File.separator + "nacos"; + } + return nacosHome; + } + + public static String getConfFilePath() { + return NACOS_HOME + File.separator + "conf" + File.separator; + } + + private static String getClusterConfFilePath() { + return NACOS_HOME + File.separator + "conf" + File.separator + "cluster.conf"; + } + + public static List readClusterConf() throws IOException { + List instanceList = new ArrayList(); + List lines = IoUtils.readLines( + new InputStreamReader(new FileInputStream(new File(CLUSTER_CONF_FILE_PATH)), UTF_8)); + String comment = "#"; + for (String line : lines) { + String instance = line.trim(); + if (instance.startsWith(comment)) { + // # it is ip + continue; + } + if (instance.contains(comment)) { + // 192.168.71.52:8848 # Instance A + instance = instance.substring(0, instance.indexOf(comment)); + instance = instance.trim(); + } + instanceList.add(instance); + } + return instanceList; + } + + public static void writeClusterConf(String content) throws IOException { + IoUtils.writeStringToFile(new File(CLUSTER_CONF_FILE_PATH), content, UTF_8); + } + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/util/UuidUtil.java b/common/src/main/java/com/alibaba/nacos/common/util/UuidUtils.java similarity index 90% rename from common/src/main/java/com/alibaba/nacos/common/util/UuidUtil.java rename to common/src/main/java/com/alibaba/nacos/common/util/UuidUtils.java index ab20b6d0f..cfbc324d5 100644 --- a/common/src/main/java/com/alibaba/nacos/common/util/UuidUtil.java +++ b/common/src/main/java/com/alibaba/nacos/common/util/UuidUtils.java @@ -18,9 +18,9 @@ package com.alibaba.nacos.common.util; import java.util.UUID; /** - * @author dungu.zpf + * @author nkorange */ -public class UuidUtil { +public class UuidUtils { public static String generateUuid() { return UUID.randomUUID().toString(); diff --git a/common/src/test/java/com/alibaba/nacos/common/SystemUtilsTest.java b/common/src/test/java/com/alibaba/nacos/common/SystemUtilsTest.java new file mode 100644 index 000000000..84d983533 --- /dev/null +++ b/common/src/test/java/com/alibaba/nacos/common/SystemUtilsTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.common; + +import com.alibaba.nacos.common.util.SystemUtils; +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Random; + +/** + * {@link SystemUtils} Test + * + * @author Mercy + * @since 0.2.2 + */ +public class SystemUtilsTest { + + private static final Random random = new Random(); + + private static boolean standaloneMode = random.nextBoolean(); + private static boolean preferHostMode = random.nextBoolean(); + + @BeforeClass + public static void init() { + System.setProperty("nacos.standalone", String.valueOf(standaloneMode)); + System.setProperty("nacos.preferHostnameOverIp", String.valueOf(preferHostMode)); + } + + @Test + public void testStandaloneModeConstants() { + + System.out.printf("System property \"%s\" = %s \n", "nacos.standalone", standaloneMode); + + if ("true".equalsIgnoreCase(System.getProperty("nacos.standalone"))) { + Assert.assertTrue(SystemUtils.STANDALONE_MODE); + } else { + Assert.assertFalse(SystemUtils.STANDALONE_MODE); + } + + Assert.assertEquals(standaloneMode, SystemUtils.STANDALONE_MODE); + + } + + @Test + public void testPreferHostModeConstants() { + + System.out.printf("System property \"%s\" = %s \n", "nacos.preferrHostnameOverIp", preferHostMode); + + if ("true".equalsIgnoreCase(System.getProperty("nacos.preferHostnameOverIp"))) { + Assert.assertTrue(SystemUtils.PREFER_HOSTNAME_OVER_IP); + } else { + Assert.assertFalse(SystemUtils.PREFER_HOSTNAME_OVER_IP); + } + + Assert.assertEquals(preferHostMode, SystemUtils.PREFER_HOSTNAME_OVER_IP); + + } + + @Test + public void testReadClusterConf() throws IOException { + FileUtils.forceMkdir(new File(SystemUtils.getConfFilePath())); + + String lineSeparator = System.getProperty("line.separator"); + + /* + * #it is ip + * #example + * 192.168.1.1:8848 + */ + SystemUtils.writeClusterConf("#it is ip" + lineSeparator + "#example" + lineSeparator + "192.168.1.1:8848"); + Assert.assertEquals(SystemUtils.readClusterConf().get(0), "192.168.1.1:8848"); + + /* + * #it is ip + * #example + * # 192.168.1.1:8848 + * 192.168.1.2:8848 # Instance A + */ + SystemUtils.writeClusterConf( + "#it is ip" + lineSeparator + " #example" + lineSeparator + " # 192.168.1.1:8848" + lineSeparator + + " 192.168.1.2:8848 # Instance A " + lineSeparator + "192.168.1.3#:8848"); + List instanceList = SystemUtils.readClusterConf(); + Assert.assertEquals(instanceList.get(0), "192.168.1.2:8848"); + Assert.assertEquals(instanceList.get(1), "192.168.1.3"); + } + +} diff --git a/config/pom.xml b/config/pom.xml index 7085dcb91..b212285d8 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -12,169 +12,180 @@ limitations under the License. --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - com.alibaba.nacos - nacos-all - 0.2.1 - + + com.alibaba.nacos + nacos-all + 0.7.0 + - 4.0.0 + 4.0.0 - nacos-config - jar + nacos-config + jar - nacos-config ${project.version} - http://maven.apache.org + nacos-config ${project.version} + http://maven.apache.org - - UTF-8 - + + UTF-8 + - - - junit - junit - test - - - org.springframework.boot - spring-boot-starter-web - - - ${project.groupId} - nacos-core - - - com.google.guava - guava - - - taglibs - standard - - - org.springframework.boot - spring-boot-starter-jdbc - - - commons-io - commons-io - - - commons-lang - commons-lang - - - mysql - mysql-connector-java - - - commons-dbcp - commons-dbcp - - - org.apache.derby - derby - - - ch.qos.logback - logback-classic - + + + junit + junit + test + + + org.springframework.boot + spring-boot-starter-web + + + ${project.groupId} + nacos-core + + + com.google.guava + guava + + + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + commons-io + commons-io + + + commons-lang + commons-lang + 2.6 + + + mysql + mysql-connector-java + + + commons-dbcp + commons-dbcp + + + org.apache.derby + derby + + + ch.qos.logback + logback-classic + - - - org.aspectj - aspectjrt - - - cglib - cglib-nodep - - - org.apache.httpcomponents - httpasyncclient - - - - - org.springframework.boot - spring-boot-starter-tomcat - - - org.codehaus.jackson - jackson-mapper-lgpl - - - net.jcip - jcip-annotations - true - - - com.github.spotbugs - spotbugs-annotations - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - true - -Dnacos.standalone=true - - - - org.apache.maven.plugins - maven-assembly-plugin - - - - com.alibaba.nacos.config.server.Config - - - - jar-with-dependencies - - - - - - - true - src/main/resources - - application.properties - banner.txt - schema.sql - nacos-db.sql - - - - - - - springboot - - - com.alibaba.nacos - nacos-core - - - - - - org.springframework.boot - spring-boot-maven-plugin - - com.alibaba.nacos.config.server.Config - - - - nacos-config - - - + + + org.aspectj + aspectjrt + + + cglib + cglib-nodep + + + org.apache.httpcomponents + httpasyncclient + + + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.codehaus.jackson + jackson-mapper-lgpl + + + net.jcip + jcip-annotations + true + + + com.github.spotbugs + spotbugs-annotations + + + org.mockito + mockito-core + test + + + org.apache.commons + commons-lang3 + 3.4 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + -Dnacos.standalone=true + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + com.alibaba.nacos.config.server.Config + + + + jar-with-dependencies + + + + + + + + + springboot + + + com.alibaba.nacos + nacos-core + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.alibaba.nacos.config.server.Config + + + + nacos-config + + + diff --git a/config/src/main/java/LogbackInitTest.java b/config/src/main/java/LogbackInitTest.java deleted file mode 100644 index 408d0fb3a..000000000 --- a/config/src/main/java/LogbackInitTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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. - */ -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.alibaba.nacos.config.server.utils.AppNameUtils; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; - -/** - * logback test - * @author Nacos - * - */ -public class LogbackInitTest { - - private static final Logger logger = LoggerFactory.getLogger(LogbackInitTest.class); - - - - public static void main(String[] args) throws Exception { - AppNameUtils.class.getClassLoader(); - String classpath = AppNameUtils.class.getResource("/").getPath(); - System.out.println("The classpath is " + classpath); - - LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); - lc.reset(); - - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(lc); - configurator.doConfigure(LogbackInitTest.class.getResource("logback-jiuren.xml")); - - for (;;) { - logger.info("hello"); - System.out.println(getLevel(logger)); - Thread.sleep(1000L); - } - } - - - static String getLevel(Logger logger) { - if (logger.isDebugEnabled()) { - return "debug"; - } else if (logger.isInfoEnabled()) { - return "info"; - } else if (logger.isWarnEnabled()) { - return "warn"; - } else if (logger.isErrorEnabled()) { - return "error"; - } else { - return "unknown"; - } - } -} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/Config.java b/config/src/main/java/com/alibaba/nacos/config/server/Config.java index 3b2c0e0a9..4a5e7dcd4 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/Config.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/Config.java @@ -15,26 +15,20 @@ */ package com.alibaba.nacos.config.server; -import java.net.UnknownHostException; - import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.web.servlet.ServletComponentScan; + +import java.net.UnknownHostException; /** * Config main - * - * @author Nacos * + * @author Nacos */ -@SpringBootApplication(scanBasePackages = "com.alibaba.nacos.config.server") -@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) -@ServletComponentScan +@SpringBootApplication public class Config { - public static void main(String[] args) throws UnknownHostException { - SpringApplication.run(Config.class, args); - } - + + public static void main(String[] args) throws UnknownHostException { + SpringApplication.run(Config.class, args); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/aspect/CapacityManagementAspect.java b/config/src/main/java/com/alibaba/nacos/config/server/aspect/CapacityManagementAspect.java index 793765461..442bea46e 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/aspect/CapacityManagementAspect.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/aspect/CapacityManagementAspect.java @@ -15,19 +15,6 @@ */ package com.alibaba.nacos.config.server.aspect; -import java.nio.charset.Charset; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.CounterMode; import com.alibaba.nacos.config.server.model.ConfigInfo; @@ -35,6 +22,17 @@ import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.service.PersistService; import com.alibaba.nacos.config.server.service.capacity.CapacityService; import com.alibaba.nacos.config.server.utils.PropertyUtil; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.Charset; /** * 容量管理切面:批量写入、更新暂不处理 @@ -44,398 +42,393 @@ import com.alibaba.nacos.config.server.utils.PropertyUtil; */ @Aspect public class CapacityManagementAspect { - private static final Logger LOGGER = LoggerFactory.getLogger(CapacityManagementAspect.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CapacityManagementAspect.class); - private static final String SYNC_UPDATE_CONFIG_ALL - = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.publishConfig(..)) && args" - + "(request,response,dataId,group,content,appName,srcUser,tenant,tag,..)"; + private static final String SYNC_UPDATE_CONFIG_ALL + = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.publishConfig(..)) && args" + + "(request,response,dataId,group,content,appName,srcUser,tenant,tag,..)"; - private static final String DELETE_CONFIG - = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.deleteConfig(..)) && args" - + "(request,response,dataId,group,tenant,..)"; + private static final String DELETE_CONFIG + = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.deleteConfig(..)) && args" + + "(request,response,dataId,group,tenant,..)"; + @Autowired + private CapacityService capacityService; + @Autowired + private PersistService persistService; - @Autowired - private CapacityService capacityService; - @Autowired - private PersistService persistService; + /** + * 更新也需要判断content内容是否超过大小限制 + */ + @Around(SYNC_UPDATE_CONFIG_ALL) + public Object aroundSyncUpdateConfigAll(ProceedingJoinPoint pjp, HttpServletRequest request, + HttpServletResponse response, String dataId, String group, String content, + String appName, String srcUser, String tenant, String tag) + throws Throwable { + if (!PropertyUtil.isManageCapacity()) { + return pjp.proceed(); + } + LOGGER.info("[capacityManagement] aroundSyncUpdateConfigAll"); + String betaIps = request.getHeader("betaIps"); + if (StringUtils.isBlank(betaIps)) { + if (StringUtils.isBlank(tag)) { + // 只对写入或更新config_info表的做容量管理的限制检验 + if (persistService.findConfigInfo(dataId, group, tenant) == null) { + // 写入操作 + return do4Insert(pjp, request, response, group, tenant, content); + } + // 更新操作 + return do4Update(pjp, request, response, dataId, group, tenant, content); + } + } + return pjp.proceed(); + } + /** + * 更新操作:开启容量管理的限制检验功能,会检验"content的大小"是否超过限制 + * + * @throws Throwable "实际操作"抛出的异常 + */ + private Object do4Update(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, + String dataId, String group, String tenant, String content) throws Throwable { + if (!PropertyUtil.isCapacityLimitCheck()) { + return pjp.proceed(); + } + try { + boolean hasTenant = hasTenant(tenant); + Capacity capacity = getCapacity(group, tenant, hasTenant); + if (isSizeLimited(group, tenant, getCurrentSize(content), hasTenant, false, capacity)) { + return response4Limit(request, response, LimitType.OVER_MAX_SIZE); + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] do4Update ", e); + } + return pjp.proceed(); + } - /** - * 更新也需要判断content内容是否超过大小限制 - */ - @Around(SYNC_UPDATE_CONFIG_ALL) - public Object aroundSyncUpdateConfigAll(ProceedingJoinPoint pjp, HttpServletRequest request, - HttpServletResponse response, String dataId, String group, String content, - String appName, String srcUser, String tenant, String tag) - throws Throwable { - if (!PropertyUtil.isManageCapacity()) { - return pjp.proceed(); - } - LOGGER.info("[capacityManagement] aroundSyncUpdateConfigAll"); - String betaIps = request.getHeader("betaIps"); - if (StringUtils.isBlank(betaIps)) { - if (StringUtils.isBlank(tag)) { - // 只对写入或更新config_info表的做容量管理的限制检验 - if (persistService.findConfigInfo(dataId, group, tenant) == null) { - // 写入操作 - return do4Insert(pjp, request, response, group, tenant, content); - } - // 更新操作 - return do4Update(pjp, request, response, dataId, group, tenant, content); - } - } - return pjp.proceed(); - } + /** + * 写入操作:1. 无论是否开启容量管理的限制检验功能都会计数(usage) 2.开启容量管理的限制检验功能,会检验"限额"和"content的大小" + * + * @throws Throwable "实际操作"抛出的异常 + */ + private Object do4Insert(ProceedingJoinPoint pjp, HttpServletRequest request, + HttpServletResponse response, String group, String tenant, String content) + throws Throwable { + LOGGER.info("[capacityManagement] do4Insert"); + CounterMode counterMode = CounterMode.INCREMENT; + boolean hasTenant = hasTenant(tenant); + if (PropertyUtil.isCapacityLimitCheck()) { + // 先写入或更新:usage + 1 + LimitType limitType = getLimitType(counterMode, group, tenant, content, hasTenant); + if (limitType != null) { + return response4Limit(request, response, limitType); + } + } else { + // 先写入或更新:usage + 1 + insertOrUpdateUsage(group, tenant, counterMode, hasTenant); + } + return getResult(pjp, response, group, tenant, counterMode, hasTenant); + } - /** - * 更新操作:开启容量管理的限制检验功能,会检验"content的大小"是否超过限制 - * - * @throws Throwable "实际操作"抛出的异常 - */ - private Object do4Update(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, - String dataId, String group, String tenant, String content) throws Throwable { - if (!PropertyUtil.isCapacityLimitCheck()) { - return pjp.proceed(); - } - try { - boolean hasTenant = hasTenant(tenant); - Capacity capacity = getCapacity(group, tenant, hasTenant); - if (isSizeLimited(group, tenant, getCurrentSize(content), hasTenant, false, capacity)) { - return response4Limit(request, response, LimitType.OVER_MAX_SIZE); - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] do4Update ", e); - } - return pjp.proceed(); - } + private Object response4Limit(HttpServletRequest request, HttpServletResponse response, LimitType limitType) { + response.setStatus(limitType.status); + return String.valueOf(limitType.status); + } - /** - * 写入操作:1. 无论是否开启容量管理的限制检验功能都会计数(usage) 2.开启容量管理的限制检验功能,会检验"限额"和"content的大小" - * - * @throws Throwable "实际操作"抛出的异常 - */ - private Object do4Insert(ProceedingJoinPoint pjp, HttpServletRequest request, - HttpServletResponse response, String group, String tenant, String content) - throws Throwable { - LOGGER.info("[capacityManagement] do4Insert"); - CounterMode counterMode = CounterMode.INCREMENT; - boolean hasTenant = hasTenant(tenant); - if (PropertyUtil.isCapacityLimitCheck()) { - // 先写入或更新:usage + 1 - LimitType limitType = getLimitType(counterMode, group, tenant, content, hasTenant); - if (limitType != null) { - return response4Limit(request, response, limitType); - } - } else { - // 先写入或更新:usage + 1 - insertOrUpdateUsage(group, tenant, counterMode, hasTenant); - } - return getResult(pjp, response, group, tenant, counterMode, hasTenant); - } + private boolean hasTenant(String tenant) { + return StringUtils.isNotBlank(tenant); + } - private Object response4Limit(HttpServletRequest request, HttpServletResponse response, LimitType limitType) { - response.setStatus(limitType.status); - return String.valueOf(limitType.status); - } + /** + * 无论是否开启容量管理的限制检验功能,删除时候,计数模块中容量信息表中的usage都得减一 + */ + @Around(DELETE_CONFIG) + public Object aroundDeleteConfig(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, + String dataId, String group, String tenant) throws Throwable { + if (!PropertyUtil.isManageCapacity()) { + return pjp.proceed(); + } + LOGGER.info("[capacityManagement] aroundDeleteConfig"); + ConfigInfo configInfo = persistService.findConfigInfo(dataId, group, tenant); + if (configInfo == null) { + return pjp.proceed(); + } + return do4Delete(pjp, response, group, tenant, configInfo); + } - private boolean hasTenant(String tenant) { - return StringUtils.isNotBlank(tenant); - } + /** + * @throws Throwable "实际操作"抛出的异常 + */ + private Object do4Delete(ProceedingJoinPoint pjp, HttpServletResponse response, String group, String tenant, + ConfigInfo configInfo) + throws Throwable { + boolean hasTenant = hasTenant(tenant); + if (configInfo == null) { + // "configInfo == null"有2种可能: + // 1. 并发删除;2. 先是新增子配置,后来删除了所有子配置,这时合并写入到configInfo的task(异步)还没执行 + // 关于第2点,那么接下会顺序执行"合并写入config_info的task","删除config_info的task" + // 主动修正usage,当刚好在上述的"合并写入config_info的task"执行完时修正usage,此时usage=1 + // 而后面个"删除config_info的task"执行时并不会把usage-1,因为请求已经返回了。 + // 因此还是需要定时修正usage的Job + correctUsage(group, tenant, hasTenant); + return pjp.proceed(); + } + // 并发删除同一个记录,可能同时走到这里,加上这个接口是异步删除的(提交MergeDataTask给MergeTaskProcessor处理),可能导致usage不止减一。因此还是需要定时修正usage的Job + CounterMode counterMode = CounterMode.DECREMENT; + insertOrUpdateUsage(group, tenant, counterMode, hasTenant); + return getResult(pjp, response, group, tenant, counterMode, hasTenant); + } - /** - * 无论是否开启容量管理的限制检验功能,删除时候,计数模块中容量信息表中的usage都得减一 - */ - @Around(DELETE_CONFIG) - public Object aroundDeleteConfig(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, - String dataId, String group, String tenant) throws Throwable { - if (!PropertyUtil.isManageCapacity()) { - return pjp.proceed(); - } - LOGGER.info("[capacityManagement] aroundDeleteConfig"); - ConfigInfo configInfo = persistService.findConfigInfo(dataId, group, tenant); - if (configInfo == null) { - return pjp.proceed(); - } - return do4Delete(pjp, response, group, tenant, configInfo); - } + private void correctUsage(String group, String tenant, boolean hasTenant) { + try { + if (hasTenant) { + LOGGER.info("主动修正usage, tenant: {}", tenant); + capacityService.correctTenantUsage(tenant); + } else { + LOGGER.info("主动修正usage, group: {}", group); + capacityService.correctGroupUsage(group); + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] correctUsage ", e); + } + } - /** - * @throws Throwable "实际操作"抛出的异常 - */ - private Object do4Delete(ProceedingJoinPoint pjp, HttpServletResponse response, String group, String tenant, - ConfigInfo configInfo) - throws Throwable { - boolean hasTenant = hasTenant(tenant); - if (configInfo == null) { - // "configInfo == null"有2种可能: - // 1. 并发删除;2. 先是新增子配置,后来删除了所有子配置,这时合并写入到configInfo的task(异步)还没执行 - // 关于第2点,那么接下会顺序执行"合并写入config_info的task","删除config_info的task" - // 主动修正usage,当刚好在上述的"合并写入config_info的task"执行完时修正usage,此时usage=1 - // 而后面个"删除config_info的task"执行时并不会把usage-1,因为请求已经返回了。 - // 因此还是需要定时修正usage的Job - correctUsage(group, tenant, hasTenant); - return pjp.proceed(); - } - // 并发删除同一个记录,可能同时走到这里,加上这个接口是异步删除的(提交MergeDataTask给MergeTaskProcessor处理),可能导致usage不止减一。因此还是需要定时修正usage的Job - CounterMode counterMode = CounterMode.DECREMENT; - insertOrUpdateUsage(group, tenant, counterMode, hasTenant); - return getResult(pjp, response, group, tenant, counterMode, hasTenant); - } + private Object getResult(ProceedingJoinPoint pjp, HttpServletResponse response, String group, String tenant, + CounterMode counterMode, boolean hasTenant) throws Throwable { + try { + // 执行实际操作 + Object result = pjp.proceed(); + // 根据执行结果判定是否需要回滚 + doResult(counterMode, response, group, tenant, result, hasTenant); + return result; + } catch (Throwable throwable) { + LOGGER.warn("[capacityManagement] inner operation throw exception, rollback, group: {}, tenant: {}", + group, tenant, throwable); + rollback(counterMode, group, tenant, hasTenant); + throw throwable; + } + } - private void correctUsage(String group, String tenant, boolean hasTenant) { - try { - if (hasTenant) { - LOGGER.info("主动修正usage, tenant: {}", tenant); - capacityService.correctTenantUsage(tenant); - } else { - LOGGER.info("主动修正usage, group: {}", group); - capacityService.correctGroupUsage(group); - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] correctUsage ", e); - } - } + /** + * usage计数器服务:无论容量管理的限制检验功能是否开启,都会进行计数 + */ + private void insertOrUpdateUsage(String group, String tenant, CounterMode counterMode, boolean hasTenant) { + try { + capacityService.insertAndUpdateClusterUsage(counterMode, true); + if (hasTenant) { + capacityService.insertAndUpdateTenantUsage(counterMode, tenant, true); + } else { + capacityService.insertAndUpdateGroupUsage(counterMode, group, true); + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] insertOrUpdateUsage ", e); + } + } - private Object getResult(ProceedingJoinPoint pjp, HttpServletResponse response, String group, String tenant, - CounterMode counterMode, boolean hasTenant) throws Throwable { - try { - // 执行实际操作 - Object result = pjp.proceed(); - // 根据执行结果判定是否需要回滚 - doResult(counterMode, response, group, tenant, result, hasTenant); - return result; - } catch (Throwable throwable) { - LOGGER.warn("[capacityManagement] inner operation throw exception, rollback, group: {}, tenant: {}", - group, tenant, throwable); - rollback(counterMode, group, tenant, hasTenant); - throw throwable; - } - } + private LimitType getLimitType(CounterMode counterMode, String group, String tenant, String content, boolean + hasTenant) { + try { + boolean clusterLimited = !capacityService.insertAndUpdateClusterUsage(counterMode, false); + if (clusterLimited) { + LOGGER.warn("[capacityManagement] cluster capacity reaches quota."); + return LimitType.OVER_CLUSTER_QUOTA; + } + if (content == null) { + return null; + } + int currentSize = getCurrentSize(content); + LimitType limitType = getGroupOrTenantLimitType(counterMode, group, tenant, currentSize, + hasTenant); + if (limitType != null) { + rollbackClusterUsage(counterMode); + return limitType; + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] isLimited ", e); + } + return null; + } - /** - * usage计数器服务:无论容量管理的限制检验功能是否开启,都会进行计数 - */ - private void insertOrUpdateUsage(String group, String tenant, CounterMode counterMode, boolean hasTenant) { - try { - capacityService.insertAndUpdateClusterUsage(counterMode, true); - if (hasTenant) { - capacityService.insertAndUpdateTenantUsage(counterMode, tenant, true); - } else { - capacityService.insertAndUpdateGroupUsage(counterMode, group, true); - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] insertOrUpdateUsage ", e); - } - } + /** + * 编码字节数 + */ + private int getCurrentSize(String content) { + try { + return content.getBytes(Charset.forName(Constants.ENCODE)).length; + } catch (Exception e) { + LOGGER.error("[capacityManagement] getCurrentSize ", e); + } + return 0; + } - private LimitType getLimitType(CounterMode counterMode, String group, String tenant, String content, boolean - hasTenant) { - try { - boolean clusterLimited = !capacityService.insertAndUpdateClusterUsage(counterMode, false); - if (clusterLimited) { - LOGGER.warn("[capacityManagement] cluster capacity reaches quota."); - return LimitType.OVER_CLUSTER_QUOTA; - } - if (content == null) { - return null; - } - int currentSize = getCurrentSize(content); - LimitType limitType = getGroupOrTenantLimitType(counterMode, group, tenant, currentSize, - hasTenant); - if (limitType != null) { - rollbackClusterUsage(counterMode); - return limitType; - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] isLimited ", e); - } - return null; - } + private LimitType getGroupOrTenantLimitType(CounterMode counterMode, String group, String tenant, + int currentSize, boolean hasTenant) { + if (group == null) { + return null; + } + Capacity capacity = getCapacity(group, tenant, hasTenant); + if (isSizeLimited(group, tenant, currentSize, hasTenant, false, capacity)) { + return LimitType.OVER_MAX_SIZE; + } + if (capacity == null) { + insertCapacity(group, tenant, hasTenant); + } + boolean updateSuccess = isUpdateSuccess(counterMode, group, tenant, hasTenant); + if (updateSuccess) { + return null; + } + if (hasTenant) { + return LimitType.OVER_TENANT_QUOTA; + } + return LimitType.OVER_GROUP_QUOTA; + } - /** - * 编码字节数 - */ - private int getCurrentSize(String content) { - try { - return content.getBytes(Charset.forName(Constants.ENCODE)).length; - } catch (Exception e) { - LOGGER.error("[capacityManagement] getCurrentSize ", e); - } - return 0; - } + private boolean isUpdateSuccess(CounterMode counterMode, String group, String tenant, boolean hasTenant) { + boolean updateSuccess; + if (hasTenant) { + updateSuccess = capacityService.updateTenantUsage(counterMode, tenant); + if (!updateSuccess) { + LOGGER.warn("[capacityManagement] tenant capacity reaches quota, tenant: {}", tenant); + } + } else { + updateSuccess = capacityService.updateGroupUsage(counterMode, group); + if (!updateSuccess) { + LOGGER.warn("[capacityManagement] group capacity reaches quota, group: {}", group); + } + } + return updateSuccess; + } - private LimitType getGroupOrTenantLimitType(CounterMode counterMode, String group, String tenant, - int currentSize, boolean hasTenant) { - if (group == null) { - return null; - } - Capacity capacity = getCapacity(group, tenant, hasTenant); - if (isSizeLimited(group, tenant, currentSize, hasTenant, false, capacity)) { - return LimitType.OVER_MAX_SIZE; - } - if (capacity == null) { - insertCapacity(group, tenant, hasTenant); - } - boolean updateSuccess = isUpdateSuccess(counterMode, group, tenant, hasTenant); - if (updateSuccess) { - return null; - } - if (hasTenant) { - return LimitType.OVER_TENANT_QUOTA; - } - return LimitType.OVER_GROUP_QUOTA; - } + private void insertCapacity(String group, String tenant, boolean hasTenant) { + if (hasTenant) { + capacityService.initTenantCapacity(tenant); + } else { + capacityService.initGroupCapacity(group); + } + } - private boolean isUpdateSuccess(CounterMode counterMode, String group, String tenant, boolean hasTenant) { - boolean updateSuccess; - if (hasTenant) { - updateSuccess = capacityService.updateTenantUsage(counterMode, tenant); - if (!updateSuccess) { - LOGGER.warn("[capacityManagement] tenant capacity reaches quota, tenant: {}", tenant); - } - } else { - updateSuccess = capacityService.updateGroupUsage(counterMode, group); - if (!updateSuccess) { - LOGGER.warn("[capacityManagement] group capacity reaches quota, group: {}", group); - } - } - return updateSuccess; - } + private Capacity getCapacity(String group, String tenant, boolean hasTenant) { + Capacity capacity; + if (hasTenant) { + capacity = capacityService.getTenantCapacity(tenant); + } else { + capacity = capacityService.getGroupCapacity(group); + } + return capacity; + } - private void insertCapacity(String group, String tenant, boolean hasTenant) { - if (hasTenant) { - capacityService.initTenantCapacity(tenant); - } else { - capacityService.initGroupCapacity(group); - } - } + private boolean isSizeLimited(String group, String tenant, int currentSize, boolean hasTenant, boolean isAggr, + Capacity capacity) { + int defaultMaxSize = getDefaultMaxSize(isAggr); + if (capacity != null) { + Integer maxSize = getMaxSize(isAggr, capacity); + if (maxSize == 0) { + // 已经存在容量信息记录,maxSize=0,则使用"默认maxSize限制值"进行比较 + return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant); + } + // 已经存在容量信息记录,maxSize!=0 + return isOverSize(group, tenant, currentSize, maxSize, hasTenant); + } + // 不已经存在容量信息记录,使用"默认maxSize限制值"进行比较 + return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant); + } - private Capacity getCapacity(String group, String tenant, boolean hasTenant) { - Capacity capacity; - if (hasTenant) { - capacity = capacityService.getTenantCapacity(tenant); - } else { - capacity = capacityService.getGroupCapacity(group); - } - return capacity; - } + private Integer getMaxSize(boolean isAggr, Capacity capacity) { + if (isAggr) { + return capacity.getMaxAggrSize(); + } + return capacity.getMaxSize(); + } - private boolean isSizeLimited(String group, String tenant, int currentSize, boolean hasTenant, boolean isAggr, - Capacity capacity) { - int defaultMaxSize = getDefaultMaxSize(isAggr); - if (capacity != null) { - Integer maxSize = getMaxSize(isAggr, capacity); - if (maxSize == 0) { - // 已经存在容量信息记录,maxSize=0,则使用"默认maxSize限制值"进行比较 - return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant); - } - // 已经存在容量信息记录,maxSize!=0 - return isOverSize(group, tenant, currentSize, maxSize, hasTenant); - } - // 不已经存在容量信息记录,使用"默认maxSize限制值"进行比较 - return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant); - } + private int getDefaultMaxSize(boolean isAggr) { + if (isAggr) { + return PropertyUtil.getDefaultMaxAggrSize(); + } + return PropertyUtil.getDefaultMaxSize(); + } - private Integer getMaxSize(boolean isAggr, Capacity capacity) { - if (isAggr) { - return capacity.getMaxAggrSize(); - } - return capacity.getMaxSize(); - } + private boolean isOverSize(String group, String tenant, int currentSize, int maxSize, boolean hasTenant) { + if (currentSize > maxSize) { + if (hasTenant) { + LOGGER.warn( + "[capacityManagement] tenant content is over maxSize, tenant: {}, maxSize: {}, currentSize: {}", + tenant, maxSize, currentSize); + } else { + LOGGER.warn( + "[capacityManagement] group content is over maxSize, group: {}, maxSize: {}, currentSize: {}", + group, maxSize, currentSize); + } + return true; + } + return false; + } - private int getDefaultMaxSize(boolean isAggr) { - if (isAggr) { - return PropertyUtil.getDefaultMaxAggrSize(); - } - return PropertyUtil.getDefaultMaxSize(); - } + private void doResult(CounterMode counterMode, HttpServletResponse response, String group, + String tenant, Object result, boolean hasTenant) { + try { + if (!isSuccess(response, result)) { + LOGGER.warn( + "[capacityManagement] inner operation is fail, rollback, counterMode: {}, group: {}, tenant: {}", + counterMode, group, tenant); + rollback(counterMode, group, tenant, hasTenant); + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] doResult ", e); + } + } - private boolean isOverSize(String group, String tenant, int currentSize, int maxSize, boolean hasTenant) { - if (currentSize > maxSize) { - if (hasTenant) { - LOGGER.warn( - "[capacityManagement] tenant content is over maxSize, tenant: {}, maxSize: {}, currentSize: {}", - tenant, maxSize, currentSize); - } else { - LOGGER.warn( - "[capacityManagement] group content is over maxSize, group: {}, maxSize: {}, currentSize: {}", - group, maxSize, currentSize); - } - return true; - } - return false; - } + private boolean isSuccess(HttpServletResponse response, Object result) { + int status = response.getStatus(); + if (status == HttpServletResponse.SC_OK) { + return true; + } + LOGGER.warn("[capacityManagement] response status is not 200, status: {}, result: {}", status, + result); + return false; + } - private void doResult(CounterMode counterMode, HttpServletResponse response, String group, - String tenant, Object result, boolean hasTenant) { - try { - if (!isSuccess(response, result)) { - LOGGER.warn( - "[capacityManagement] inner operation is fail, rollback, counterMode: {}, group: {}, tenant: {}", - counterMode, group, tenant); - rollback(counterMode, group, tenant, hasTenant); - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] doResult ", e); - } - } + private void rollback(CounterMode counterMode, String group, String tenant, boolean hasTenant) { + try { + rollbackClusterUsage(counterMode); + if (hasTenant) { + capacityService.updateTenantUsage(counterMode.reverse(), tenant); + } else { + capacityService.updateGroupUsage(counterMode.reverse(), group); + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] rollback ", e); + } + } - private boolean isSuccess(HttpServletResponse response, Object result) { - int status = response.getStatus(); - if (status == HttpServletResponse.SC_OK) { - return true; - } - LOGGER.warn("[capacityManagement] response status is not 200, status: {}, result: {}", status, - result); - return false; - } + private void rollbackClusterUsage(CounterMode counterMode) { + try { + if (!capacityService.updateClusterUsage(counterMode.reverse())) { + LOGGER.error("[capacityManagement] cluster usage rollback fail counterMode: {}", counterMode); + } + } catch (Exception e) { + LOGGER.error("[capacityManagement] rollback ", e); + } + } - private void rollback(CounterMode counterMode, String group, String tenant, boolean hasTenant) { - try { - rollbackClusterUsage(counterMode); - if (hasTenant) { - capacityService.updateTenantUsage(counterMode.reverse(), tenant); - } else { - capacityService.updateGroupUsage(counterMode.reverse(), group); - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] rollback ", e); - } - } + /** + * limit tyep + * + * @author Nacos + */ + public enum LimitType { + /** + * over limit + */ + OVER_CLUSTER_QUOTA("超过集群配置个数上限", 429), + OVER_GROUP_QUOTA("超过该Group配置个数上限", 429), + OVER_TENANT_QUOTA("超过该租户配置个数上限", 429), + OVER_MAX_SIZE("超过配置的内容大小上限", 429); + public final String description; + public final int status; - private void rollbackClusterUsage(CounterMode counterMode) { - try { - if (!capacityService.updateClusterUsage(counterMode.reverse())) { - LOGGER.error("[capacityManagement] cluster usage rollback fail counterMode: {}", counterMode); - } - } catch (Exception e) { - LOGGER.error("[capacityManagement] rollback ", e); - } - } - - /** - * limit tyep - * - * @author Nacos - * - */ - public enum LimitType { - /** - * over limit - */ - OVER_CLUSTER_QUOTA("超过集群配置个数上限", 429), - OVER_GROUP_QUOTA("超过该Group配置个数上限", 429), - OVER_TENANT_QUOTA("超过该租户配置个数上限", 429), - OVER_MAX_SIZE("超过配置的内容大小上限", 429), - OVER_MAX_AGGR_COUNT("超过聚合子配置个数上限", 429), - OVER_MAX_AGGR_SIZE("超过聚合数据子配置的内容大小上限", 429); - public final String description; - public final int status; - - LimitType(String description, int status) { - this.description = description; - this.status = status; - } - } + LimitType(String description, int status) { + this.description = description; + this.status = status; + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java b/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java index eb21966d8..4e7e60f83 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java @@ -15,90 +15,94 @@ */ package com.alibaba.nacos.config.server.aspect; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; - import com.alibaba.nacos.config.server.service.ConfigService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5; import com.alibaba.nacos.config.server.utils.RequestUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** - * * Created with IntelliJ IDEA. User: dingjoey Date: 13-12-12 Time: 21:12 - * client api && sdk api 请求日志打点逻辑 - * - * @author Nacos + * * Created with IntelliJ IDEA. User: dingjoey Date: 13-12-12 Time: 21:12 client api && sdk api 请求日志打点逻辑 * + * @author Nacos */ @Aspect public class RequestLogAspect { - /** - * publish config - */ - public static final String CLIENT_INTERFACE_PUBLISH_SINGLE_CONFIG = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.publishConfig(..)) && args(request,response,dataId,group,tenant,content,..)"; + /** + * publish config + */ + private static final String CLIENT_INTERFACE_PUBLISH_SINGLE_CONFIG + = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.publishConfig(..)) && args" + + "(request,response,dataId,group,tenant,content,..)"; - /** - * get config - */ - public static final String CLIENT_INTERFACE_GET_CONFIG = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.getConfig(..)) && args(request,response,dataId,group,tenant,..)"; + /** + * get config + */ + private static final String CLIENT_INTERFACE_GET_CONFIG + = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.getConfig(..)) && args(request," + + "response,dataId,group,tenant,..)"; - /** - * remove config - */ - public static final String CLIENT_INTERFACE_REMOVE_ALL_CONFIG = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.deleteConfig(..)) && args(request,response,dataId,group,..)"; + /** + * remove config + */ + private static final String CLIENT_INTERFACE_REMOVE_ALL_CONFIG + = "execution(* com.alibaba.nacos.config.server.controller.ConfigController.deleteConfig(..)) && args(request," + + "response,dataId,group,..)"; - /** - * publishSingle - */ - @Around(CLIENT_INTERFACE_PUBLISH_SINGLE_CONFIG) - public Object interfacePublishSingle(ProceedingJoinPoint pjp, HttpServletRequest request, - HttpServletResponse response, String dataId, String group, String tenant, String content) throws Throwable { - final String md5 = content == null ? null : MD5.getInstance().getMD5String(content); - return logClientRequest("publish", pjp, request, response, dataId, group, tenant, md5); - } + /** + * publishSingle + */ + @Around(CLIENT_INTERFACE_PUBLISH_SINGLE_CONFIG) + public Object interfacePublishSingle(ProceedingJoinPoint pjp, HttpServletRequest request, + HttpServletResponse response, String dataId, String group, String tenant, + String content) throws Throwable { + final String md5 = content == null ? null : MD5.getInstance().getMD5String(content); + return logClientRequest("publish", pjp, request, response, dataId, group, tenant, md5); + } - /** - * removeAll - */ - @Around(CLIENT_INTERFACE_REMOVE_ALL_CONFIG) - public Object interfaceRemoveAll(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, - String dataId, String group, String tenant) throws Throwable { - return logClientRequest("remove", pjp, request, response, dataId, group, tenant, null); - } + /** + * removeAll + */ + @Around(CLIENT_INTERFACE_REMOVE_ALL_CONFIG) + public Object interfaceRemoveAll(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, + String dataId, String group, String tenant) throws Throwable { + return logClientRequest("remove", pjp, request, response, dataId, group, tenant, null); + } - /** - * getConfig - */ - @Around(CLIENT_INTERFACE_GET_CONFIG) - public Object interfaceGetConfig(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, - String dataId, String group, String tenant) throws Throwable { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - final String md5 = ConfigService.getContentMd5(groupKey); - return logClientRequest("get", pjp, request, response, dataId, group, tenant, md5); - } + /** + * getConfig + */ + @Around(CLIENT_INTERFACE_GET_CONFIG) + public Object interfaceGetConfig(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response, + String dataId, String group, String tenant) throws Throwable { + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + final String md5 = ConfigService.getContentMd5(groupKey); + return logClientRequest("get", pjp, request, response, dataId, group, tenant, md5); + } - /** - * client api request log rt | status | requestIp | opType | dataId | group - * | datumId | md5 - */ - private Object logClientRequest(String requestType, ProceedingJoinPoint pjp, HttpServletRequest request, - HttpServletResponse response, String dataId, String group, String tenant, String md5) throws Throwable { - final String requestIp = RequestUtil.getRemoteIp(request); - String appName = request.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); - final long st = System.currentTimeMillis(); - Object retVal = pjp.proceed(); - final long rt = System.currentTimeMillis() - st; - // rt | status | requestIp | opType | dataId | group | datumId | md5 | - // appName - LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}", - new Object[] { rt, retVal, requestIp, requestType, dataId, group, tenant, md5, appName }); - return retVal; - } + /** + * client api request log rt | status | requestIp | opType | dataId | group | datumId | md5 + */ + private Object logClientRequest(String requestType, ProceedingJoinPoint pjp, HttpServletRequest request, + HttpServletResponse response, String dataId, String group, String tenant, + String md5) throws Throwable { + final String requestIp = RequestUtil.getRemoteIp(request); + String appName = request.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); + final long st = System.currentTimeMillis(); + Object retVal = pjp.proceed(); + final long rt = System.currentTimeMillis() - st; + // rt | status | requestIp | opType | dataId | group | datumId | md5 | + // appName + LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}", rt, retVal, requestIp, requestType, dataId, group, tenant, + md5, appName); + return retVal; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/configuration/ClusterDataSourceConfiguration.java b/config/src/main/java/com/alibaba/nacos/config/server/configuration/ClusterDataSourceConfiguration.java new file mode 100644 index 000000000..c4a42d710 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/configuration/ClusterDataSourceConfiguration.java @@ -0,0 +1,198 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package com.alibaba.nacos.config.server.configuration; +// +//import com.alibaba.nacos.config.server.service.BasicDataSourceServiceImpl; +//import com.alibaba.nacos.config.server.service.TimerTaskService; +//import com.alibaba.nacos.config.server.utils.PropertyUtil; +//import org.apache.commons.dbcp.BasicDataSource; +//import org.apache.commons.lang.math.NumberUtils; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.context.annotation.Profile; +//import org.springframework.jdbc.core.JdbcTemplate; +//import org.springframework.jdbc.datasource.DataSourceTransactionManager; +//import org.springframework.transaction.support.TransactionTemplate; +// +//import javax.annotation.PostConstruct; +//import javax.sql.DataSource; +//import java.io.IOException; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.TimeUnit; +//import java.util.regex.Pattern; +// +//import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; +// +///** +// * Cluster {@link DataSource} {@link Configuration} +// * +// * @author Mercy +// * @since 0.2.2 +// */ +//@Profile("!standalone") +//@Configuration +//public class ClusterDataSourceConfiguration { +// +// private static final String JDBC_DRIVER_NAME = "com.mysql.jdbc.Driver"; +// +// /** +// * JDBC执行超时时间, 单位秒 +// */ +// private int queryTimeout = 3; +// +// private static final int TRANSACTION_QUERY_TIMEOUT = 5; +// +// private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error"; +// +// private List dataSourceList = new ArrayList(); +// private JdbcTemplate jt; +// private DataSourceTransactionManager tm; +// private TransactionTemplate tjt; +// +// private JdbcTemplate testMasterJT; +// private JdbcTemplate testMasterWritableJT; +// +// volatile private List testJTList; +// volatile private List isHealthList; +// private volatile int masterIndex; +// private static Pattern ipPattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); +// +// +// +// @PostConstruct +// public void init() { +// queryTimeout = NumberUtils +// .toInt(System.getProperty("QUERYTIMEOUT"), 3); +// jt = new JdbcTemplate(); +// /** +// * 设置最大记录数,防止内存膨胀 +// */ +// jt.setMaxRows(50000); +// jt.setQueryTimeout(queryTimeout); +// +// testMasterJT = new JdbcTemplate(); +// testMasterJT.setQueryTimeout(queryTimeout); +// +// testMasterWritableJT = new JdbcTemplate(); +// /** +// * 防止login接口因为主库不可用而rt太长 +// */ +// testMasterWritableJT.setQueryTimeout(1); +// /** +// * 数据库健康检测 +// */ +// testJTList = new ArrayList(); +// isHealthList = new ArrayList(); +// +// tm = new DataSourceTransactionManager(); +// tjt = new TransactionTemplate(tm); +// /** +// * 事务的超时时间需要与普通操作区分开 +// */ +// tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT); +// if (!STANDALONE_MODE) { +// try { +// reload(); +// } catch (IOException e) { +// e.printStackTrace(); +// throw new RuntimeException(DB_LOAD_ERROR_MSG); +// } +// +// TimerTaskService.scheduleWithFixedDelay(new BasicDataSourceServiceImpl.SelectMasterTask(), 10, 10, +// TimeUnit.SECONDS); +// TimerTaskService.scheduleWithFixedDelay(new BasicDataSourceServiceImpl.CheckDBHealthTask(), 10, 10, +// TimeUnit.SECONDS); +// } +// } +// +// public synchronized void reload() throws IOException { +// List dblist = new ArrayList(); +// try { +// String val = null; +// val = env.getProperty("db.num"); +// if (null == val) { +// throw new IllegalArgumentException("db.num is null"); +// } +// int dbNum = Integer.parseInt(val.trim()); +// +// for (int i = 0; i < dbNum; i++) { +// BasicDataSource ds = new BasicDataSource(); +// ds.setDriverClassName(JDBC_DRIVER_NAME); +// +// val = env.getProperty("db.url." + i); +// if (null == val) { +// fatalLog.error("db.url." + i + " is null"); +// throw new IllegalArgumentException(); +// } +// ds.setUrl(val.trim()); +// +// val = env.getProperty("db.user"); +// if (null == val) { +// fatalLog.error("db.user is null"); +// throw new IllegalArgumentException(); +// } +// ds.setUsername(val.trim()); +// +// val = env.getProperty("db.password"); +// if (null == val) { +// fatalLog.error("db.password is null"); +// throw new IllegalArgumentException(); +// } +// ds.setPassword(val.trim()); +// +// val = env.getProperty("db.initialSize"); +// ds.setInitialSize(Integer.parseInt(defaultIfNull(val, "10"))); +// +// val = env.getProperty("db.maxActive"); +// ds.setMaxActive(Integer.parseInt(defaultIfNull(val, "20"))); +// +// val = env.getProperty("db.maxIdle"); +// ds.setMaxIdle(Integer.parseInt(defaultIfNull(val, "50"))); +// +// ds.setMaxWait(3000L); +// ds.setPoolPreparedStatements(true); +// +// // 每10分钟检查一遍连接池 +// ds.setTimeBetweenEvictionRunsMillis(TimeUnit.MINUTES +// .toMillis(10L)); +// ds.setTestWhileIdle(true); +// ds.setValidationQuery("SELECT 1 FROM dual"); +// +// dblist.add(ds); +// +// JdbcTemplate jdbcTemplate = new JdbcTemplate(); +// jdbcTemplate.setQueryTimeout(queryTimeout); +// jdbcTemplate.setDataSource(ds); +// +// testJTList.add(jdbcTemplate); +// isHealthList.add(Boolean.TRUE); +// } +// +// if (dblist == null || dblist.size() == 0) { +// throw new RuntimeException("no datasource available"); +// } +// +// dataSourceList = dblist; +// new BasicDataSourceServiceImpl.SelectMasterTask().run(); +// new BasicDataSourceServiceImpl.CheckDBHealthTask().run(); +// } catch (RuntimeException e) { +// fatalLog.error(DB_LOAD_ERROR_MSG, e); +// throw new IOException(e); +// } finally { +// } +// } +//} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/configuration/DataBaseConfiguration.java b/config/src/main/java/com/alibaba/nacos/config/server/configuration/DataBaseConfiguration.java new file mode 100644 index 000000000..edb148950 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/configuration/DataBaseConfiguration.java @@ -0,0 +1,63 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package com.alibaba.nacos.config.server.configuration; +// +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.beans.factory.annotation.Qualifier; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.core.env.Environment; +//import org.springframework.jdbc.core.JdbcTemplate; +//import org.springframework.jdbc.datasource.DataSourceTransactionManager; +//import org.springframework.transaction.PlatformTransactionManager; +// +//import javax.sql.DataSource; +// +///** +// * DataBase {@link Configuration} +// * +// * @author Mercy +// * @since 0.2.2 +// */ +//@Configuration +//public class DataBaseConfiguration { +// +// /** +// * The bean name of {@link DataSource} for Nacos Config +// */ +// public static final String DATA_SOURCE_BEAN_NAME = "nacosConfigDataSource"; +// +// @Bean +// @Autowired +// public JdbcTemplate jdbcTemplate(@Qualifier(DATA_SOURCE_BEAN_NAME) DataSource dataSource, Environment environment) { +// JdbcTemplate jdbcTemplate = new JdbcTemplate(); +// jdbcTemplate = new JdbcTemplate(); +// jdbcTemplate.setMaxRows(50000); +// jdbcTemplate.setQueryTimeout(5000); +// jdbcTemplate.setDataSource(dataSource); +// return jdbcTemplate; +// } +// +// @Bean +// @Autowired +// public PlatformTransactionManager transactionManager(@Qualifier(DATA_SOURCE_BEAN_NAME) DataSource dataSource) { +// DataSourceTransactionManager manager = new DataSourceTransactionManager(); +// manager.setDataSource(dataSource); +// return manager; +// } +// +//} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/configuration/LocalDataSourceConfiguration.java b/config/src/main/java/com/alibaba/nacos/config/server/configuration/LocalDataSourceConfiguration.java new file mode 100644 index 000000000..f0c30bda3 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/configuration/LocalDataSourceConfiguration.java @@ -0,0 +1,75 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package com.alibaba.nacos.config.server.configuration; +// +//import org.apache.commons.dbcp.BasicDataSource; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.context.annotation.Profile; +//import org.springframework.core.env.Environment; +// +//import javax.sql.DataSource; +//import java.io.File; +//import java.util.concurrent.TimeUnit; +// +//import static com.alibaba.nacos.config.server.configuration.DataBaseConfiguration.DATA_SOURCE_BEAN_NAME; +// +///** +// * Local {@link DataSource} {@link Configuration} +// * +// * @author Mercy +// * @since 0.2.2 +// */ +//@Profile("standalone") +//@Configuration +//public class LocalDataSourceConfiguration { +// +// private static final String JDBC_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; +// +// private static final String DERBY_BASE_DIR = "data" + File.separator + "derby-data"; +// +// private static final String NACOS_HOME_PROPERTY_NAME = "nacos.home"; +// +// private static final String DEFAULT_APP_NAME = System.getProperty("user.home") + File.separator + "nacos"; +// +// private static final String APP_HOME = System.getProperty(NACOS_HOME_PROPERTY_NAME, DEFAULT_APP_NAME); +// +// private static final String DATA_SOURCE_URL = "jdbc:derby:" + APP_HOME + File.separator + DERBY_BASE_DIR + +// ";create=true"; +// +// private static final String USER_NAME = "nacos"; +// +// private static final String PASSWORD = "nacos"; +// +// @Bean(name = DATA_SOURCE_BEAN_NAME,destroyMethod = "close") +// public DataSource nacosConfigDataSource(Environment environment) { +// BasicDataSource ds = new BasicDataSource(); +// ds.setDriverClassName(JDBC_DRIVER_NAME); +// ds.setUrl(DATA_SOURCE_URL); +// ds.setUsername(USER_NAME); +// ds.setPassword(PASSWORD); +// ds.setInitialSize(environment.getProperty("db.initialSize", int.class, 20)); +// ds.setMaxActive(environment.getProperty("db.maxActive", int.class, 30)); +// ds.setMaxIdle(environment.getProperty("db.maxIdle", int.class, 50)); +// ds.setMaxWait(environment.getProperty("db.maxWait", long.class, 10000L)); +// ds.setPoolPreparedStatements(true); +// ds.setTimeBetweenEvictionRunsMillis(TimeUnit.MINUTES.toMillis(10L)); +// ds.setTestWhileIdle(true); +// return ds; +// } +// +//} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/configuration/NacosConfigConfiguration.java b/config/src/main/java/com/alibaba/nacos/config/server/configuration/NacosConfigConfiguration.java new file mode 100644 index 000000000..c2966ef26 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/configuration/NacosConfigConfiguration.java @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.config.server.configuration; + +import org.springframework.context.annotation.Configuration; + +/** + * Nacos Config {@link Configuration} includes required Spring components. + * + * @author Mercy + * @since 0.2.2 + */ +@Configuration +public class NacosConfigConfiguration { +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java b/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java index fae9f2c02..b36163990 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java @@ -17,8 +17,8 @@ package com.alibaba.nacos.config.server.constant; /** * Server Constants - * @author Nacos * + * @author Nacos */ public class Constants { @@ -27,21 +27,21 @@ public class Constants { public static final String CLIENT_VERSION = "3.0.0"; public static int DATA_IN_BODY_VERSION = 204; - + public static final String DEFAULT_GROUP = "DEFAULT_GROUP"; /** - * server端配置文件基目录 + * server端配置文件基目录 */ public static final String BASE_DIR = "config-data"; - + /** * server端配置文件备份目录 */ public static final String CONFIG_BAK_DIR = System.getProperty("user.home", "/home/admin") + "/nacos/bak_data"; public static final String APPNAME = "AppName"; - + public static final String UNKNOWN_APP = "UnknownApp"; public static final String DEFAULT_DOMAINNAME = "commonconfig.config-host.taobao.com"; @@ -79,7 +79,7 @@ public class Constants { /** * 秒 */ - public static final int ASYNC_UPDATE_ADDRESS_INTERVAL = 300; + public static final int ASYNC_UPDATE_ADDRESS_INTERVAL = 300; /** * 秒 */ @@ -101,24 +101,24 @@ public class Constants { */ public static final int RECV_WAIT_TIMEOUT = ONCE_TIMEOUT * 5; - public static final String BASE_PATH = "/v1/cs"; - - public static final String OPS_CONTROLLER_PATH = BASE_PATH + "/ops"; + public static final String BASE_PATH = "/v1/cs"; - public static final String CAPACITY_CONTROLLER_PATH = BASE_PATH + "/capacity"; - - public static final String COMMUNICATION_CONTROLLER_PATH = BASE_PATH + "/communication"; - - public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs"; - - public static final String HEALTH_CONTROLLER_PATH = BASE_PATH + "/health"; + public static final String OPS_CONTROLLER_PATH = BASE_PATH + "/ops"; - public static final String HISTORY_CONTROLLER_PATH = BASE_PATH + "/history"; + public static final String CAPACITY_CONTROLLER_PATH = BASE_PATH + "/capacity"; + + public static final String COMMUNICATION_CONTROLLER_PATH = BASE_PATH + "/communication"; + + public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs"; + + public static final String HEALTH_CONTROLLER_PATH = BASE_PATH + "/health"; + + public static final String HISTORY_CONTROLLER_PATH = BASE_PATH + "/history"; + + public static final String LISTENER_CONTROLLER_PATH = BASE_PATH + "/listener"; + + public static final String NAMESPACE_CONTROLLER_PATH = BASE_PATH + "/namespaces"; - public static final String LISTENER_CONTROLLER_PATH = BASE_PATH + "/listener"; - - public static final String NAMESPACE_CONTROLLER_PATH = BASE_PATH + "/namespaces"; - public static final String ENCODE = "UTF-8"; public static final String MAP_FILE = "map-file.js"; @@ -129,18 +129,18 @@ public class Constants { public static final int FLOW_CONTROL_INTERVAL = 1000; - public static final String LINE_SEPARATOR = Character.toString((char) 1); + public static final String LINE_SEPARATOR = Character.toString((char)1); - public static final String WORD_SEPARATOR = Character.toString((char) 2); + public static final String WORD_SEPARATOR = Character.toString((char)2); public static final String NACOS_LINE_SEPARATOR = "\r\n"; /** - * 从网络获取数据的总时间, 当超过此时间, 不再从网络获取数据, 单位ms + * 从网络获取数据的总时间, 当超过此时间, 不再从网络获取数据, 单位ms */ public static final long TOTALTIME_FROM_SERVER = 10000; /** - * 从网络获取数据的总时间的失效时间, 单位ms + * 从网络获取数据的总时间的失效时间, 单位ms */ public static final long TOTALTIME_INVALID_THRESHOLD = 60000; @@ -148,27 +148,27 @@ public class Constants { * 批量操作时, 单条数据的状态码 */ /** - * 发生异常 + * 发生异常 */ public static final int BATCH_OP_ERROR = -1; public static final String BATCH_OP_ERROR_IO_MSG = "get config dump error"; public static final String BATCH_OP_ERROR_CONFLICT_MSG = "config get conflicts"; /** - * 查询成功, 数据存在 + * 查询成功, 数据存在 */ public static final int BATCH_QUERY_EXISTS = 1; public static final String BATCH_QUERY_EXISTS_MSG = "config exits"; /** - * 查询成功, 数据不存在 + * 查询成功, 数据不存在 */ public static final int BATCH_QUERY_NONEXISTS = 2; public static final String BATCH_QUERY_NONEEXISTS_MSG = "config not exits"; /** - * 新增成功 + * 新增成功 */ public static final int BATCH_ADD_SUCCESS = 3; /** - * 更新成功 + * 更新成功 */ public static final int BATCH_UPDATE_SUCCESS = 4; @@ -180,20 +180,20 @@ public class Constants { public static final int MAX_ADDACK_COUNT = 5; /** - * 数据的初始版本号 + * 数据的初始版本号 */ public static final int FIRST_VERSION = 1; /** - * 数据被删除的标识版本号 + * 数据被删除的标识版本号 */ public static final int POISON_VERSION = -1; /** - * 写磁盘文件时, 临时版本号 + * 写磁盘文件时, 临时版本号 */ public static final int TEMP_VERSION = 0; - /** - * 获取数据的顺序:容灾文件-> 服务器 -> 本地缓存 - */ + /** + * 获取数据的顺序:容灾文件-> 服务器 -> 本地缓存 + */ public static final int GETCONFIG_LOCAL_SERVER_SNAPSHOT = 1; /** * 获取数据的顺序:容灾文件-> 本地缓存 -> 服务器 @@ -204,13 +204,13 @@ public class Constants { public static final String CLIENT_REQUEST_TS_HEADER = "Client-RequestTS"; public static final String CLIENT_REQUEST_TOKEN_HEADER = "Client-RequestToken"; /** - * client, sdk请求server服务的身份 + * client, sdk请求server服务的身份 */ - public static final String REQUEST_IDENTITY = "Request-Identity"; + public static final String REQUEST_IDENTITY = "Request-Identity"; /** - * 鉴权结果信息 + * 鉴权结果信息 */ - public static final String ACL_RESPONSE = "ACL-Response"; - + public static final String ACL_RESPONSE = "ACL-Response"; + public static final int ATOMIC_MAX_SIZE = 1000; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/constant/CounterMode.java b/config/src/main/java/com/alibaba/nacos/config/server/constant/CounterMode.java index 988e7d46b..13f5a26fd 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/constant/CounterMode.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/constant/CounterMode.java @@ -17,24 +17,24 @@ package com.alibaba.nacos.config.server.constant; /** * counter mode - * + * * @author hexu.hxy * @date 2018/3/13 */ public enum CounterMode { - /** - * 增加 - */ - INCREMENT, - /** - * 减少 - */ - DECREMENT; + /** + * 增加 + */ + INCREMENT, + /** + * 减少 + */ + DECREMENT; - public CounterMode reverse() { - if (INCREMENT == this) { - return DECREMENT; - } - return INCREMENT; - } + public CounterMode reverse() { + if (INCREMENT == this) { + return DECREMENT; + } + return INCREMENT; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/CapacityController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/CapacityController.java index 1c52b6c10..632cef6bb 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/CapacityController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/CapacityController.java @@ -15,9 +15,11 @@ */ package com.alibaba.nacos.config.server.controller; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.model.RestResult; +import com.alibaba.nacos.config.server.model.capacity.Capacity; +import com.alibaba.nacos.config.server.service.capacity.CapacityService; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,134 +29,133 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.model.RestResult; -import com.alibaba.nacos.config.server.model.capacity.Capacity; -import com.alibaba.nacos.config.server.service.capacity.CapacityService; +import javax.servlet.http.HttpServletResponse; /** - * capcity manage - * + * Capacity Management + * * @author hexu.hxy */ @Controller @RequestMapping(Constants.CAPACITY_CONTROLLER_PATH) public class CapacityController { - private static final Logger LOGGER = LoggerFactory.getLogger(CapacityController.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CapacityController.class); - @Autowired - private CapacityService capacityService; + private final CapacityService capacityService; - @ResponseBody - @RequestMapping(method = RequestMethod.GET) - public RestResult getCapacity(HttpServletResponse response, - @RequestParam(required = false) String group, - @RequestParam(required = false) String tenant) { - if (group == null && tenant == null) { - RestResult restResult = new RestResult(); - response.setStatus(400); - restResult.setCode(400); - restResult.setMessage("参数group和tenant不能同时为空"); - return restResult; - } - if (group == null && StringUtils.isBlank(tenant)) { - RestResult restResult = new RestResult(); - response.setStatus(400); - restResult.setCode(400); - restResult.setMessage("tenant不能为空字符串"); - return restResult; - } - RestResult restResult = new RestResult(); - try { - response.setStatus(200); - restResult.setCode(200); - Capacity capacity = capacityService.getCapacityWithDefault(group, tenant); - if (capacity == null) { - LOGGER.warn("[getCapacity] capacity不存在,需初始化 group: {}, tenant: {}", group, tenant); - capacityService.initCapacity(group, tenant); - capacity = capacityService.getCapacityWithDefault(group, tenant); - } - if (capacity != null) { - restResult.setData(capacity); - } - } catch (Exception e) { - LOGGER.error("[getCapacity] ", e); - response.setStatus(500); - restResult.setCode(500); - restResult.setMessage(e.getMessage()); - } - return restResult; - } + @Autowired + public CapacityController(CapacityService capacityService) {this.capacityService = capacityService;} - /** - * 修改Group或租户的容量,容量信息还没有初始化的则初始化记录 - */ - @ResponseBody - @RequestMapping(method = RequestMethod.POST) - public RestResult updateCapacity(HttpServletResponse response, - @RequestParam(required = false) String group, - @RequestParam(required = false) String tenant, - @RequestParam(required = false) Integer quota, - @RequestParam(required = false) Integer maxSize, - @RequestParam(required = false) Integer maxAggrCount, - @RequestParam(required = false) Integer maxAggrSize) { - if (StringUtils.isBlank(group) && StringUtils.isBlank(tenant)) { - capacityService.initAllCapacity(); - RestResult restResult = new RestResult(); - setFailResult(response, restResult, 400); - restResult.setMessage("参数group和tenant不能同时为空"); - return restResult; - } - if (quota == null && maxSize == null && maxAggrCount == null && maxAggrSize == null) { - RestResult restResult = new RestResult(); - setFailResult(response, restResult, 400); - restResult.setMessage("参数quota、maxSize、maxAggrCount、maxAggrSize不能同时为空"); - return restResult; - } - String targetFieldName; - String targetFieldValue; - if (tenant == null) { - targetFieldName = "group"; - targetFieldValue = group; - } else { - targetFieldName = "tenant"; - targetFieldValue = tenant; - } - RestResult restResult = new RestResult(); - if (StringUtils.isBlank(targetFieldValue)) { - setFailResult(response, restResult, 400); - restResult.setMessage(String.format("参数%s为空", targetFieldName)); - return restResult; - } - try { - boolean insertOrUpdateResult = capacityService.insertOrUpdateCapacity(group, tenant, quota, maxSize, - maxAggrCount, maxAggrSize); - if (insertOrUpdateResult) { - setSuccessResult(response, restResult); - restResult.setMessage(String.format("成功更新%s为%s的容量信息配置", targetFieldName, targetFieldValue)); - return restResult; - } - setFailResult(response, restResult, 500); - restResult.setMessage(String.format("%s为%s的容量信息配置更新失败", targetFieldName, targetFieldValue)); - return restResult; - } catch (Exception e) { - LOGGER.error("[updateCapacity] ", e); - setFailResult(response, restResult, 500); - restResult.setMessage(e.getMessage()); - return restResult; - } - } + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public RestResult getCapacity(HttpServletResponse response, + @RequestParam(required = false) String group, + @RequestParam(required = false) String tenant) { + if (group == null && tenant == null) { + RestResult restResult = new RestResult(); + response.setStatus(400); + restResult.setCode(400); + restResult.setMessage("参数group和tenant不能同时为空"); + return restResult; + } + if (group == null && StringUtils.isBlank(tenant)) { + RestResult restResult = new RestResult(); + response.setStatus(400); + restResult.setCode(400); + restResult.setMessage("tenant不能为空字符串"); + return restResult; + } + RestResult restResult = new RestResult(); + try { + response.setStatus(200); + restResult.setCode(200); + Capacity capacity = capacityService.getCapacityWithDefault(group, tenant); + if (capacity == null) { + LOGGER.warn("[getCapacity] capacity不存在,需初始化 group: {}, tenant: {}", group, tenant); + capacityService.initCapacity(group, tenant); + capacity = capacityService.getCapacityWithDefault(group, tenant); + } + if (capacity != null) { + restResult.setData(capacity); + } + } catch (Exception e) { + LOGGER.error("[getCapacity] ", e); + response.setStatus(500); + restResult.setCode(500); + restResult.setMessage(e.getMessage()); + } + return restResult; + } - private void setFailResult(HttpServletResponse response, RestResult restResult, int statusCode) { - response.setStatus(statusCode); - restResult.setCode(statusCode); - restResult.setData(false); - } + /** + * 修改Group或租户的容量,容量信息还没有初始化的则初始化记录 + */ + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public RestResult updateCapacity(HttpServletResponse response, + @RequestParam(required = false) String group, + @RequestParam(required = false) String tenant, + @RequestParam(required = false) Integer quota, + @RequestParam(required = false) Integer maxSize, + @RequestParam(required = false) Integer maxAggrCount, + @RequestParam(required = false) Integer maxAggrSize) { + if (StringUtils.isBlank(group) && StringUtils.isBlank(tenant)) { + capacityService.initAllCapacity(); + RestResult restResult = new RestResult(); + setFailResult(response, restResult, 400); + restResult.setMessage("参数group和tenant不能同时为空"); + return restResult; + } + if (quota == null && maxSize == null && maxAggrCount == null && maxAggrSize == null) { + RestResult restResult = new RestResult(); + setFailResult(response, restResult, 400); + restResult.setMessage("参数quota、maxSize、maxAggrCount、maxAggrSize不能同时为空"); + return restResult; + } + String targetFieldName; + String targetFieldValue; + if (tenant == null) { + targetFieldName = "group"; + targetFieldValue = group; + } else { + targetFieldName = "tenant"; + targetFieldValue = tenant; + } + RestResult restResult = new RestResult(); + if (StringUtils.isBlank(targetFieldValue)) { + setFailResult(response, restResult, 400); + restResult.setMessage(String.format("参数%s为空", targetFieldName)); + return restResult; + } + try { + boolean insertOrUpdateResult = capacityService.insertOrUpdateCapacity(group, tenant, quota, maxSize, + maxAggrCount, maxAggrSize); + if (insertOrUpdateResult) { + setSuccessResult(response, restResult); + restResult.setMessage(String.format("成功更新%s为%s的容量信息配置", targetFieldName, targetFieldValue)); + return restResult; + } + setFailResult(response, restResult, 500); + restResult.setMessage(String.format("%s为%s的容量信息配置更新失败", targetFieldName, targetFieldValue)); + return restResult; + } catch (Exception e) { + LOGGER.error("[updateCapacity] ", e); + setFailResult(response, restResult, 500); + restResult.setMessage(e.getMessage()); + return restResult; + } + } - private void setSuccessResult(HttpServletResponse response, RestResult restResult) { - response.setStatus(200); - restResult.setCode(200); - restResult.setData(true); - } + private void setFailResult(HttpServletResponse response, RestResult restResult, int statusCode) { + response.setStatus(statusCode); + restResult.setCode(statusCode); + restResult.setData(false); + } + + private void setSuccessResult(HttpServletResponse response, RestResult restResult) { + response.setStatus(200); + restResult.setCode(200); + restResult.setData(true); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/CommunicationController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/CommunicationController.java index f1f6651b2..e31ae75d5 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/CommunicationController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/CommunicationController.java @@ -15,13 +15,12 @@ */ package com.alibaba.nacos.config.server.controller; -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.model.SampleResult; +import com.alibaba.nacos.config.server.service.LongPollingService; +import com.alibaba.nacos.config.server.service.dump.DumpService; +import com.alibaba.nacos.config.server.service.notify.NotifyService; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -30,16 +29,12 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.model.SampleResult; -import com.alibaba.nacos.config.server.service.LongPullingService; -import com.alibaba.nacos.config.server.service.dump.DumpService; -import com.alibaba.nacos.config.server.service.notify.NotifyService; - +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * 用于其他节点通知的控制器 - * + * * @author boyan * @date 2010-5-7 */ @@ -47,66 +42,65 @@ import com.alibaba.nacos.config.server.service.notify.NotifyService; @RequestMapping(Constants.COMMUNICATION_CONTROLLER_PATH) public class CommunicationController { - @Autowired - private DumpService dumpService; + private final DumpService dumpService; + + private final LongPollingService longPollingService; + + private String trueStr = "true"; @Autowired - protected LongPullingService longPullingService; - - private String trueStr = "true"; - + public CommunicationController(DumpService dumpService, LongPollingService longPollingService) { + this.dumpService = dumpService; + this.longPollingService = longPollingService; + } + /** * 通知配置信息改变 - * */ - @RequestMapping(value="/dataChange", method = RequestMethod.GET) - @ResponseBody - public Boolean notifyConfigInfo(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - @RequestParam(value = "tag", required = false) String tag) { - dataId = dataId.trim(); - group = group.trim(); - String lastModified = request.getHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED); - long lastModifiedTs = StringUtils.isEmpty(lastModified) ? -1 : Long.parseLong(lastModified); - String handleIp = request.getHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP); - String isBetaStr = request.getHeader("isBeta"); - if (StringUtils.isNotBlank(isBetaStr) && trueStr.equals(isBetaStr)) { - dumpService.dump(dataId, group, tenant, lastModifiedTs, handleIp, true); - } else { - dumpService.dump(dataId, group, tenant, tag, lastModifiedTs, handleIp); - } - return true; - } - + @RequestMapping(value = "/dataChange", method = RequestMethod.GET) + @ResponseBody + public Boolean notifyConfigInfo(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) + String tenant, + @RequestParam(value = "tag", required = false) String tag) { + dataId = dataId.trim(); + group = group.trim(); + String lastModified = request.getHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED); + long lastModifiedTs = StringUtils.isEmpty(lastModified) ? -1 : Long.parseLong(lastModified); + String handleIp = request.getHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP); + String isBetaStr = request.getHeader("isBeta"); + if (StringUtils.isNotBlank(isBetaStr) && trueStr.equals(isBetaStr)) { + dumpService.dump(dataId, group, tenant, lastModifiedTs, handleIp, true); + } else { + dumpService.dump(dataId, group, tenant, tag, lastModifiedTs, handleIp); + } + return true; + } + /** - * 在本台机器上获得订阅改配置的客户端信息 - */ - @RequestMapping(value="/configWatchers", method = RequestMethod.GET) - @ResponseBody - public SampleResult getSubClientConfig(HttpServletRequest request, - HttpServletResponse response, - @RequestParam("dataId") String dataId, - @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false) String tenant, - ModelMap modelMap) - throws IOException, ServletException, Exception { - group = StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group; - SampleResult sampleResult = longPullingService.getCollectSubscribleInfo(dataId, group, tenant); - return sampleResult; - } - - /** - * 在本台机器上获得客户端监听的配置列表 - */ - @RequestMapping(value= "/watcherConfigs", method = RequestMethod.GET) - @ResponseBody - public SampleResult getSubClientConfigByIp(HttpServletRequest request, - HttpServletResponse response, - @RequestParam("ip") String ip, - ModelMap modelMap) - throws IOException, ServletException, Exception { - SampleResult sampleResult = longPullingService.getCollectSubscribleInfoByIp(ip); - return sampleResult; - } + * 在本台机器上获得订阅改配置的客户端信息 + */ + @RequestMapping(value = "/configWatchers", method = RequestMethod.GET) + @ResponseBody + public SampleResult getSubClientConfig(HttpServletRequest request, + HttpServletResponse response, + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false) String tenant, + ModelMap modelMap) { + group = StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group; + return longPollingService.getCollectSubscribleInfo(dataId, group, tenant); + } + + /** + * 在本台机器上获得客户端监听的配置列表 + */ + @RequestMapping(value = "/watcherConfigs", method = RequestMethod.GET) + @ResponseBody + public SampleResult getSubClientConfigByIp(HttpServletRequest request, + HttpServletResponse response, @RequestParam("ip") String ip, + ModelMap modelMap) { + return longPollingService.getCollectSubscribleInfoByIp(ip); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java index 922ea7db4..8e592568c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java @@ -15,373 +15,371 @@ */ package com.alibaba.nacos.config.server.controller; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.exception.NacosException; +import com.alibaba.nacos.config.server.model.*; +import com.alibaba.nacos.config.server.service.AggrWhitelist; +import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent; +import com.alibaba.nacos.config.server.service.ConfigSubService; +import com.alibaba.nacos.config.server.service.PersistService; +import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; +import com.alibaba.nacos.config.server.utils.*; +import com.alibaba.nacos.config.server.utils.event.EventDispatcher; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLDecoder; import java.sql.Timestamp; import java.util.HashMap; import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.exception.NacosException; -import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; -import com.alibaba.nacos.config.server.model.ConfigAllInfo; -import com.alibaba.nacos.config.server.model.ConfigInfo; -import com.alibaba.nacos.config.server.model.ConfigInfo4Beta; -import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus; -import com.alibaba.nacos.config.server.model.Page; -import com.alibaba.nacos.config.server.model.RestResult; -import com.alibaba.nacos.config.server.model.SampleResult; -import com.alibaba.nacos.config.server.service.AggrWhitelist; -import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent; -import com.alibaba.nacos.config.server.service.ConfigSubService; -import com.alibaba.nacos.config.server.service.PersistService; -import com.alibaba.nacos.config.server.service.merge.MergeDatumService; -import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; -import com.alibaba.nacos.config.server.utils.MD5Util; -import com.alibaba.nacos.config.server.utils.ParamUtils; -import com.alibaba.nacos.config.server.utils.RequestUtil; -import com.alibaba.nacos.config.server.utils.SystemConfig; -import com.alibaba.nacos.config.server.utils.TimeUtils; -import com.alibaba.nacos.config.server.utils.event.EventDispatcher; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * 软负载客户端发布数据专用控制器 - * + * * @author leiwen - * */ @Controller @RequestMapping(Constants.CONFIG_CONTROLLER_PATH) -public class ConfigController extends HttpServlet { - /** - * uid - */ - private static final long serialVersionUID = 4339468526746635388L; - - private static final Logger log = LoggerFactory.getLogger(ConfigController.class); - +public class ConfigController { + + private static final Logger log = LoggerFactory.getLogger(ConfigController.class); + + private final transient ConfigServletInner inner; + + private final transient PersistService persistService; + + private final transient ConfigSubService configSubService; + @Autowired - private transient ConfigServletInner inner; - - @Autowired - private transient PersistService persistService; - - @Autowired - private transient MergeDatumService mergeService; + public ConfigController(ConfigServletInner configServletInner, PersistService persistService, + ConfigSubService configSubService) { + this.inner = configServletInner; + this.persistService = persistService; + this.configSubService = configSubService; + } - @Autowired - private transient ConfigSubService configSubService; - - /** - * 增加或更新非聚合数据。 - * - * @throws NacosException - */ - @RequestMapping(method = RequestMethod.POST) - @ResponseBody - public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - @RequestParam("content") String content, - @RequestParam(value = "tag", required = false) String tag, - @RequestParam(value = "appName", required = false) String appName, - @RequestParam(value = "src_user", required = false) String srcUser, - @RequestParam(value = "config_tags", required = false) String configTags, - @RequestParam(value = "desc", required = false) String desc, - @RequestParam(value = "use", required = false) String use, - @RequestParam(value = "effect", required = false) String effect, - @RequestParam(value = "type", required = false) String type, - @RequestParam(value = "schema", required = false) String schema) throws NacosException { - final String srcIp = RequestUtil.getRemoteIp(request); - String requestIpApp = RequestUtil.getAppName(request); - ParamUtils.checkParam(dataId, group, "datumId", content); - ParamUtils.checkParam(tag); + /** + * 增加或更新非聚合数据。 + * + * @throws NacosException + */ + @RequestMapping(method = RequestMethod.POST) + @ResponseBody + public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) + String tenant, + @RequestParam("content") String content, + @RequestParam(value = "tag", required = false) String tag, + @RequestParam(value = "appName", required = false) String appName, + @RequestParam(value = "src_user", required = false) String srcUser, + @RequestParam(value = "config_tags", required = false) String configTags, + @RequestParam(value = "desc", required = false) String desc, + @RequestParam(value = "use", required = false) String use, + @RequestParam(value = "effect", required = false) String effect, + @RequestParam(value = "type", required = false) String type, + @RequestParam(value = "schema", required = false) String schema) + throws NacosException { + final String srcIp = RequestUtil.getRemoteIp(request); + String requestIpApp = RequestUtil.getAppName(request); + ParamUtils.checkParam(dataId, group, "datumId", content); + ParamUtils.checkParam(tag); - Map configAdvanceInfo = new HashMap(10); - if (configTags != null) { - configAdvanceInfo.put("config_tags", configTags); - } - if (desc != null) { - configAdvanceInfo.put("desc", desc); - } - if (use != null) { - configAdvanceInfo.put("use", use); - } - if (effect != null) { - configAdvanceInfo.put("effect", effect); - } - if (type != null) { - configAdvanceInfo.put("type", type); - } - if (schema != null) { - configAdvanceInfo.put("schema", schema); - } - ParamUtils.checkParam(configAdvanceInfo); + Map configAdvanceInfo = new HashMap(10); + if (configTags != null) { + configAdvanceInfo.put("config_tags", configTags); + } + if (desc != null) { + configAdvanceInfo.put("desc", desc); + } + if (use != null) { + configAdvanceInfo.put("use", use); + } + if (effect != null) { + configAdvanceInfo.put("effect", effect); + } + if (type != null) { + configAdvanceInfo.put("type", type); + } + if (schema != null) { + configAdvanceInfo.put("schema", schema); + } + ParamUtils.checkParam(configAdvanceInfo); - if (AggrWhitelist.isAggrDataId(dataId)) { - log.warn("[aggr-conflict] {} attemp to publish single data, {}, {}", - new Object[] { RequestUtil.getRemoteIp(request), dataId, group }); - throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr"); - } + if (AggrWhitelist.isAggrDataId(dataId)) { + log.warn("[aggr-conflict] {} attemp to publish single data, {}, {}", + RequestUtil.getRemoteIp(request), dataId, group); + throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr"); + } - final Timestamp time = TimeUtils.getCurrentTime(); - String betaIps = request.getHeader("betaIps"); - ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); - if (StringUtils.isBlank(betaIps)) { - if (StringUtils.isBlank(tag)) { - persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, false); - EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime())); - } else { - persistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser, time, false); - EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); - } - } else { // beta publish - persistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser, time, false); - EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, time.getTime())); - } - ConfigTraceService.logPersistenceEvent(dataId, group, tenant, requestIpApp, time.getTime(), - SystemConfig.LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, content); + final Timestamp time = TimeUtils.getCurrentTime(); + String betaIps = request.getHeader("betaIps"); + ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); + if (StringUtils.isBlank(betaIps)) { + if (StringUtils.isBlank(tag)) { + persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, false); + EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime())); + } else { + persistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser, time, false); + EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); + } + } else { // beta publish + persistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser, time, false); + EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, time.getTime())); + } + ConfigTraceService.logPersistenceEvent(dataId, group, tenant, requestIpApp, time.getTime(), + LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, content); - return true; - } + return true; + } - /** - * 取数据 - * - * @throws ServletException - * @throws IOException - * @throws NacosException - */ - @RequestMapping(method = RequestMethod.GET) - public void getConfig(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - @RequestParam(value = "tag", required = false) String tag) - throws IOException, ServletException, NacosException { - // check params - ParamUtils.checkParam(dataId, group, "datumId", "content"); - ParamUtils.checkParam(tag); + /** + * 取数据 + * + * @throws ServletException + * @throws IOException + * @throws NacosException + */ + @RequestMapping(method = RequestMethod.GET) + public void getConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) + String tenant, + @RequestParam(value = "tag", required = false) String tag) + throws IOException, ServletException, NacosException { + // check params + ParamUtils.checkParam(dataId, group, "datumId", "content"); + ParamUtils.checkParam(tag); - final String clientIp = RequestUtil.getRemoteIp(request); - inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp); - } - - /** - * 取数据 - * - * @throws ServletException - * @throws IOException - * @throws NacosException - */ - @RequestMapping(params = "show=all", method = RequestMethod.GET) - @ResponseBody - public ConfigAllInfo detailConfigInfo(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant) - throws IOException, ServletException, NacosException { - // check params - ParamUtils.checkParam(dataId, group, "datumId", "content"); - return persistService.findConfigAllInfo(dataId, group, tenant); - } + final String clientIp = RequestUtil.getRemoteIp(request); + inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp); + } - /** - * 同步删除某个dataId下面所有的聚合前数据 - * - * @throws NacosException - */ - @RequestMapping(method = RequestMethod.DELETE) - @ResponseBody - public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, // - @RequestParam("group") String group, // - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - @RequestParam(value = "tag", required = false) String tag) throws NacosException { - ParamUtils.checkParam(dataId, group, "datumId", "rm"); - ParamUtils.checkParam(tag); - String clientIp = RequestUtil.getRemoteIp(request); - if (StringUtils.isBlank(tag)) { - persistService.removeAggrConfigInfo(dataId, group, tenant); - } - mergeService.addMergeTask(dataId, group, tenant, tag, clientIp); - return true; - } - - - @RequestMapping(value = "/catalog", method = RequestMethod.GET) - @ResponseBody - public RestResult getConfigAdvanceInfo(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - ModelMap modelMap) { - RestResult rr = new RestResult(); - ConfigAdvanceInfo configInfo = persistService.findConfigAdvanceInfo(dataId, group, tenant); - rr.setCode(200); - rr.setData(configInfo); - return rr; - } - - /** - * 比较MD5 - */ - @RequestMapping(value = "/listener", method = RequestMethod.POST) - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); - String probeModify = request.getParameter("Listening-Configs"); - if (StringUtils.isBlank(probeModify)) { - throw new IllegalArgumentException("invalid probeModify"); - } - - probeModify = URLDecoder.decode(probeModify, Constants.ENCODE); - - Map clientMd5Map = null; - try { - clientMd5Map = MD5Util.getClientMd5Map(probeModify); - } catch (Throwable e) { - throw new IllegalArgumentException("invalid probeModify"); - } + /** + * 取数据 + * + * @throws NacosException + */ + @RequestMapping(params = "show=all", method = RequestMethod.GET) + @ResponseBody + public ConfigAllInfo detailConfigInfo(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant) + throws NacosException { + // check params + ParamUtils.checkParam(dataId, group, "datumId", "content"); + return persistService.findConfigAllInfo(dataId, group, tenant); + } - // do long-polling - inner.doPollingConfig(request, response, clientMd5Map, probeModify.length()); - } - - /* - * 订阅改配置的客户端信息 - */ - @RequestMapping(value = "/listener", method = RequestMethod.GET) - @ResponseBody - public GroupkeyListenserStatus getListeners(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, @RequestParam("group") String group, - @RequestParam(value = "tenant", required = false) String tenant, - @RequestParam(value = "sampleTime", required = false, defaultValue = "1") int sampleTime, ModelMap modelMap) - throws IOException, ServletException, Exception { - group = StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group; - SampleResult collectSampleResult = configSubService.getCollectSampleResult(dataId, group, tenant, sampleTime); - GroupkeyListenserStatus gls = new GroupkeyListenserStatus(); - gls.setCollectStatus(200); - if (collectSampleResult.getLisentersGroupkeyStatus() != null) { - gls.setLisentersGroupkeyStatus(collectSampleResult.getLisentersGroupkeyStatus()); - } - return gls; - } + /** + * 同步删除某个dataId下面所有的聚合前数据 + * + * @throws NacosException + */ + @RequestMapping(method = RequestMethod.DELETE) + @ResponseBody + public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, // + @RequestParam("group") String group, // + @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) + String tenant, + @RequestParam(value = "tag", required = false) String tag) throws NacosException { + ParamUtils.checkParam(dataId, group, "datumId", "rm"); + ParamUtils.checkParam(tag); + String clientIp = RequestUtil.getRemoteIp(request); + if (StringUtils.isBlank(tag)) { + persistService.removeConfigInfo(dataId, group, tenant, clientIp, null); + } else { + persistService.removeConfigInfoTag(dataId, group, tenant, tag, clientIp, null); + } + final Timestamp time = TimeUtils.getCurrentTime(); + ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), clientIp, + ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null); + EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); + return true; + } - /** - * 查询配置信息,返回JSON格式。 - */ - @RequestMapping(params = "search=accurate", method = RequestMethod.GET) - @ResponseBody + @RequestMapping(value = "/catalog", method = RequestMethod.GET) + @ResponseBody + public RestResult getConfigAdvanceInfo(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant) { + RestResult rr = new RestResult(); + ConfigAdvanceInfo configInfo = persistService.findConfigAdvanceInfo(dataId, group, tenant); + rr.setCode(200); + rr.setData(configInfo); + return rr; + } + + /** + * 比较MD5 + */ + @RequestMapping(value = "/listener", method = RequestMethod.POST) + public void listener(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); + String probeModify = request.getParameter("Listening-Configs"); + if (StringUtils.isBlank(probeModify)) { + throw new IllegalArgumentException("invalid probeModify"); + } + + probeModify = URLDecoder.decode(probeModify, Constants.ENCODE); + + Map clientMd5Map; + try { + clientMd5Map = MD5Util.getClientMd5Map(probeModify); + } catch (Throwable e) { + throw new IllegalArgumentException("invalid probeModify"); + } + + // do long-polling + inner.doPollingConfig(request, response, clientMd5Map, probeModify.length()); + } + + /* + * 订阅改配置的客户端信息 + */ + @RequestMapping(value = "/listener", method = RequestMethod.GET) + @ResponseBody + public GroupkeyListenserStatus getListeners(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "tenant", required = false) String tenant, + @RequestParam(value = "sampleTime", required = false, + defaultValue = "1") int sampleTime) + throws Exception { + group = StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group; + SampleResult collectSampleResult = configSubService.getCollectSampleResult(dataId, group, tenant, sampleTime); + GroupkeyListenserStatus gls = new GroupkeyListenserStatus(); + gls.setCollectStatus(200); + if (collectSampleResult.getLisentersGroupkeyStatus() != null) { + gls.setLisentersGroupkeyStatus(collectSampleResult.getLisentersGroupkeyStatus()); + } + return gls; + } + + /** + * 查询配置信息,返回JSON格式。 + */ + @RequestMapping(params = "search=accurate", method = RequestMethod.GET) + @ResponseBody public Page searchConfig(HttpServletRequest request, - HttpServletResponse response, - @RequestParam("dataId") String dataId, - @RequestParam("group") String group, - @RequestParam(value = "appName", required = false) String appName, - @RequestParam(value = "tenant", required = false, defaultValue=StringUtils.EMPTY) String tenant, - @RequestParam(value = "config_tags", required = false) String configTags, - @RequestParam("pageNo") int pageNo, - @RequestParam("pageSize") int pageSize, ModelMap modelMap) { - Map configAdvanceInfo = new HashMap(100); - if (StringUtils.isNotBlank(appName)) { - configAdvanceInfo.put("appName", appName); - } - if (StringUtils.isNotBlank(configTags)) { - configAdvanceInfo.put("config_tags", configTags); - } - try { - Page page = persistService.findConfigInfo4Page(pageNo, pageSize, dataId, group, tenant, - configAdvanceInfo); - return page; - } catch (Exception e) { - String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group; - log.error(errorMsg, e); - throw new RuntimeException(errorMsg, e); - } - } - - /** - * 模糊查询配置信息。不允许只根据内容模糊查询,即dataId和group都为NULL,但content不是NULL。这种情况下,返回所有配置。 - */ - @RequestMapping(params = "search=blur", method = RequestMethod.GET) - @ResponseBody - public Page fuzzySearchConfig(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, // - @RequestParam("group") String group, // - @RequestParam(value = "appName", required = false) String appName, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - @RequestParam(value = "config_tags", required = false) String configTags, - @RequestParam("pageNo") int pageNo, // - @RequestParam("pageSize") int pageSize, // - ModelMap modelMap) { - Map configAdvanceInfo = new HashMap(50); - if (StringUtils.isNotBlank(appName)) { - configAdvanceInfo.put("appName", appName); - } - if (StringUtils.isNotBlank(configTags)) { - configAdvanceInfo.put("config_tags", configTags); - } - try { - Page page = persistService.findConfigInfoLike4Page(pageNo, pageSize, dataId, group, tenant, - configAdvanceInfo); - return page; - } catch (Exception e) { - String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group; - log.error(errorMsg, e); - throw new RuntimeException(errorMsg, e); - } - } - - @RequestMapping(params = "beta=true", method = RequestMethod.DELETE) - @ResponseBody - public RestResult stopBeta(HttpServletRequest request, HttpServletResponse response, - @RequestParam(value = "dataId") String dataId, @RequestParam(value = "group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant) { - RestResult rr = new RestResult(); - try { - persistService.removeConfigInfo4Beta(dataId, group, tenant); - } catch (Throwable e) { - log.error("remove beta data error", e); - rr.setCode(500); - rr.setData(false); - rr.setMessage("remove beta data error"); - return rr; - } - EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, System.currentTimeMillis())); - rr.setCode(200); - rr.setData(true); - rr.setMessage("stop beta ok"); - return rr; - } + HttpServletResponse response, + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "appName", required = false) String appName, + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant, + @RequestParam(value = "config_tags", required = false) String configTags, + @RequestParam("pageNo") int pageNo, + @RequestParam("pageSize") int pageSize) { + Map configAdvanceInfo = new HashMap(100); + if (StringUtils.isNotBlank(appName)) { + configAdvanceInfo.put("appName", appName); + } + if (StringUtils.isNotBlank(configTags)) { + configAdvanceInfo.put("config_tags", configTags); + } + try { + return persistService.findConfigInfo4Page(pageNo, pageSize, dataId, group, tenant, + configAdvanceInfo); + } catch (Exception e) { + String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group; + log.error(errorMsg, e); + throw new RuntimeException(errorMsg, e); + } + } - @RequestMapping(params = "beta=true", method = RequestMethod.GET) - @ResponseBody - public RestResult queryBeta(HttpServletRequest request, HttpServletResponse response, - @RequestParam(value = "dataId") String dataId, @RequestParam(value = "group") String group, - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant) { - RestResult rr = new RestResult(); - try { - ConfigInfo4Beta ci = persistService.findConfigInfo4Beta(dataId, group, tenant); - rr.setCode(200); - rr.setData(ci); - rr.setMessage("stop beta ok"); - return rr; - } catch (Throwable e) { - log.error("remove beta data error", e); - rr.setCode(500); - rr.setMessage("remove beta data error"); - return rr; - } - } + /** + * 模糊查询配置信息。不允许只根据内容模糊查询,即dataId和group都为NULL,但content不是NULL。这种情况下,返回所有配置。 + */ + @RequestMapping(params = "search=blur", method = RequestMethod.GET) + @ResponseBody + public Page fuzzySearchConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, + @RequestParam("group") String group, + @RequestParam(value = "appName", required = false) String appName, + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant, + @RequestParam(value = "config_tags", required = false) String configTags, + @RequestParam("pageNo") int pageNo, + @RequestParam("pageSize") int pageSize) { + Map configAdvanceInfo = new HashMap(50); + if (StringUtils.isNotBlank(appName)) { + configAdvanceInfo.put("appName", appName); + } + if (StringUtils.isNotBlank(configTags)) { + configAdvanceInfo.put("config_tags", configTags); + } + try { + return persistService.findConfigInfoLike4Page(pageNo, pageSize, dataId, group, tenant, + configAdvanceInfo); + } catch (Exception e) { + String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group; + log.error(errorMsg, e); + throw new RuntimeException(errorMsg, e); + } + } + + @RequestMapping(params = "beta=true", method = RequestMethod.DELETE) + @ResponseBody + public RestResult stopBeta(HttpServletRequest request, HttpServletResponse response, + @RequestParam(value = "dataId") String dataId, + @RequestParam(value = "group") String group, + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant) { + RestResult rr = new RestResult(); + try { + persistService.removeConfigInfo4Beta(dataId, group, tenant); + } catch (Throwable e) { + log.error("remove beta data error", e); + rr.setCode(500); + rr.setData(false); + rr.setMessage("remove beta data error"); + return rr; + } + EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, System.currentTimeMillis())); + rr.setCode(200); + rr.setData(true); + rr.setMessage("stop beta ok"); + return rr; + } + + @RequestMapping(params = "beta=true", method = RequestMethod.GET) + @ResponseBody + public RestResult queryBeta(HttpServletRequest request, HttpServletResponse response, + @RequestParam(value = "dataId") String dataId, + @RequestParam(value = "group") String group, + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant) { + RestResult rr = new RestResult(); + try { + ConfigInfo4Beta ci = persistService.findConfigInfo4Beta(dataId, group, tenant); + rr.setCode(200); + rr.setData(ci); + rr.setMessage("stop beta ok"); + return rr; + } catch (Throwable e) { + log.error("remove beta data error", e); + rr.setCode(500); + rr.setMessage("remove beta data error"); + return rr; + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java index 35a3e90e6..7206febad 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java @@ -15,8 +15,22 @@ */ package com.alibaba.nacos.config.server.controller; -import static com.alibaba.nacos.config.server.utils.LogUtil.pullLog; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.model.CacheItem; +import com.alibaba.nacos.config.server.model.ConfigInfoBase; +import com.alibaba.nacos.config.server.service.ConfigService; +import com.alibaba.nacos.config.server.service.DiskUtil; +import com.alibaba.nacos.config.server.service.LongPollingService; +import com.alibaba.nacos.config.server.service.PersistService; +import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; +import com.alibaba.nacos.config.server.utils.*; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -27,55 +41,37 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.model.CacheItem; -import com.alibaba.nacos.config.server.model.ConfigInfoBase; -import com.alibaba.nacos.config.server.service.ConfigService; -import com.alibaba.nacos.config.server.service.DiskUtil; -import com.alibaba.nacos.config.server.service.LongPullingService; -import com.alibaba.nacos.config.server.service.PersistService; -import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; -import com.alibaba.nacos.config.server.utils.GroupKey2; -import com.alibaba.nacos.config.server.utils.LogUtil; -import com.alibaba.nacos.config.server.utils.MD5Util; -import com.alibaba.nacos.config.server.utils.PropertyUtil; -import com.alibaba.nacos.config.server.utils.Protocol; -import com.alibaba.nacos.config.server.utils.RequestUtil; -import com.alibaba.nacos.config.server.utils.TimeUtils; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.config.server.utils.LogUtil.pullLog; /** * ConfigServlet inner for aop - * + * * @author Nacos */ @Service public class ConfigServletInner { - @Autowired - private LongPullingService longPullingService; + @Autowired + private LongPollingService longPollingService; - @Autowired - private PersistService persistService; - - private static final int TRY_GET_LOCK_TIMES = 9; + @Autowired + private PersistService persistService; + + private static final int TRY_GET_LOCK_TIMES = 9; + + private static final int START_LONGPULLING_VERSION_NUM = 204; - private static final int START_LONGPULLING_VERSION_NUM = 204; /** * 轮询接口 */ - public String doPollingConfig(HttpServletRequest request, HttpServletResponse response, Map clientMd5Map, int probeRequestSize) throws IOException, ServletException { + public String doPollingConfig(HttpServletRequest request, HttpServletResponse response, + Map clientMd5Map, int probeRequestSize) + throws IOException, ServletException { // 长轮询 - if (LongPullingService.isSupportLongPulling(request)) { - longPullingService.addLongPullingClient(request, response, clientMd5Map, probeRequestSize); + if (LongPollingService.isSupportLongPulling(request)) { + longPollingService.addLongPullingClient(request, response, clientMd5Map, probeRequestSize); return HttpServletResponse.SC_OK + ""; } @@ -92,10 +88,10 @@ public class ConfigServletInner { } int versionNum = Protocol.getVersionNumber(version); - /** - * 2.0.4版本以前, 返回值放入header中 - */ - if (versionNum < START_LONGPULLING_VERSION_NUM) { + /** + * 2.0.4版本以前, 返回值放入header中 + */ + if (versionNum < START_LONGPULLING_VERSION_NUM) { response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult); response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult); } else { @@ -113,188 +109,190 @@ public class ConfigServletInner { /** * 同步配置获取接口 */ - public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group, - String tenant, String tag, String clientIp) throws IOException, ServletException { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - String autoTag = request.getHeader("Vipserver-Tag"); - String requestIpApp = RequestUtil.getAppName(request); - int lockResult = tryConfigReadLock(request, response, groupKey); + public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group, + String tenant, String tag, String clientIp) throws IOException, ServletException { + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + String autoTag = request.getHeader("Vipserver-Tag"); + String requestIpApp = RequestUtil.getAppName(request); + int lockResult = tryConfigReadLock(request, response, groupKey); - final String requestIp = RequestUtil.getRemoteIp(request); - boolean isBeta = false; - if (lockResult > 0) { - FileInputStream fis = null; - try { - String md5 = Constants.NULL; - long lastModified = 0L; - CacheItem cacheItem = ConfigService.getContentCache(groupKey); - if (cacheItem != null) { - if (cacheItem.isBeta()) { - if (cacheItem.getIps4Beta().contains(clientIp)) { - isBeta = true; - } - } - } - File file = null; - ConfigInfoBase configInfoBase = null; - PrintWriter out = null; - if (isBeta) { - md5 = cacheItem.getMd54Beta(); - lastModified = cacheItem.getLastModifiedTs4Beta(); - if (PropertyUtil.isStandaloneMode()) { - configInfoBase = persistService.findConfigInfo4Beta(dataId, group,tenant); - } else { - file = DiskUtil.targetBetaFile(dataId, group, tenant); - } - response.setHeader("isBeta", "true"); - } else { - if (StringUtils.isBlank(tag)) { - if (isUseTag(cacheItem, autoTag)) { - if (cacheItem != null) { - if (cacheItem.tagMd5 != null) { - md5 = cacheItem.tagMd5.get(autoTag); - } - if (cacheItem.tagLastModifiedTs != null) { - lastModified = cacheItem.tagLastModifiedTs.get(autoTag); - } - } - if (PropertyUtil.isStandaloneMode()) { - configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, autoTag); - } else { - file = DiskUtil.targetTagFile(dataId, group, tenant, autoTag); - } - - response.setHeader("Vipserver-Tag", - URLEncoder.encode(autoTag, StandardCharsets.UTF_8.displayName())); - } else { - md5 = cacheItem.getMd5(); - lastModified = cacheItem.getLastModifiedTs(); - if (PropertyUtil.isStandaloneMode()) { - configInfoBase = persistService.findConfigInfo(dataId, group, tenant); - } else { - file = DiskUtil.targetFile(dataId, group, tenant); - } - if (configInfoBase == null && fileNotExist(file)) { - // FIXME CacheItem - // 不存在了无法简单的计算推送delayed,这里简单的记做-1 - ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, - ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp); + final String requestIp = RequestUtil.getRemoteIp(request); + boolean isBeta = false; + if (lockResult > 0) { + FileInputStream fis = null; + try { + String md5 = Constants.NULL; + long lastModified = 0L; + CacheItem cacheItem = ConfigService.getContentCache(groupKey); + if (cacheItem != null) { + if (cacheItem.isBeta()) { + if (cacheItem.getIps4Beta().contains(clientIp)) { + isBeta = true; + } + } + } + File file = null; + ConfigInfoBase configInfoBase = null; + PrintWriter out = null; + if (isBeta) { + md5 = cacheItem.getMd54Beta(); + lastModified = cacheItem.getLastModifiedTs4Beta(); + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + configInfoBase = persistService.findConfigInfo4Beta(dataId, group, tenant); + } else { + file = DiskUtil.targetBetaFile(dataId, group, tenant); + } + response.setHeader("isBeta", "true"); + } else { + if (StringUtils.isBlank(tag)) { + if (isUseTag(cacheItem, autoTag)) { + if (cacheItem != null) { + if (cacheItem.tagMd5 != null) { + md5 = cacheItem.tagMd5.get(autoTag); + } + if (cacheItem.tagLastModifiedTs != null) { + lastModified = cacheItem.tagLastModifiedTs.get(autoTag); + } + } + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, autoTag); + } else { + file = DiskUtil.targetTagFile(dataId, group, tenant, autoTag); + } - // pullLog.info("[client-get] clientIp={}, {}, - // no data", - // new Object[]{clientIp, groupKey}); + response.setHeader("Vipserver-Tag", + URLEncoder.encode(autoTag, StandardCharsets.UTF_8.displayName())); + } else { + md5 = cacheItem.getMd5(); + lastModified = cacheItem.getLastModifiedTs(); + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + configInfoBase = persistService.findConfigInfo(dataId, group, tenant); + } else { + file = DiskUtil.targetFile(dataId, group, tenant); + } + if (configInfoBase == null && fileNotExist(file)) { + // FIXME CacheItem + // 不存在了无法简单的计算推送delayed,这里简单的记做-1 + ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, + ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - response.getWriter().println("config data not exist"); - return HttpServletResponse.SC_NOT_FOUND + ""; - } - } - } else { - if (cacheItem != null) { - if (cacheItem.tagMd5 != null) { - md5 = cacheItem.tagMd5.get(tag); - } - if (cacheItem.tagLastModifiedTs != null) { - Long lm = cacheItem.tagLastModifiedTs.get(tag); - if (lm != null) { - lastModified = lm; - } - } - } - if (PropertyUtil.isStandaloneMode()) { - configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, tag); - } else { - file = DiskUtil.targetTagFile(dataId, group, tenant, tag); - } - if (configInfoBase == null && fileNotExist(file)) { - // FIXME CacheItem - // 不存在了无法简单的计算推送delayed,这里简单的记做-1 - ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, ConfigTraceService.PULL_EVENT_NOTFOUND, - -1, requestIp); + // pullLog.info("[client-get] clientIp={}, {}, + // no data", + // new Object[]{clientIp, groupKey}); - // pullLog.info("[client-get] clientIp={}, {}, - // no data", - // new Object[]{clientIp, groupKey}); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + response.getWriter().println("config data not exist"); + return HttpServletResponse.SC_NOT_FOUND + ""; + } + } + } else { + if (cacheItem != null) { + if (cacheItem.tagMd5 != null) { + md5 = cacheItem.tagMd5.get(tag); + } + if (cacheItem.tagLastModifiedTs != null) { + Long lm = cacheItem.tagLastModifiedTs.get(tag); + if (lm != null) { + lastModified = lm; + } + } + } + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, tag); + } else { + file = DiskUtil.targetTagFile(dataId, group, tenant, tag); + } + if (configInfoBase == null && fileNotExist(file)) { + // FIXME CacheItem + // 不存在了无法简单的计算推送delayed,这里简单的记做-1 + ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, + ConfigTraceService.PULL_EVENT_NOTFOUND, + -1, requestIp); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - response.getWriter().println("config data not exist"); - return HttpServletResponse.SC_NOT_FOUND + ""; - } - } - } + // pullLog.info("[client-get] clientIp={}, {}, + // no data", + // new Object[]{clientIp, groupKey}); - response.setHeader(Constants.CONTENT_MD5, md5); - /** - * 禁用缓存 - */ - response.setHeader("Pragma", "no-cache"); - response.setDateHeader("Expires", 0); - response.setHeader("Cache-Control", "no-cache,no-store"); - if (PropertyUtil.isStandaloneMode()) { - response.setDateHeader("Last-Modified", lastModified); - } else { - fis = new FileInputStream(file); - response.setDateHeader("Last-Modified", file.lastModified()); - } + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + response.getWriter().println("config data not exist"); + return HttpServletResponse.SC_NOT_FOUND + ""; + } + } + } + response.setHeader(Constants.CONTENT_MD5, md5); + /** + * 禁用缓存 + */ + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + response.setHeader("Cache-Control", "no-cache,no-store"); + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + response.setDateHeader("Last-Modified", lastModified); + } else { + fis = new FileInputStream(file); + response.setDateHeader("Last-Modified", file.lastModified()); + } - if (PropertyUtil.isStandaloneMode()) { - out = response.getWriter(); - out.print(configInfoBase.getContent()); - out.flush(); - out.close(); - } else { - fis.getChannel().transferTo(0L, fis.getChannel().size(), - Channels.newChannel(response.getOutputStream())); - } + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + out = response.getWriter(); + out.print(configInfoBase.getContent()); + out.flush(); + out.close(); + } else { + fis.getChannel().transferTo(0L, fis.getChannel().size(), + Channels.newChannel(response.getOutputStream())); + } - LogUtil.pullCheckLog.warn("{}|{}|{}|{}", groupKey,requestIp,md5, TimeUtils.getCurrentTimeStr()); + LogUtil.pullCheckLog.warn("{}|{}|{}|{}", groupKey, requestIp, md5, TimeUtils.getCurrentTimeStr()); + final long delayed = System.currentTimeMillis() - lastModified; - final long delayed = System.currentTimeMillis() - lastModified; + // TODO distinguish pull-get && push-get + // 否则无法直接把delayed作为推送延时的依据,因为主动get请求的delayed值都很大 + ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, lastModified, + ConfigTraceService.PULL_EVENT_OK, delayed, + requestIp); - // TODO distinguish pull-get && push-get - // 否则无法直接把delayed作为推送延时的依据,因为主动get请求的delayed值都很大 - ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, lastModified, ConfigTraceService.PULL_EVENT_OK, delayed, - requestIp); + } finally { + releaseConfigReadLock(groupKey); + if (null != fis) { + fis.close(); + } + } + } else if (lockResult == 0) { - } finally { - releaseConfigReadLock(groupKey); - if (null != fis) { - fis.close(); - } - } - } else if (lockResult == 0) { + // FIXME CacheItem 不存在了无法简单的计算推送delayed,这里简单的记做-1 + ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, + ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp); - // FIXME CacheItem 不存在了无法简单的计算推送delayed,这里简单的记做-1 - ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, ConfigTraceService.PULL_EVENT_NOTFOUND, -1, requestIp); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + response.getWriter().println("config data not exist"); + return HttpServletResponse.SC_NOT_FOUND + ""; - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - response.getWriter().println("config data not exist"); - return HttpServletResponse.SC_NOT_FOUND + ""; + } else { - } else { + pullLog.info("[client-get] clientIp={}, {}, get data during dump", clientIp, groupKey); - pullLog.info("[client-get] clientIp={}, {}, get data during dump", clientIp, groupKey); + response.setStatus(HttpServletResponse.SC_CONFLICT); + response.getWriter().println("requested file is being modified, please try later."); + return HttpServletResponse.SC_CONFLICT + ""; - response.setStatus(HttpServletResponse.SC_CONFLICT); - response.getWriter().println("requested file is being modified, please try later."); - return HttpServletResponse.SC_CONFLICT + ""; + } - } - - return HttpServletResponse.SC_OK + ""; - } + return HttpServletResponse.SC_OK + ""; + } private static void releaseConfigReadLock(String groupKey) { ConfigService.releaseReadLock(groupKey); } - private static int tryConfigReadLock(HttpServletRequest request, HttpServletResponse response, String groupKey) throws IOException, ServletException { - /** - * 默认加锁失败 - */ - int lockResult = -1; + private static int tryConfigReadLock(HttpServletRequest request, HttpServletResponse response, String groupKey) + throws IOException, ServletException { + /** + * 默认加锁失败 + */ + int lockResult = -1; /** * 尝试加锁,最多10次 */ @@ -303,20 +301,20 @@ public class ConfigServletInner { /** * 数据不存在 */ - if (0 == lockResult) { + if (0 == lockResult) { break; } /** * success */ - if (lockResult > 0) { + if (lockResult > 0) { break; } /** * retry */ - if (i > 0) { + if (i > 0) { try { Thread.sleep(1); } catch (Exception e) { @@ -327,17 +325,15 @@ public class ConfigServletInner { return lockResult; } - private static boolean isUseTag(CacheItem cacheItem, String tag) { - if (cacheItem != null && cacheItem.tagMd5 != null && cacheItem.tagMd5.size() > 0) { - if (StringUtils.isNotBlank(tag) && cacheItem.tagMd5.containsKey(tag)) { - return true; - } - } - return false; - } - - private static boolean fileNotExist(File file) { - return file == null || !file.exists(); - } + private static boolean isUseTag(CacheItem cacheItem, String tag) { + if (cacheItem != null && cacheItem.tagMd5 != null && cacheItem.tagMd5.size() > 0) { + return StringUtils.isNotBlank(tag) && cacheItem.tagMd5.containsKey(tag); + } + return false; + } + + private static boolean fileNotExist(File file) { + return file == null || !file.exists(); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/HealthController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/HealthController.java index 68a2f1b01..3ff4fbd43 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/HealthController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/HealthController.java @@ -15,65 +15,70 @@ */ package com.alibaba.nacos.config.server.controller; -import javax.annotation.PostConstruct; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.service.DataSourceService; +import com.alibaba.nacos.config.server.service.DynamicDataSource; +import com.alibaba.nacos.config.server.service.ServerListService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.service.DataSourceService; -import com.alibaba.nacos.config.server.service.DynamicDataSource; -import com.alibaba.nacos.config.server.service.ServerListService; -import com.alibaba.nacos.config.server.utils.SystemConfig; + +import javax.annotation.PostConstruct; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * health service - * - * @author Nacos * + * @author Nacos */ @Controller @RequestMapping(Constants.HEALTH_CONTROLLER_PATH) public class HealthController { - - @Autowired - private DynamicDataSource dynamicDataSource; - private DataSourceService dataSourceService; - private String heathUpStr = "UP"; - private String heathDownStr = "DOWN"; - private String heathWarnStr = "WARN"; - @PostConstruct - public void init() { - dataSourceService = dynamicDataSource.getDataSource(); - } + private final DynamicDataSource dynamicDataSource; + private DataSourceService dataSourceService; + private String heathUpStr = "UP"; + private String heathDownStr = "DOWN"; + private String heathWarnStr = "WARN"; - @ResponseBody - @RequestMapping(method = RequestMethod.GET) - public String getHealth() { - // TODO UP DOWN WARN - StringBuilder sb = new StringBuilder(); - String dbStatus = dataSourceService.getHealth(); - if (dbStatus.contains(heathUpStr) && ServerListService.isAddressServerHealth() && ServerListService.isInIpList()) { - sb.append(heathUpStr); - } else if (dbStatus.contains(heathWarnStr) && ServerListService.isAddressServerHealth() && ServerListService.isInIpList()){ - sb.append("WARN:"); - sb.append("从数据库 ").append(dbStatus.split(":")[1]).append(" down. "); - } else { - sb.append("DOWN:"); - if (dbStatus.indexOf(heathDownStr) != -1) { - sb.append("主数据库 ").append(dbStatus.split(":")[1]).append(" down. "); - } - if (!ServerListService.isAddressServerHealth()) { - sb.append("地址服务器 down. "); - } - if (!ServerListService.isInIpList()) { - sb.append("server ").append(SystemConfig.LOCAL_IP).append(" 不在地址服务器的IP列表中. "); - } - } + @Autowired + public HealthController(DynamicDataSource dynamicDataSource) {this.dynamicDataSource = dynamicDataSource;} - return sb.toString(); - } + @PostConstruct + public void init() { + dataSourceService = dynamicDataSource.getDataSource(); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public String getHealth() { + // TODO UP DOWN WARN + StringBuilder sb = new StringBuilder(); + String dbStatus = dataSourceService.getHealth(); + if (dbStatus.contains(heathUpStr) && ServerListService.isAddressServerHealth() && ServerListService + .isInIpList()) { + sb.append(heathUpStr); + } else if (dbStatus.contains(heathWarnStr) && ServerListService.isAddressServerHealth() && ServerListService + .isInIpList()) { + sb.append("WARN:"); + sb.append("从数据库 ").append(dbStatus.split(":")[1]).append(" down. "); + } else { + sb.append("DOWN:"); + if (dbStatus.contains(heathDownStr)) { + sb.append("主数据库 ").append(dbStatus.split(":")[1]).append(" down. "); + } + if (!ServerListService.isAddressServerHealth()) { + sb.append("地址服务器 down. "); + } + if (!ServerListService.isInIpList()) { + sb.append("server ").append(LOCAL_IP).append(" 不在地址服务器的IP列表中. "); + } + } + + return sb.toString(); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java index 50cd17b26..9f2116828 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/HistoryController.java @@ -15,10 +15,11 @@ */ package com.alibaba.nacos.config.server.controller; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; +import com.alibaba.nacos.config.server.model.Page; +import com.alibaba.nacos.config.server.service.PersistService; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -27,50 +28,49 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; -import com.alibaba.nacos.config.server.model.Page; -import com.alibaba.nacos.config.server.service.PersistService; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** - * 管理控制器。 + * 管理控制器。 * - *@author Nacos + * @author Nacos */ @Controller @RequestMapping(Constants.HISTORY_CONTROLLER_PATH) public class HistoryController { - @Autowired - protected PersistService persistService; + @Autowired + protected PersistService persistService; - @RequestMapping(params = "search=accurate", method = RequestMethod.GET) - @ResponseBody - public Page listConfigHistory(HttpServletRequest request, HttpServletResponse response, - @RequestParam("dataId") String dataId, // - @RequestParam("group") String group, // - @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant, - @RequestParam(value = "appName", required = false) String appName, - @RequestParam(value = "pageNo", required = false) Integer pageNo, // - @RequestParam(value = "pageSize", required = false) Integer pageSize, // - ModelMap modelMap) { - pageNo = null == pageNo ? Integer.valueOf(1) : pageNo; - pageSize = null == pageSize ? Integer.valueOf(100) : pageSize; - pageSize = pageSize > 500 ? Integer.valueOf(500) : pageSize; - // configInfoBase没有appName字段 - Page page = persistService.findConfigHistory(dataId, group, tenant, pageNo, pageSize); - return page; - } - - /** - * 查看配置历史信息详情 - */ - @RequestMapping(method = RequestMethod.GET) - @ResponseBody - public ConfigHistoryInfo getConfigHistoryInfo(HttpServletRequest request, HttpServletResponse response, - @RequestParam("nid") Long nid, ModelMap modelMap) { - ConfigHistoryInfo configInfo = persistService.detailConfigHistory(nid); - return configInfo; - } + @RequestMapping(params = "search=accurate", method = RequestMethod.GET) + @ResponseBody + public Page listConfigHistory(HttpServletRequest request, HttpServletResponse response, + @RequestParam("dataId") String dataId, // + @RequestParam("group") String group, // + @RequestParam(value = "tenant", required = false, + defaultValue = StringUtils.EMPTY) String tenant, + @RequestParam(value = "appName", required = false) String appName, + @RequestParam(value = "pageNo", required = false) Integer pageNo, + // + @RequestParam(value = "pageSize", required = false) + Integer pageSize, // + ModelMap modelMap) { + pageNo = null == pageNo ? Integer.valueOf(1) : pageNo; + pageSize = null == pageSize ? Integer.valueOf(100) : pageSize; + pageSize = pageSize > 500 ? Integer.valueOf(500) : pageSize; + // configInfoBase没有appName字段 + return persistService.findConfigHistory(dataId, group, tenant, pageNo, pageSize); + } + + /** + * 查看配置历史信息详情 + */ + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public ConfigHistoryInfo getConfigHistoryInfo(HttpServletRequest request, HttpServletResponse response, + @RequestParam("nid") Long nid, ModelMap modelMap) { + return persistService.detailConfigHistory(nid); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java index 9a8a08644..aac841a09 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ListenerController.java @@ -15,15 +15,12 @@ */ package com.alibaba.nacos.config.server.controller; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus; +import com.alibaba.nacos.config.server.model.SampleResult; +import com.alibaba.nacos.config.server.service.ConfigSubService; +import com.alibaba.nacos.config.server.utils.GroupKey2; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; @@ -32,65 +29,66 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus; -import com.alibaba.nacos.config.server.model.SampleResult; -import com.alibaba.nacos.config.server.service.ConfigSubService; -import com.alibaba.nacos.config.server.utils.GroupKey2; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; /** * Config longpulling - * - * @author Nacos * + * @author Nacos */ @Controller @RequestMapping(Constants.LISTENER_CONTROLLER_PATH) public class ListenerController { - - @Autowired - ConfigSubService configSubService; - - /* - * 获取客户端订阅配置信息 - */ - @RequestMapping(method = RequestMethod.GET) - @ResponseBody - public GroupkeyListenserStatus getAllSubClientConfigByIp(HttpServletRequest request, HttpServletResponse response, - @RequestParam("ip") String ip, @RequestParam(value = "all", required = false) boolean all, - @RequestParam(value = "tenant", required = false) String tenant, - @RequestParam(value = "sampleTime", required = false, defaultValue = "1") int sampleTime, ModelMap modelMap) - throws IOException, ServletException, Exception { - SampleResult collectSampleResult = configSubService.getCollectSampleResultByIp(ip, sampleTime); - GroupkeyListenserStatus gls = new GroupkeyListenserStatus(); - gls.setCollectStatus(200); - Map configMd5Status = new HashMap(100); - if (collectSampleResult.getLisentersGroupkeyStatus() != null) { - Map status = collectSampleResult.getLisentersGroupkeyStatus(); - for (Map.Entry config : status.entrySet()) { - if (!StringUtils.isBlank(tenant)) { - if (config.getKey().contains(tenant)) { - configMd5Status.put(config.getKey(), config.getValue()); - } - } else { - // 默认值获取公共配置,如果想看所有配置,要加all - if (all) { - configMd5Status.put(config.getKey(), config.getValue()); - } else { - String[] configKeys = GroupKey2.parseKey(config.getKey()); - if (StringUtils.isBlank(configKeys[2])) { - configMd5Status.put(config.getKey(), config.getValue()); - } - } - } - } - gls.setLisentersGroupkeyStatus(configMd5Status); - } - return gls; - } - - + private final ConfigSubService configSubService; + + @Autowired + public ListenerController(ConfigSubService configSubService) {this.configSubService = configSubService;} + + /* + * 获取客户端订阅配置信息 + */ + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public GroupkeyListenserStatus getAllSubClientConfigByIp(HttpServletRequest request, HttpServletResponse response, + @RequestParam("ip") String ip, + @RequestParam(value = "all", required = false) boolean all, + @RequestParam(value = "tenant", required = false) + String tenant, + @RequestParam(value = "sampleTime", required = false, + defaultValue = "1") int sampleTime, ModelMap modelMap) + throws Exception { + SampleResult collectSampleResult = configSubService.getCollectSampleResultByIp(ip, sampleTime); + GroupkeyListenserStatus gls = new GroupkeyListenserStatus(); + gls.setCollectStatus(200); + Map configMd5Status = new HashMap(100); + if (collectSampleResult.getLisentersGroupkeyStatus() != null) { + Map status = collectSampleResult.getLisentersGroupkeyStatus(); + for (Map.Entry config : status.entrySet()) { + if (!StringUtils.isBlank(tenant)) { + if (config.getKey().contains(tenant)) { + configMd5Status.put(config.getKey(), config.getValue()); + } + } else { + // 默认值获取公共配置,如果想看所有配置,要加all + if (all) { + configMd5Status.put(config.getKey(), config.getValue()); + } else { + String[] configKeys = GroupKey2.parseKey(config.getKey()); + if (StringUtils.isBlank(configKeys[2])) { + configMd5Status.put(config.getKey(), config.getValue()); + } + } + } + } + gls.setLisentersGroupkeyStatus(configMd5Status); + } + + return gls; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/OpsController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/OpsController.java index 56d4a0d57..9b303e945 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/OpsController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/OpsController.java @@ -15,9 +15,9 @@ */ package com.alibaba.nacos.config.server.controller; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.service.PersistService; +import com.alibaba.nacos.config.server.service.dump.DumpService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -26,9 +26,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.service.PersistService; -import com.alibaba.nacos.config.server.service.dump.DumpService; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * 管理控制器。 @@ -39,22 +38,26 @@ import com.alibaba.nacos.config.server.service.dump.DumpService; @RequestMapping(Constants.OPS_CONTROLLER_PATH) public class OpsController { - private static final Logger log = LoggerFactory.getLogger(OpsController.class); + private static final Logger log = LoggerFactory.getLogger(OpsController.class); - @Autowired - protected PersistService persistService; + protected final PersistService persistService; - @Autowired - DumpService dumpService; + private final DumpService dumpService; - // ops call - @RequestMapping(value = "/localCache", method = RequestMethod.POST) - @ResponseBody - public String updateLocalCacheFromStore(HttpServletRequest request, HttpServletResponse respons) { - log.info("start to dump all data from store."); - dumpService.dumpAll(); - log.info("finish to dump all data from store."); - return HttpServletResponse.SC_OK + ""; - } + @Autowired + public OpsController(PersistService persistService, DumpService dumpService) { + this.persistService = persistService; + this.dumpService = dumpService; + } + + // ops call + @RequestMapping(value = "/localCache", method = RequestMethod.POST) + @ResponseBody + public String updateLocalCacheFromStore(HttpServletRequest request, HttpServletResponse respons) { + log.info("start to dump all data from store."); + dumpService.dumpAll(); + log.info("finish to dump all data from store."); + return HttpServletResponse.SC_OK + ""; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java index 52dce3509..8fbd2b5a7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java @@ -15,53 +15,49 @@ */ package com.alibaba.nacos.config.server.exception; -import java.io.IOException; - -import javax.servlet.http.HttpServletResponse; - import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + /** * global exception handler - * - * @author Nacos * + * @author Nacos */ @ControllerAdvice public class GlobalExceptionHandler { - /** - * For IllegalArgumentException, we are returning void with status code as - * 400, so our error-page will be used in this case. - * - * @throws IllegalArgumentException - * - */ - @ExceptionHandler(IllegalArgumentException.class) - public void handleIllegalArgumentException(HttpServletResponse response, Exception ex) throws IOException { - response.setStatus(400); - if (ex.getMessage() != null) { - response.getWriter().println(ex.getMessage()); - } else { - response.getWriter().println("invalid param"); - } - } + /** + * For IllegalArgumentException, we are returning void with status code as 400, so our error-page will be used in + * this case. + * + * @throws IllegalArgumentException + */ + @ExceptionHandler(IllegalArgumentException.class) + public void handleIllegalArgumentException(HttpServletResponse response, Exception ex) throws IOException { + response.setStatus(400); + if (ex.getMessage() != null) { + response.getWriter().println(ex.getMessage()); + } else { + response.getWriter().println("invalid param"); + } + } - /** - * For NacosException - * - * @throws NacosException - * - */ - @ExceptionHandler(NacosException.class) - public void handleNacosException(HttpServletResponse response, NacosException ex) throws IOException { - response.setStatus(ex.getErrCode()); - if (ex.getErrMsg() != null) { - response.getWriter().println(ex.getErrMsg()); - } else { - response.getWriter().println("unknown exception"); - } - } + /** + * For NacosException + * + * @throws NacosException + */ + @ExceptionHandler(NacosException.class) + public void handleNacosException(HttpServletResponse response, NacosException ex) throws IOException { + response.setStatus(ex.getErrCode()); + if (ex.getErrMsg() != null) { + response.getWriter().println(ex.getErrMsg()); + } else { + response.getWriter().println("unknown exception"); + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/exception/NacosException.java b/config/src/main/java/com/alibaba/nacos/config/server/exception/NacosException.java index 0e0c7c668..d24d95867 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/exception/NacosException.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/exception/NacosException.java @@ -17,78 +17,77 @@ package com.alibaba.nacos.config.server.exception; /** * Nacos exception - * - * @author Nacos * + * @author Nacos */ public class NacosException extends Exception { - /** - * serialVersionUID - */ - private static final long serialVersionUID = -3913902031489277776L; + /** + * serialVersionUID + */ + private static final long serialVersionUID = -3913902031489277776L; - private int errCode; + private int errCode; - private String errMsg; - - public NacosException() { - } + private String errMsg; - public NacosException(int errCode, String errMsg) { - super(errMsg); - this.errCode = errCode; - this.errMsg = errMsg; - } - - public int getErrCode() { - return errCode; - } + public NacosException() { + } - public String getErrMsg() { - return errMsg; - } + public NacosException(int errCode, String errMsg) { + super(errMsg); + this.errCode = errCode; + this.errMsg = errMsg; + } - public void setErrCode(int errCode) { - this.errCode = errCode; - } + public int getErrCode() { + return errCode; + } - public void setErrMsg(String errMsg) { - this.errMsg = errMsg; - } - - @Override - public String toString() { - return "ErrCode:" + errCode + ",ErrMsg:" + errMsg; - } + public String getErrMsg() { + return errMsg; + } + + public void setErrCode(int errCode) { + this.errCode = errCode; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + + @Override + public String toString() { + return "ErrCode:" + errCode + ",ErrMsg:" + errMsg; + } + + /** + * server error code, use http code 400 403 throw exception to user 500 502 + * 503 change ip and retry + */ + /** + * invalid param(参数错误) + */ + public static final int INVALID_PARAM = 400; + /** + * no right(鉴权失败) + */ + public static final int NO_RIGHT = 403; + /** + * conflict(写并发冲突) + */ + public static final int CONFLICT = 409; + /** + * server error(server异常,如超时) + */ + public static final int SERVER_ERROR = 500; + /** + * bad gateway(路由异常,如nginx后面的Server挂掉) + */ + public static final int BAD_GATEWAY = 502; + /** + * over threshold(超过server端的限流阈值) + */ + public static final int OVER_THRESHOLD = 503; - /** - * server error code, use http code 400 403 throw exception to user 500 502 - * 503 change ip and retry - */ - /** - * invalid param(参数错误) - */ - public static final int INVALID_PARAM = 400; - /** - * no right(鉴权失败) - */ - public static final int NO_RIGHT = 403; - /** - * conflict(写并发冲突) - */ - public static final int CONFLICT = 409; - /** - * server error(server异常,如超时) - */ - public static final int SERVER_ERROR = 500; - /** - * bad gateway(路由异常,如nginx后面的Server挂掉) - */ - public static final int BAD_GATEWAY = 502; - /** - * over threshold(超过server端的限流阈值) - */ - public static final int OVER_THRESHOLD = 503; - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/filter/WebFilter.java b/config/src/main/java/com/alibaba/nacos/config/server/filter/NacosWebFilter.java similarity index 57% rename from config/src/main/java/com/alibaba/nacos/config/server/filter/WebFilter.java rename to config/src/main/java/com/alibaba/nacos/config/server/filter/NacosWebFilter.java index f27d9f335..e4a5099bb 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/filter/WebFilter.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/filter/NacosWebFilter.java @@ -15,68 +15,62 @@ */ package com.alibaba.nacos.config.server.filter; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; +import com.alibaba.nacos.config.server.constant.Constants; +import org.springframework.core.annotation.Order; +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; import java.io.IOException; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import org.springframework.core.annotation.Order; -import com.alibaba.nacos.config.server.constant.Constants; +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; /** * encode filter - * - * @author Nacos * + * @author Nacos */ @Order(1) -@javax.servlet.annotation.WebFilter(filterName = "webFilter", urlPatterns = "/*") -public class WebFilter implements Filter { - +@WebFilter(filterName = "webFilter", urlPatterns = "/*") +public class NacosWebFilter implements Filter { + static private String webRootPath; - + static public String rootPath() { return webRootPath; } - + /** - * 方便测试 + * 方便测试 + * * @param path web path */ static public void setWebRootPath(String path) { webRootPath = path; } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - ServletContext ctx = filterConfig.getServletContext(); - setWebRootPath(ctx.getRealPath("/")); - } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + ServletContext ctx = filterConfig.getServletContext(); + setWebRootPath(ctx.getRealPath("/")); + } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { + throws IOException, ServletException { request.setCharacterEncoding(Constants.ENCODE); - response.setContentType("application/json;charset="+Constants.ENCODE); - - try { - chain.doFilter(request, response); - } catch (IOException ioe) { - defaultLog.debug("Filter catch exception, " + ioe.toString(), ioe); - throw ioe; - } catch (ServletException se) { - defaultLog.debug("Filter catch exception, " + se.toString(), se); - throw se; - } + response.setContentType("application/json;charset=" + Constants.ENCODE); + + try { + chain.doFilter(request, response); + } catch (IOException ioe) { + defaultLog.debug("Filter catch exception, " + ioe.toString(), ioe); + throw ioe; + } catch (ServletException se) { + defaultLog.debug("Filter catch exception, " + se.toString(), se); + throw se; + } } - @Override public void destroy() { } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/manager/AbstractTask.java b/config/src/main/java/com/alibaba/nacos/config/server/manager/AbstractTask.java index e5e79b7a9..51e7b33ba 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/manager/AbstractTask.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/manager/AbstractTask.java @@ -17,48 +17,50 @@ package com.alibaba.nacos.config.server.manager; /** * task manage - * - * @author huali * + * @author huali */ public abstract class AbstractTask { - /** - * 一个任务两次处理的间隔,单位是毫秒 - */ - private long taskInterval; - - /** - * 任务上次被处理的时间,用毫秒表示 - */ - private long lastProcessTime; - /** - * merge task - * @param task task - */ - public abstract void merge(AbstractTask task); - - public void setTaskInterval(long interval){ - this.taskInterval = interval; - } - - public long getTaskInterval(){ - return this.taskInterval; - } - - public void setLastProcessTime(long lastProcessTime){ - this.lastProcessTime = lastProcessTime; - } - - public long getLastProcessTime(){ - return this.lastProcessTime; - } - - /** - * TaskManager 判断当前是否需要处理这个Task,子类可以Override这个函数实现自己的逻辑 - * @return - */ - public boolean shouldProcess(){ - return (System.currentTimeMillis() - this.lastProcessTime >= this.taskInterval); - } - + /** + * 一个任务两次处理的间隔,单位是毫秒 + */ + private long taskInterval; + + /** + * 任务上次被处理的时间,用毫秒表示 + */ + private long lastProcessTime; + + /** + * merge task + * + * @param task task + */ + public abstract void merge(AbstractTask task); + + public void setTaskInterval(long interval) { + this.taskInterval = interval; + } + + public long getTaskInterval() { + return this.taskInterval; + } + + public void setLastProcessTime(long lastProcessTime) { + this.lastProcessTime = lastProcessTime; + } + + public long getLastProcessTime() { + return this.lastProcessTime; + } + + /** + * TaskManager 判断当前是否需要处理这个Task,子类可以Override这个函数实现自己的逻辑 + * + * @return + */ + public boolean shouldProcess() { + return (System.currentTimeMillis() - this.lastProcessTime >= this.taskInterval); + } + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java index 5042e4c39..e7b08e784 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java @@ -15,6 +15,11 @@ */ package com.alibaba.nacos.config.server.manager; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.utils.LogUtil; +import org.slf4j.Logger; + +import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.util.Date; import java.util.Map; @@ -23,34 +28,27 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import javax.management.ObjectName; -import org.slf4j.Logger; - -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.utils.LogUtil; - /** * 用于处理一定要执行成功的任务 单线程的方式处理任务,保证任务一定被成功处理 - * + * * @author huali - * */ public final class TaskManager implements TaskManagerMBean { - - private static final Logger log =LogUtil.defaultLog; + + private static final Logger log = LogUtil.defaultLog; private final ConcurrentHashMap tasks = new ConcurrentHashMap(); private final ConcurrentHashMap taskProcessors = - new ConcurrentHashMap(); + new ConcurrentHashMap(); private TaskProcessor defaultTaskProcessor; Thread processingThread; private final AtomicBoolean closed = new AtomicBoolean(true); - + private String name; class ProcessRunnable implements Runnable { @@ -60,8 +58,7 @@ public final class TaskManager implements TaskManagerMBean { try { Thread.sleep(100); TaskManager.this.process(); - } - catch (Throwable e) { + } catch (Throwable e) { } } @@ -73,17 +70,14 @@ public final class TaskManager implements TaskManagerMBean { Condition notEmpty = this.lock.newCondition(); - public TaskManager() { this(null); } - public AbstractTask getTask(String type) { return this.tasks.get(type); } - public TaskProcessor getTaskProcessor(String type) { return this.taskProcessors.get(type); } @@ -93,8 +87,7 @@ public final class TaskManager implements TaskManagerMBean { this.name = name; if (null != name && name.length() > 0) { this.processingThread = new Thread(new ProcessRunnable(), name); - } - else { + } else { this.processingThread = new Thread(new ProcessRunnable()); } this.processingThread.setDaemon(true); @@ -111,62 +104,54 @@ public final class TaskManager implements TaskManagerMBean { this.processingThread.interrupt(); } - public void await() throws InterruptedException { this.lock.lock(); try { while (!this.isEmpty()) { this.notEmpty.await(); } - } - finally { + } finally { this.lock.unlock(); } } - - public boolean await(long timeout, TimeUnit unit) throws InterruptedException { - this.lock.lock(); - boolean isawait = false; - try { - while (!this.isEmpty()) { - isawait = this.notEmpty.await(timeout, unit); - } - return isawait; - } finally { - this.lock.unlock(); - } - } - + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { + this.lock.lock(); + boolean isawait = false; + try { + while (!this.isEmpty()) { + isawait = this.notEmpty.await(timeout, unit); + } + return isawait; + } finally { + this.lock.unlock(); + } + } public void addProcessor(String type, TaskProcessor taskProcessor) { this.taskProcessors.put(type, taskProcessor); } - public void removeProcessor(String type) { this.taskProcessors.remove(type); } - public void removeTask(String type) { this.lock.lock(); try { this.tasks.remove(type); - } - finally { + } finally { this.lock.unlock(); } } - /** * 将任务加入到任务Map中 - * + * * @param type * @param task * @param previousTask - * */ + */ public void addTask(String type, AbstractTask task) { this.lock.lock(); try { @@ -179,9 +164,8 @@ public final class TaskManager implements TaskManagerMBean { } } - /** - * + * */ protected void process() { for (Map.Entry entry : this.tasks.entrySet()) { @@ -198,8 +182,7 @@ public final class TaskManager implements TaskManagerMBean { // 先将任务从任务Map中删除 this.tasks.remove(entry.getKey()); } - } - finally { + } finally { this.lock.unlock(); } @@ -215,9 +198,8 @@ public final class TaskManager implements TaskManagerMBean { try { // 处理任务 result = processor.process(entry.getKey(), task); - } - catch (Throwable t) { - log.error("task_fail", "处理task失败", t); + } catch (Throwable t) { + log.error("task_fail", "处理task失败", t); } if (!result) { // 任务处理失败,设置最后处理时间 @@ -234,65 +216,56 @@ public final class TaskManager implements TaskManagerMBean { this.lock.lock(); try { this.notEmpty.signalAll(); - } - finally { + } finally { this.lock.unlock(); } } } - public boolean isEmpty() { return tasks.isEmpty(); } - public TaskProcessor getDefaultTaskProcessor() { this.lock.lock(); try { return this.defaultTaskProcessor; - } - finally { + } finally { this.lock.unlock(); } } - public void setDefaultTaskProcessor(TaskProcessor defaultTaskProcessor) { this.lock.lock(); try { this.defaultTaskProcessor = defaultTaskProcessor; - } - finally { + } finally { this.lock.unlock(); } } - public String getTaskInfos() { StringBuilder sb = new StringBuilder(); - for(String taskType: this.taskProcessors.keySet()) { + for (String taskType : this.taskProcessors.keySet()) { sb.append(taskType).append(":"); AbstractTask task = this.tasks.get(taskType); - if(task != null) { + if (task != null) { sb.append(new Date(task.getLastProcessTime()).toString()); } else { sb.append("finished"); } sb.append(Constants.NACOS_LINE_SEPARATOR); } - + return sb.toString(); } - - + public void init() { try { ObjectName oName = new ObjectName(this.name + ":type=" + TaskManager.class.getSimpleName()); ManagementFactory.getPlatformMBeanServer().registerMBean(this, oName); - } - catch (Exception e) { - log.error("registerMBean_fail", "注册mbean出错", e); + } catch (Exception e) { + log.error("registerMBean_fail", "注册mbean出错", e); } } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManagerMBean.java b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManagerMBean.java index 363ddbd6e..9b25e97c3 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManagerMBean.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManagerMBean.java @@ -17,18 +17,17 @@ package com.alibaba.nacos.config.server.manager; /** * tasks - * - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public interface TaskManagerMBean { - /** - * get task info - * - * @return info - */ + /** + * get task info + * + * @return info + */ public String getTaskInfos(); - + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskProcessor.java b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskProcessor.java index bb79d5efe..d518aef3d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskProcessor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/manager/TaskProcessor.java @@ -17,19 +17,16 @@ package com.alibaba.nacos.config.server.manager; /** * task processor - * - * @author Nacos * + * @author Nacos */ public interface TaskProcessor { - /** - * process task - * - * @param taskType - * task type - * @param task - * task - * @return process task result - */ - boolean process(String taskType, AbstractTask task); + /** + * process task + * + * @param taskType task type + * @param task task + * @return process task result + */ + boolean process(String taskType, AbstractTask task); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ACLInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ACLInfo.java index cb4c99e11..b6dbf73c2 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ACLInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ACLInfo.java @@ -19,29 +19,28 @@ import java.util.List; /** * acl info - * - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class ACLInfo { - private Boolean isOpen; - private List ips; + private Boolean isOpen; + private List ips; - public List getIps() { - return ips; - } + public List getIps() { + return ips; + } - public void setIps(List ips) { - this.ips = ips; - } + public void setIps(List ips) { + this.ips = ips; + } - public Boolean getIsOpen() { - return isOpen; - } + public Boolean getIsOpen() { + return isOpen; + } - public void setIsOpen(Boolean isOpen) { - this.isOpen = isOpen; - } + public void setIsOpen(Boolean isOpen) { + this.isOpen = isOpen; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/AuthType.java b/config/src/main/java/com/alibaba/nacos/config/server/model/AuthType.java index 73b2be45d..f7600daa8 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/AuthType.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/AuthType.java @@ -17,13 +17,15 @@ package com.alibaba.nacos.config.server.model; /** * auth type - * - * @author Nacos * + * @author Nacos */ public enum AuthType { - /** - * auth type - */ - GROUP, GROUP_DATAID, TENANT_GROUP, TENANT + /** + * auth type + */ + GROUP, + GROUP_DATAID, + TENANT_GROUP, + TENANT } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java index cc1c294a4..0258a89ac 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java @@ -15,95 +15,113 @@ */ package com.alibaba.nacos.config.server.model; -import java.util.List; -import java.util.Map; - import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; import com.alibaba.nacos.config.server.utils.SingletonRepository.DataIdGroupIdCache; + +import java.util.List; +import java.util.Map; + /** * cache item - * @author Nacos * + * @author Nacos */ public class CacheItem { - - public CacheItem(String groupKey) { + + public CacheItem(String groupKey) { this.groupKey = DataIdGroupIdCache.getSingleton(groupKey); } - - public String getMd5() { - return md5; - } - public void setMd5(String md5) { - this.md5 = md5; - } - public long getLastModifiedTs() { - return lastModifiedTs; - } - public void setLastModifiedTs(long lastModifiedTs) { - this.lastModifiedTs = lastModifiedTs; - } - public boolean isBeta() { - return isBeta; - } - public void setBeta(boolean isBeta) { - this.isBeta = isBeta; - } - public String getMd54Beta() { - return md54Beta; - } - public void setMd54Beta(String md54Beta) { - this.md54Beta = md54Beta; - } - public List getIps4Beta() { - return ips4Beta; - } - public void setIps4Beta(List ips4Beta) { - this.ips4Beta = ips4Beta; - } - public long getLastModifiedTs4Beta() { - return lastModifiedTs4Beta; - } - public void setLastModifiedTs4Beta(long lastModifiedTs4Beta) { - this.lastModifiedTs4Beta = lastModifiedTs4Beta; - } - public SimpleReadWriteLock getRwLock() { - return rwLock; - } - public void setRwLock(SimpleReadWriteLock rwLock) { - this.rwLock = rwLock; - } - public String getGroupKey() { - return groupKey; - } - public Map getTagMd5() { - return tagMd5; - } - public Map getTagLastModifiedTs() { - return tagLastModifiedTs; - } - public void setTagMd5(Map tagMd5) { - this.tagMd5 = tagMd5; - } - public void setTagLastModifiedTs(Map tagLastModifiedTs) { - this.tagLastModifiedTs = tagLastModifiedTs; - } - final String groupKey; + public String getMd5() { + return md5; + } + + public void setMd5(String md5) { + this.md5 = md5; + } + + public long getLastModifiedTs() { + return lastModifiedTs; + } + + public void setLastModifiedTs(long lastModifiedTs) { + this.lastModifiedTs = lastModifiedTs; + } + + public boolean isBeta() { + return isBeta; + } + + public void setBeta(boolean isBeta) { + this.isBeta = isBeta; + } + + public String getMd54Beta() { + return md54Beta; + } + + public void setMd54Beta(String md54Beta) { + this.md54Beta = md54Beta; + } + + public List getIps4Beta() { + return ips4Beta; + } + + public void setIps4Beta(List ips4Beta) { + this.ips4Beta = ips4Beta; + } + + public long getLastModifiedTs4Beta() { + return lastModifiedTs4Beta; + } + + public void setLastModifiedTs4Beta(long lastModifiedTs4Beta) { + this.lastModifiedTs4Beta = lastModifiedTs4Beta; + } + + public SimpleReadWriteLock getRwLock() { + return rwLock; + } + + public void setRwLock(SimpleReadWriteLock rwLock) { + this.rwLock = rwLock; + } + + public String getGroupKey() { + return groupKey; + } + + public Map getTagMd5() { + return tagMd5; + } + + public Map getTagLastModifiedTs() { + return tagLastModifiedTs; + } + + public void setTagMd5(Map tagMd5) { + this.tagMd5 = tagMd5; + } + + public void setTagLastModifiedTs(Map tagLastModifiedTs) { + this.tagLastModifiedTs = tagLastModifiedTs; + } + + final String groupKey; public volatile String md5 = Constants.NULL; public volatile long lastModifiedTs; - + /** - * use for beta + * use for beta */ - public volatile boolean isBeta = false; - public volatile String md54Beta = Constants.NULL; - public volatile List ips4Beta; + public volatile boolean isBeta = false; + public volatile String md54Beta = Constants.NULL; + public volatile List ips4Beta; public volatile long lastModifiedTs4Beta; public volatile Map tagMd5; - public volatile Map tagLastModifiedTs; + public volatile Map tagLastModifiedTs; public SimpleReadWriteLock rwLock = new SimpleReadWriteLock(); - - + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java index 991e04c83..51b4d1444 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java @@ -19,82 +19,100 @@ import java.io.Serializable; /** * Config advance info - * - * @author Nacos * + * @author Nacos */ -public class ConfigAdvanceInfo implements Serializable { - static final long serialVersionUID = -1L; - private long createTime; - private long modifyTime; - private String createUser; - private String createIp; - private String desc; - private String use; - private String effect; - private String type; - private String schema; - private String configTags; - public long getCreateTime() { - return createTime; - } - public void setCreateTime(long createTime) { - this.createTime = createTime; - } - public long getModifyTime() { - return modifyTime; - } - public void setModifyTime(long modifyTime) { - this.modifyTime = modifyTime; - } - public String getCreateUser() { - return createUser; - } - public void setCreateUser(String createUser) { - this.createUser = createUser; - } - public String getCreateIp() { - return createIp; - } - public void setCreateIp(String createIp) { - this.createIp = createIp; - } - public String getDesc() { - return desc; - } - public void setDesc(String desc) { - this.desc = desc; - } - public String getUse() { - return use; - } - public void setUse(String use) { - this.use = use; - } - public String getEffect() { - return effect; - } - public void setEffect(String effect) { - this.effect = effect; - } - public String getType() { - return type; - } - public void setType(String type) { - this.type = type; - } - public String getSchema() { - return schema; - } - public void setSchema(String schema) { - this.schema = schema; - } - public String getConfigTags() { - return configTags; - } - public void setConfigTags(String configTags) { - this.configTags = configTags; - } +public class ConfigAdvanceInfo implements Serializable { + static final long serialVersionUID = -1L; + private long createTime; + private long modifyTime; + private String createUser; + private String createIp; + private String desc; + private String use; + private String effect; + private String type; + private String schema; + private String configTags; + public long getCreateTime() { + return createTime; + } + + public void setCreateTime(long createTime) { + this.createTime = createTime; + } + + public long getModifyTime() { + return modifyTime; + } + + public void setModifyTime(long modifyTime) { + this.modifyTime = modifyTime; + } + + public String getCreateUser() { + return createUser; + } + + public void setCreateUser(String createUser) { + this.createUser = createUser; + } + + public String getCreateIp() { + return createIp; + } + + public void setCreateIp(String createIp) { + this.createIp = createIp; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getUse() { + return use; + } + + public void setUse(String use) { + this.use = use; + } + + public String getEffect() { + return effect; + } + + public void setEffect(String effect) { + this.effect = effect; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getConfigTags() { + return configTags; + } + + public void setConfigTags(String configTags) { + this.configTags = configTags; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAllInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAllInfo.java index d5fac6140..218d263a2 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAllInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAllInfo.java @@ -17,98 +17,117 @@ package com.alibaba.nacos.config.server.model; /** * beta Info - * @author Nacos * + * @author Nacos */ public class ConfigAllInfo extends ConfigInfo { - /** - * - */ - private static final long serialVersionUID = 296578467953931353L; + /** + * + */ + private static final long serialVersionUID = 296578467953931353L; - private long createTime; - private long modifyTime; - private String createUser; - private String createIp; - private String desc; - private String use; - private String effect; - private String type; - private String schema; - private String configTags; - - public ConfigAllInfo() { - } + private long createTime; + private long modifyTime; + private String createUser; + private String createIp; + private String desc; + private String use; + private String effect; + private String type; + private String schema; + private String configTags; - public long getCreateTime() { - return createTime; - } - public void setCreateTime(long createTime) { - this.createTime = createTime; - } - public long getModifyTime() { - return modifyTime; - } - public void setModifyTime(long modifyTime) { - this.modifyTime = modifyTime; - } - public String getCreateUser() { - return createUser; - } - public void setCreateUser(String createUser) { - this.createUser = createUser; - } - public String getCreateIp() { - return createIp; - } - public void setCreateIp(String createIp) { - this.createIp = createIp; - } - public String getDesc() { - return desc; - } - public void setDesc(String desc) { - this.desc = desc; - } - public String getUse() { - return use; - } - public void setUse(String use) { - this.use = use; - } - public String getEffect() { - return effect; - } - public void setEffect(String effect) { - this.effect = effect; - } - public String getType() { - return type; - } - public void setType(String type) { - this.type = type; - } - public String getSchema() { - return schema; - } - public void setSchema(String schema) { - this.schema = schema; - } - public String getConfigTags() { - return configTags; - } - public void setConfigTags(String configTags) { - this.configTags = configTags; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + public ConfigAllInfo() { + } + + public long getCreateTime() { + return createTime; + } + + public void setCreateTime(long createTime) { + this.createTime = createTime; + } + + public long getModifyTime() { + return modifyTime; + } + + public void setModifyTime(long modifyTime) { + this.modifyTime = modifyTime; + } + + public String getCreateUser() { + return createUser; + } + + public void setCreateUser(String createUser) { + this.createUser = createUser; + } + + public String getCreateIp() { + return createIp; + } + + public void setCreateIp(String createIp) { + this.createIp = createIp; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getUse() { + return use; + } + + public void setUse(String use) { + this.use = use; + } + + public String getEffect() { + return effect; + } + + public void setEffect(String effect) { + this.effect = effect; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getConfigTags() { + return configTags; + } + + public void setConfigTags(String configTags) { + this.configTags = configTags; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigHistoryInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigHistoryInfo.java index d2bcbb246..86a23c8e6 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigHistoryInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigHistoryInfo.java @@ -19,31 +19,27 @@ import java.sql.Timestamp; /** * history Info - * @author Nacos * + * @author Nacos */ public class ConfigHistoryInfo { /** - * id, nid, - * data_id, group_id, - * content, md5, - * gmt_create, gmt_modified, (配置创建时间,配置变更时间) - * src_user, src_ip, (变更操作者) + * id, nid, data_id, group_id, content, md5, gmt_create, gmt_modified, (配置创建时间,配置变更时间) src_user, src_ip, (变更操作者) * op_type(变更操作类型) */ private long id; /** - * 上次改动历史的id + * 上次改动历史的id */ - private long lastId = -1; + private long lastId = -1; - private String dataId; - private String group; - private String tenant; - private String appName; - private String md5; + private String dataId; + private String group; + private String tenant; + private String appName; + private String md5; public long getId() { return id; @@ -77,14 +73,14 @@ public class ConfigHistoryInfo { this.group = group; } - public String getTenant() { - return tenant; - } + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } - public void setTenant(String tenant) { - this.tenant = tenant; - } - public String getContent() { return content; } @@ -117,9 +113,9 @@ public class ConfigHistoryInfo { this.opType = opType; } - public Timestamp getCreatedTime() { - return new Timestamp(createdTime.getTime()); - } + public Timestamp getCreatedTime() { + return new Timestamp(createdTime.getTime()); + } public void setCreatedTime(Timestamp createdTime) { this.createdTime = new Timestamp(createdTime.getTime()); @@ -134,29 +130,29 @@ public class ConfigHistoryInfo { } public String getAppName() { - return appName; - } + return appName; + } - public void setAppName(String appName) { - this.appName = appName; - } + public void setAppName(String appName) { + this.appName = appName; + } - public String getMd5() { - return md5; - } + public String getMd5() { + return md5; + } - public void setMd5(String md5) { - this.md5 = md5; - } + public void setMd5(String md5) { + this.md5 = md5; + } - private String content; + private String content; private String srcIp; private String srcUser; /** - * 操作类型, 包括插入、更新、删除 + * 操作类型, 包括插入、更新、删除 */ - private String opType; + private String opType; private Timestamp createdTime; private Timestamp lastModifiedTime; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java index 816753733..be0d2de0d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java @@ -17,67 +17,67 @@ package com.alibaba.nacos.config.server.model; /** * 配置信息类 - * + * * @author boyan * @date 2010-5-4 */ public class ConfigInfo extends ConfigInfoBase { - static final long serialVersionUID = -1L; + static final long serialVersionUID = -1L; - private String tenant; + private String tenant; - private String appName; + private String appName; - public ConfigInfo() { + public ConfigInfo() { - } + } - public ConfigInfo(String dataId, String group, String content) { - super(dataId, group, content); - } + public ConfigInfo(String dataId, String group, String content) { + super(dataId, group, content); + } - public ConfigInfo(String dataId, String group, String appName, String content) { - super(dataId, group, content); - this.appName = appName; - } - - public ConfigInfo(String dataId, String group, String tenant, String appName, String content) { - super(dataId, group, content); - this.tenant = tenant; - this.appName = appName; - } + public ConfigInfo(String dataId, String group, String appName, String content) { + super(dataId, group, content); + this.appName = appName; + } - public String getTenant() { - return tenant; - } + public ConfigInfo(String dataId, String group, String tenant, String appName, String content) { + super(dataId, group, content); + this.tenant = tenant; + this.appName = appName; + } - public void setTenant(String tenant) { - this.tenant = tenant; - } + public String getTenant() { + return tenant; + } - public String getAppName() { - return appName; - } + public void setTenant(String tenant) { + this.tenant = tenant; + } - public void setAppName(String appName) { - this.appName = appName; - } + public String getAppName() { + return appName; + } - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - - @Override - public String toString() { - return "ConfigInfo{" + "id=" + getId() + ", dataId='" + getDataId() + '\'' + ", group='" + getGroup() + '\'' - + ", tenant='" + tenant + '\'' + ", appName='" + appName + '\'' + ", content='" + getContent() + '\'' - + ", md5='" + getMd5() + '\'' + '}'; - } + public void setAppName(String appName) { + this.appName = appName; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return "ConfigInfo{" + "id=" + getId() + ", dataId='" + getDataId() + '\'' + ", group='" + getGroup() + '\'' + + ", tenant='" + tenant + '\'' + ", appName='" + appName + '\'' + ", content='" + getContent() + '\'' + + ", md5='" + getMd5() + '\'' + '}'; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Beta.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Beta.java index 26743da04..ae458a2a5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Beta.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Beta.java @@ -17,42 +17,41 @@ package com.alibaba.nacos.config.server.model; /** * beta Info - * @author Nacos * + * @author Nacos */ public class ConfigInfo4Beta extends ConfigInfo { - /** - * - */ - private static final long serialVersionUID = 296578467953931353L; + /** + * + */ + private static final long serialVersionUID = 296578467953931353L; - private String betaIps; - - - public ConfigInfo4Beta() { - } + private String betaIps; - public ConfigInfo4Beta(String dataId, String group, String appName, String content, String betaIps) { - super(dataId, group, appName, content); - this.betaIps = betaIps; - } + public ConfigInfo4Beta() { + } - public String getBetaIps() { - return betaIps; - } + public ConfigInfo4Beta(String dataId, String group, String appName, String content, String betaIps) { + super(dataId, group, appName, content); + this.betaIps = betaIps; + } - public void setBetaIps(String betaIps) { - this.betaIps = betaIps; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + public String getBetaIps() { + return betaIps; + } + + public void setBetaIps(String betaIps) { + this.betaIps = betaIps; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Tag.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Tag.java index f84bf7e5a..3f521d4a2 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Tag.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Tag.java @@ -14,45 +14,45 @@ * limitations under the License. */ package com.alibaba.nacos.config.server.model; + /** * tag info - * @author Nacos * + * @author Nacos */ public class ConfigInfo4Tag extends ConfigInfo { - /** - * - */ - private static final long serialVersionUID = 296578467953931353L; + /** + * + */ + private static final long serialVersionUID = 296578467953931353L; - private String tag; - - - public ConfigInfo4Tag() { - } + private String tag; - public ConfigInfo4Tag(String dataId, String group, String tag, String appName, String content) { - super(dataId, group, appName, content); - this.tag = tag; - } + public ConfigInfo4Tag() { + } - public String getTag() { - return tag; - } + public ConfigInfo4Tag(String dataId, String group, String tag, String appName, String content) { + super(dataId, group, appName, content); + this.tag = tag; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } - public void setTag(String tag) { - this.tag = tag; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoAggr.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoAggr.java index fd5d6d406..a7627cf08 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoAggr.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoAggr.java @@ -17,12 +17,10 @@ package com.alibaba.nacos.config.server.model; import java.io.Serializable; - /** * 聚合前的配置信息类 - * + * * @author leiwen.zh - * */ public class ConfigInfoAggr implements Serializable { @@ -33,82 +31,69 @@ public class ConfigInfoAggr implements Serializable { private String dataId; private String group; private String datumId; - private String tenant; + private String tenant; private String appName; private String content; - public ConfigInfoAggr(String dataId, String group, String datumId, String content) { this.dataId = dataId; this.group = group; this.datumId = datumId; this.content = content; } - - public ConfigInfoAggr(String dataId, String group, String datumId, String appName, String content) { - this.dataId = dataId; - this.group = group; - this.datumId = datumId; - this.appName = appName; - this.content = content; - } + public ConfigInfoAggr(String dataId, String group, String datumId, String appName, String content) { + this.dataId = dataId; + this.group = group; + this.datumId = datumId; + this.appName = appName; + this.content = content; + } public ConfigInfoAggr() { } - public long getId() { return id; } - public void setId(long id) { this.id = id; } - public String getDataId() { return dataId; } - public void setDataId(String dataId) { this.dataId = dataId; } - public String getGroup() { return group; } - public void setGroup(String group) { this.group = group; } - public String getDatumId() { return datumId; } - public void setDatumId(String datumId) { this.datumId = datumId; } - public String getContent() { return content; } - public void setContent(String content) { this.content = content; } - @Override public int hashCode() { final int prime = 31; @@ -120,7 +105,6 @@ public class ConfigInfoAggr implements Serializable { return result; } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -132,65 +116,58 @@ public class ConfigInfoAggr implements Serializable { if (getClass() != obj.getClass()) { return false; } - ConfigInfoAggr other = (ConfigInfoAggr) obj; + ConfigInfoAggr other = (ConfigInfoAggr)obj; if (content == null) { if (other.content != null) { return false; } - } - else if (!content.equals(other.content)) { + } else if (!content.equals(other.content)) { return false; } if (dataId == null) { if (other.dataId != null) { return false; } - } - else if (!dataId.equals(other.dataId)) { + } else if (!dataId.equals(other.dataId)) { return false; } if (datumId == null) { if (other.datumId != null) { return false; } - } - else if (!datumId.equals(other.datumId)) { + } else if (!datumId.equals(other.datumId)) { return false; } if (group == null) { if (other.group != null) { return false; } - } - else if (!group.equals(other.group)) { - return false; + } else if (!group.equals(other.group)) { + return false; } return true; } - @Override public String toString() { return "ConfigInfoAggr [dataId=" + dataId + ", group=" + group + ", datumId=" + datumId + ", content=" - + content + "]"; + + content + "]"; } + public String getAppName() { + return appName; + } - public String getAppName() { - return appName; - } + public void setAppName(String appName) { + this.appName = appName; + } + public String getTenant() { + return tenant; + } - public void setAppName(String appName) { - this.appName = appName; - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } + public void setTenant(String tenant) { + this.tenant = tenant; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java index fdf519f5f..94cb5ef32 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java @@ -15,201 +15,200 @@ */ package com.alibaba.nacos.config.server.model; +import com.alibaba.nacos.config.server.utils.MD5; + import java.io.PrintWriter; import java.io.Serializable; -import com.alibaba.nacos.config.server.utils.MD5; - /** * 不能增加字段,为了兼容老前台接口(老接口增加一个字段会出现不兼容问题)设置的model。 - * - * @author Nacos * + * @author Nacos */ public class ConfigInfoBase implements Serializable, Comparable { - static final long serialVersionUID = -1L; - - /** - * 不能增加字段 - */ - private long id; - private String dataId; - private String group; - private String content; - private String md5; + static final long serialVersionUID = -1L; - public ConfigInfoBase() { + /** + * 不能增加字段 + */ + private long id; + private String dataId; + private String group; + private String content; + private String md5; - } + public ConfigInfoBase() { - public ConfigInfoBase(String dataId, String group, String content) { - this.dataId = dataId; - this.group = group; - this.content = content; - if (this.content != null) { - this.md5 = MD5.getInstance().getMD5String(this.content); - } - } + } - public long getId() { - return id; - } + public ConfigInfoBase(String dataId, String group, String content) { + this.dataId = dataId; + this.group = group; + this.content = content; + if (this.content != null) { + this.md5 = MD5.getInstance().getMD5String(this.content); + } + } - public void setId(long id) { - this.id = id; - } + public long getId() { + return id; + } - public String getDataId() { - return dataId; - } + public void setId(long id) { + this.id = id; + } - public void setDataId(String dataId) { - this.dataId = dataId; - } + public String getDataId() { + return dataId; + } - public String getGroup() { - return group; - } + public void setDataId(String dataId) { + this.dataId = dataId; + } - public void setGroup(String group) { - this.group = group; - } + public String getGroup() { + return group; + } - public String getContent() { - return content; - } + public void setGroup(String group) { + this.group = group; + } - public void setContent(String content) { - this.content = content; - } + public String getContent() { + return content; + } - public String getMd5() { - return md5; - } + public void setContent(String content) { + this.content = content; + } - public void setMd5(String md5) { - this.md5 = md5; - } + public String getMd5() { + return md5; + } - public void dump(PrintWriter writer) { - writer.write(this.content); - } + public void setMd5(String md5) { + this.md5 = md5; + } - public int compareTo(ConfigInfoBase o) { - if (o == null) { - return 1; - } - if (this.dataId == null ){ - if (o.getDataId() == null) { - return 0; - } else { - return -1; - } - } else { - if (o.getDataId() == null) { - return 1; - } else { - int cmpDataId = this.dataId.compareTo(o.getDataId()); - if (cmpDataId != 0) { - return cmpDataId; - } - } - } - - if (this.group == null) { - if (o.getGroup() == null) { - return 0; - } else { - return -1; - } - } else { - if (o.getGroup() == null) { - return 1; - } else { - int cmpGroup = this.group.compareTo(o.getGroup()); - if (cmpGroup != 0) { - return cmpGroup; - } - } - } - - if (this.content == null) { - if (o.getContent() == null) { - return 0; - } else { - return -1; - } - } else { - if (o.getContent() == null) { - return 1; - } else { - int cmpContent = this.content.compareTo(o.getContent()); - if (cmpContent != 0) { - return cmpContent; - } - } - } - return 0; - } + public void dump(PrintWriter writer) { + writer.write(this.content); + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((content == null) ? 0 : content.hashCode()); - result = prime * result + ((dataId == null) ? 0 : dataId.hashCode()); - result = prime * result + ((group == null) ? 0 : group.hashCode()); - result = prime * result + ((md5 == null) ? 0 : md5.hashCode()); - return result; - } + public int compareTo(ConfigInfoBase o) { + if (o == null) { + return 1; + } + if (this.dataId == null) { + if (o.getDataId() == null) { + return 0; + } else { + return -1; + } + } else { + if (o.getDataId() == null) { + return 1; + } else { + int cmpDataId = this.dataId.compareTo(o.getDataId()); + if (cmpDataId != 0) { + return cmpDataId; + } + } + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ConfigInfoBase other = (ConfigInfoBase) obj; - if (content == null) { - if (other.content != null) { - return false; - } - } else if (!content.equals(other.content)) { - return false; - } - if (dataId == null) { - if (other.dataId != null) { - return false; - } - } else if (!dataId.equals(other.dataId)) { - return false; - } - if (group == null) { - if (other.group != null) { - return false; - } - } else if (!group.equals(other.group)) { - return false; - } - if (md5 == null) { - if (other.md5 != null) { - return false; - } - } else if (!md5.equals(other.md5)) { - return false; - } - return true; - } + if (this.group == null) { + if (o.getGroup() == null) { + return 0; + } else { + return -1; + } + } else { + if (o.getGroup() == null) { + return 1; + } else { + int cmpGroup = this.group.compareTo(o.getGroup()); + if (cmpGroup != 0) { + return cmpGroup; + } + } + } - @Override - public String toString() { - return "ConfigInfoBase{" + "id=" + id + ", dataId='" + dataId + '\'' - + ", group='" + group + '\'' + ", content='" + content + '\'' - + ", md5='" + md5 + '\'' + '}'; - } + if (this.content == null) { + if (o.getContent() == null) { + return 0; + } else { + return -1; + } + } else { + if (o.getContent() == null) { + return 1; + } else { + int cmpContent = this.content.compareTo(o.getContent()); + if (cmpContent != 0) { + return cmpContent; + } + } + } + return 0; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((content == null) ? 0 : content.hashCode()); + result = prime * result + ((dataId == null) ? 0 : dataId.hashCode()); + result = prime * result + ((group == null) ? 0 : group.hashCode()); + result = prime * result + ((md5 == null) ? 0 : md5.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ConfigInfoBase other = (ConfigInfoBase)obj; + if (content == null) { + if (other.content != null) { + return false; + } + } else if (!content.equals(other.content)) { + return false; + } + if (dataId == null) { + if (other.dataId != null) { + return false; + } + } else if (!dataId.equals(other.dataId)) { + return false; + } + if (group == null) { + if (other.group != null) { + return false; + } + } else if (!group.equals(other.group)) { + return false; + } + if (md5 == null) { + if (other.md5 != null) { + return false; + } + } else if (!md5.equals(other.md5)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "ConfigInfoBase{" + "id=" + id + ", dataId='" + dataId + '\'' + + ", group='" + group + '\'' + ", content='" + content + '\'' + + ", md5='" + md5 + '\'' + '}'; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java index 9c0e037df..e9dabeed6 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java @@ -17,68 +17,67 @@ package com.alibaba.nacos.config.server.model; /** * 不能增加字段,为了兼容老前台接口(老接口增加一个字段会出现不兼容问题)设置的model。 - * - * @author Nacos * + * @author Nacos */ public class ConfigInfoBaseEx extends ConfigInfoBase { - private static final long serialVersionUID = -1L; - //不能增加字段 - /** - * 批量查询时, 单条数据的状态码, 具体的状态码在Constants.java中 - */ - private int status; - /** - * 批量查询时, 单条数据的信息 - */ - private String message; + private static final long serialVersionUID = -1L; + //不能增加字段 + /** + * 批量查询时, 单条数据的状态码, 具体的状态码在Constants.java中 + */ + private int status; + /** + * 批量查询时, 单条数据的信息 + */ + private String message; - public ConfigInfoBaseEx() { - super(); - } + public ConfigInfoBaseEx() { + super(); + } - public ConfigInfoBaseEx(String dataId, String group, String content) { - super(dataId, group, content); - } + public ConfigInfoBaseEx(String dataId, String group, String content) { + super(dataId, group, content); + } - public ConfigInfoBaseEx(String dataId, String group, String content, - int status, String message) { - super(dataId, group, content); - this.status = status; - this.message = message; - } + public ConfigInfoBaseEx(String dataId, String group, String content, + int status, String message) { + super(dataId, group, content); + this.status = status; + this.message = message; + } - public int getStatus() { - return status; - } + public int getStatus() { + return status; + } - public void setStatus(int status) { - this.status = status; - } + public void setStatus(int status) { + this.status = status; + } - public String getMessage() { - return message; - } + public String getMessage() { + return message; + } - public void setMessage(String message) { - this.message = message; - } + public void setMessage(String message) { + this.message = message; + } - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public String toString() { - return "ConfigInfoBaseEx [status=" + status + ", message=" + message - + ", dataId=" + getDataId() + ", group()=" + getGroup() - + ", content()=" + getContent() + "]"; - } + @Override + public int hashCode() { + return super.hashCode(); + } - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + @Override + public String toString() { + return "ConfigInfoBaseEx [status=" + status + ", message=" + message + + ", dataId=" + getDataId() + ", group()=" + getGroup() + + ", content()=" + getContent() + "]"; + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoChanged.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoChanged.java index 539e890a9..e6ee76983 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoChanged.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoChanged.java @@ -17,9 +17,8 @@ package com.alibaba.nacos.config.server.model; /** * 变化的配置信息, 聚合时使用 - * + * * @author leiwen.zh - * */ public class ConfigInfoChanged { @@ -33,32 +32,26 @@ public class ConfigInfoChanged { this.setTenant(tenant); } - public ConfigInfoChanged() { } - public String getDataId() { return dataId; } - public void setDataId(String dataId) { this.dataId = dataId; } - public String getGroup() { return group; } - public void setGroup(String group) { this.group = group; } - @Override public int hashCode() { final int prime = 31; @@ -68,7 +61,6 @@ public class ConfigInfoChanged { return result; } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -80,40 +72,35 @@ public class ConfigInfoChanged { if (getClass() != obj.getClass()) { return false; } - ConfigInfoChanged other = (ConfigInfoChanged) obj; + ConfigInfoChanged other = (ConfigInfoChanged)obj; if (dataId == null) { if (other.dataId != null) { return false; } - } - else if (!dataId.equals(other.dataId)) { + } else if (!dataId.equals(other.dataId)) { return false; } if (group == null) { if (other.group != null) { return false; } - } - else if (!group.equals(other.group)) { + } else if (!group.equals(other.group)) { return false; } return true; } - @Override public String toString() { return "ConfigInfoChanged [dataId=" + dataId + ", group=" + group + "]"; } + public String getTenant() { + return tenant; + } - public String getTenant() { - return tenant; - } - - - public void setTenant(String tenant) { - this.tenant = tenant; - } + public void setTenant(String tenant) { + this.tenant = tenant; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java index 15ce6d068..ca46ffeec 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java @@ -17,20 +17,19 @@ package com.alibaba.nacos.config.server.model; /** * ConfigInfo的扩展类, 用于批量处理 - * + * * @author leiwen.zh - * */ public class ConfigInfoEx extends ConfigInfo { private static final long serialVersionUID = -1L; /** - * 批量查询时, 单条数据的状态码, 具体的状态码在Constants.java中 + * 批量查询时, 单条数据的状态码, 具体的状态码在Constants.java中 */ private int status; /** - * 批量查询时, 单条数据的信息 + * 批量查询时, 单条数据的信息 */ private String message; @@ -42,48 +41,44 @@ public class ConfigInfoEx extends ConfigInfo { super(dataId, group, content); } - public ConfigInfoEx(String dataId, String group, String content, int status, String message){ + public ConfigInfoEx(String dataId, String group, String content, int status, String message) { super(dataId, group, content); this.status = status; this.message = message; } - public int getStatus() { return status; } - public void setStatus(int status) { this.status = status; } - public String getMessage() { return message; } - public void setMessage(String message) { this.message = message; } - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + @Override + public int hashCode() { + return super.hashCode(); + } - @Override - public String toString() { - return "ConfigInfoEx [status=" + status + ", message=" + message - + ", dataId=" + getDataId() + ", group=" + getGroup() - + ", appName=" + getAppName() + ", content=" + getContent() - + "]"; - } + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return "ConfigInfoEx [status=" + status + ", message=" + message + + ", dataId=" + getDataId() + ", group=" + getGroup() + + ", appName=" + getAppName() + ", content=" + getContent() + + "]"; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoWrapper.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoWrapper.java index d0faac5e6..b9fab6b59 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoWrapper.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoWrapper.java @@ -14,34 +14,35 @@ * limitations under the License. */ package com.alibaba.nacos.config.server.model; + /** * ConfigInfo Wrapper - * @author Nacos * + * @author Nacos */ public class ConfigInfoWrapper extends ConfigInfo { - private static final long serialVersionUID = 4511997359365712505L; + private static final long serialVersionUID = 4511997359365712505L; - private long lastModified; + private long lastModified; - public ConfigInfoWrapper() { - } + public ConfigInfoWrapper() { + } - public long getLastModified() { - return lastModified; - } + public long getLastModified() { + return lastModified; + } - public void setLastModified(long lastModified) { - this.lastModified = lastModified; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigKey.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigKey.java index f726b8165..0be7b4ed1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigKey.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigKey.java @@ -19,52 +19,53 @@ import java.io.Serializable; /** * config key - * - * @author Nacos * + * @author Nacos */ public class ConfigKey implements Serializable { - /** - * - */ - private static final long serialVersionUID = -1748953484511867580L; + /** + * + */ + private static final long serialVersionUID = -1748953484511867580L; - private String appName; - private String dataId; - private String group; + private String appName; + private String dataId; + private String group; - public ConfigKey() { - }; + public ConfigKey() { + } - public ConfigKey(String appName, String dataId, String group) { - this.appName = appName; - this.dataId = dataId; - this.group = group; - } + ; - public String getAppName() { - return appName; - } + public ConfigKey(String appName, String dataId, String group) { + this.appName = appName; + this.dataId = dataId; + this.group = group; + } - public void setAppName(String appName) { - this.appName = appName; - } + public String getAppName() { + return appName; + } - public String getDataId() { - return dataId; - } + public void setAppName(String appName) { + this.appName = appName; + } - public void setDataId(String dataId) { - this.dataId = dataId; - } + public String getDataId() { + return dataId; + } - public String getGroup() { - return group; - } + public void setDataId(String dataId) { + this.dataId = dataId; + } - public void setGroup(String group) { - this.group = group; - } + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java index 916b954a9..c19a404ca 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/GroupInfo.java @@ -16,10 +16,11 @@ package com.alibaba.nacos.config.server.model; import java.io.Serializable; + /** * group info - * @author Nacos * + * @author Nacos */ public class GroupInfo implements Serializable { static final long serialVersionUID = -1L; @@ -28,12 +29,10 @@ public class GroupInfo implements Serializable { private String group; private String dataId; - public GroupInfo() { } - public GroupInfo(String address, String dataId, String group) { super(); this.address = address; @@ -41,47 +40,38 @@ public class GroupInfo implements Serializable { this.dataId = dataId; } - public long getId() { return id; } - public void setId(long id) { this.id = id; } - public String getAddress() { return address; } - public void setAddress(String address) { this.address = address; } - public String getGroup() { return group; } - public void setGroup(String group) { this.group = group; } - public String getDataId() { return dataId; } - public void setDataId(String dataId) { this.dataId = dataId; } - @Override public int hashCode() { final int prime = 31; @@ -92,7 +82,6 @@ public class GroupInfo implements Serializable { return result; } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -104,29 +93,26 @@ public class GroupInfo implements Serializable { if (getClass() != obj.getClass()) { return false; } - GroupInfo other = (GroupInfo) obj; + GroupInfo other = (GroupInfo)obj; if (address == null) { if (other.address != null) { return false; } - } - else if (!address.equals(other.address)) { + } else if (!address.equals(other.address)) { return false; } if (dataId == null) { if (other.dataId != null) { return false; } - } - else if (!dataId.equals(other.dataId)) { + } else if (!dataId.equals(other.dataId)) { return false; } if (group == null) { if (other.group != null) { return false; } - } - else if (!group.equals(other.group)) { + } else if (!group.equals(other.group)) { return false; } return true; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/GroupkeyListenserStatus.java b/config/src/main/java/com/alibaba/nacos/config/server/model/GroupkeyListenserStatus.java index 2ea42722d..a18bcfb7a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/GroupkeyListenserStatus.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/GroupkeyListenserStatus.java @@ -20,35 +20,34 @@ import java.util.Map; /** * litener status - * - * @author Nacos * + * @author Nacos */ -public class GroupkeyListenserStatus implements Serializable{ +public class GroupkeyListenserStatus implements Serializable { - /** - * 随机数 - */ - private static final long serialVersionUID = -2094829323598842474L; + /** + * 随机数 + */ + private static final long serialVersionUID = -2094829323598842474L; - private int collectStatus; - - private Map lisentersGroupkeyStatus; + private int collectStatus; - public int getCollectStatus() { - return collectStatus; - } + private Map lisentersGroupkeyStatus; - public void setCollectStatus(int collectStatus) { - this.collectStatus = collectStatus; - } + public int getCollectStatus() { + return collectStatus; + } - public Map getLisentersGroupkeyStatus() { - return lisentersGroupkeyStatus; - } + public void setCollectStatus(int collectStatus) { + this.collectStatus = collectStatus; + } - public void setLisentersGroupkeyStatus( - Map lisentersGroupkeyStatus) { - this.lisentersGroupkeyStatus = lisentersGroupkeyStatus; - } + public Map getLisentersGroupkeyStatus() { + return lisentersGroupkeyStatus; + } + + public void setLisentersGroupkeyStatus( + Map lisentersGroupkeyStatus) { + this.lisentersGroupkeyStatus = lisentersGroupkeyStatus; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/HistoryContext.java b/config/src/main/java/com/alibaba/nacos/config/server/model/HistoryContext.java index d3b9d5ca2..7335903d2 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/HistoryContext.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/HistoryContext.java @@ -17,105 +17,104 @@ package com.alibaba.nacos.config.server.model; /** * history context - * - * @author Nacos * + * @author Nacos */ public class HistoryContext { - public String serverId; + public String serverId; public String dataId; public String group; public String tenant; - private String appName; + private String appName; public boolean success; public int statusCode; public String statusMsg; public Page configs; + public HistoryContext(String serverId, String dataId, String group, int statusCode, String statusMsg, + Page configs) { + this.serverId = serverId; + this.dataId = dataId; + this.group = group; + this.statusCode = statusCode; + this.statusMsg = statusMsg; + this.configs = configs; + this.success = 200 == statusCode; + } - public HistoryContext(String serverId, String dataId, String group, int statusCode, String statusMsg, - Page configs) { - this.serverId = serverId; - this.dataId = dataId; - this.group = group; - this.statusCode = statusCode; - this.statusMsg = statusMsg; - this.configs = configs; - this.success = 200 == statusCode; - } - - public HistoryContext() { - } + public HistoryContext() { + } - public String getServerId() { - return serverId; - } + public String getServerId() { + return serverId; + } - public void setServerId(String serverId) { - this.serverId = serverId; - } + public void setServerId(String serverId) { + this.serverId = serverId; + } - public String getDataId() { - return dataId; - } + public String getDataId() { + return dataId; + } - public void setDataId(String dataId) { - this.dataId = dataId; - } + public void setDataId(String dataId) { + this.dataId = dataId; + } - public String getGroup() { - return group; - } + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } - public void setGroup(String group) { - this.group = group; - } - public String getTenant() { - return tenant; - } + return tenant; + } - public void setTenant(String tenant) { - this.tenant = tenant; - } - public boolean isSuccess() { - return success; - } + public void setTenant(String tenant) { + this.tenant = tenant; + } - public void setSuccess(boolean success) { - this.success = success; - } + public boolean isSuccess() { + return success; + } - public int getStatusCode() { - return statusCode; - } + public void setSuccess(boolean success) { + this.success = success; + } - public void setStatusCode(int statusCode) { - this.statusCode = statusCode; - } + public int getStatusCode() { + return statusCode; + } - public String getStatusMsg() { - return statusMsg; - } + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } - public void setStatusMsg(String statusMsg) { - this.statusMsg = statusMsg; - } + public String getStatusMsg() { + return statusMsg; + } - public Page getConfigs() { - return configs; - } + public void setStatusMsg(String statusMsg) { + this.statusMsg = statusMsg; + } - public void setConfigs(Page configs) { - this.configs = configs; - } + public Page getConfigs() { + return configs; + } - public String getAppName() { - return appName; - } + public void setConfigs(Page configs) { + this.configs = configs; + } - public void setAppName(String appName) { - this.appName = appName; - } + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java b/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java index 770a33f57..eaaaab6cc 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/Page.java @@ -19,69 +19,60 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; - /** * 分页对象 - * + * + * @param * @author boyan * @date 2010-5-6 - * @param */ public class Page implements Serializable { static final long serialVersionUID = -1L; /** * 总记录数 */ - private int totalCount; + private int totalCount; /** - * 页数 + * 页数 */ - private int pageNumber; + private int pageNumber; /** - * 总页数 + * 总页数 */ - private int pagesAvailable; + private int pagesAvailable; /** - * 该页内容 + * 该页内容 */ - private List pageItems = new ArrayList(); - + private List pageItems = new ArrayList(); public void setPageNumber(int pageNumber) { this.pageNumber = pageNumber; } - public void setPagesAvailable(int pagesAvailable) { this.pagesAvailable = pagesAvailable; } - public void setPageItems(List pageItems) { this.pageItems = pageItems; } - public int getTotalCount() { return totalCount; } - public void setTotalCount(int totalCount) { this.totalCount = totalCount; } - public int getPageNumber() { return pageNumber; } - public int getPagesAvailable() { return pagesAvailable; } - public List getPageItems() { return pageItems; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/RestPageResult.java b/config/src/main/java/com/alibaba/nacos/config/server/model/RestPageResult.java index e8056d78d..d66e382f1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/RestPageResult.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/RestPageResult.java @@ -19,65 +19,74 @@ import java.io.Serializable; /** * rest page result - * - * @author Nacos * - * @param - * data type + * @param data type + * @author Nacos */ public class RestPageResult implements Serializable { - /** - * - */ - private static final long serialVersionUID = -8048577763828650575L; + /** + * + */ + private static final long serialVersionUID = -8048577763828650575L; - private int code; - private String message; - private int total; - private int pageSize; - private int currentPage; - private T data; - public int getCode() { - return code; - } - public void setCode(int code) { - this.code = code; - } - public String getMessage() { - return message; - } - public void setMessage(String message) { - this.message = message; - } + private int code; + private String message; + private int total; + private int pageSize; + private int currentPage; + private T data; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public int getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(int currentPage) { + this.currentPage = currentPage; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } - public int getTotal() { - return total; - } - public void setTotal(int total) { - this.total = total; - } - public int getPageSize() { - return pageSize; - } - public void setPageSize(int pageSize) { - this.pageSize = pageSize; - } - public int getCurrentPage() { - return currentPage; - } - public void setCurrentPage(int currentPage) { - this.currentPage = currentPage; - } - public T getData() { - return data; - } - public void setData(T data) { - this.data = data; - } - public static long getSerialversionuid() { - return serialVersionUID; - } - - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/RestResult.java b/config/src/main/java/com/alibaba/nacos/config/server/model/RestResult.java index 50016a3f7..d357c5cfd 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/RestResult.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/RestResult.java @@ -19,65 +19,63 @@ import java.io.Serializable; /** * rest result class - * - * @author Nacos * * @param data type + * @author Nacos */ public class RestResult implements Serializable { - /** - * - */ - private static final long serialVersionUID = 6095433538316185017L; + /** + * + */ + private static final long serialVersionUID = 6095433538316185017L; - private int code; - private String message; - private T data; + private int code; + private String message; + private T data; - public RestResult() { - } + public RestResult() { + } - public RestResult(int code, String message, T data) { - this.code = code; - this.setMessage(message); - this.data = data; - } + public RestResult(int code, String message, T data) { + this.code = code; + this.setMessage(message); + this.data = data; + } - public RestResult(int code, T data) { - this.code = code; - this.data = data; - } + public RestResult(int code, T data) { + this.code = code; + this.data = data; + } - public RestResult(int code, String message) { - this.code = code; - this.setMessage(message); - } + public RestResult(int code, String message) { + this.code = code; + this.setMessage(message); + } - public int getCode() { - return code; - } + public int getCode() { + return code; + } - public void setCode(int code) { - this.code = code; - } + public void setCode(int code) { + this.code = code; + } - public String getMessage() { - return message; - } + public String getMessage() { + return message; + } - public void setMessage(String message) { - this.message = message; - } + public void setMessage(String message) { + this.message = message; + } - public T getData() { - return data; - } - - public void setData(T data) { - this.data = data; - } + public T getData() { + return data; + } + public void setData(T data) { + this.data = data; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/SampleResult.java b/config/src/main/java/com/alibaba/nacos/config/server/model/SampleResult.java index 499b0a81a..87259cb5f 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/SampleResult.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/SampleResult.java @@ -17,27 +17,28 @@ package com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Map; + /** * sample result - * @author Nacos * + * @author Nacos */ -public class SampleResult implements Serializable{ +public class SampleResult implements Serializable { - /** - * 随机数 - */ - private static final long serialVersionUID = 2587823382317389453L; + /** + * 随机数 + */ + private static final long serialVersionUID = 2587823382317389453L; - private Map lisentersGroupkeyStatus; - - public Map getLisentersGroupkeyStatus() { - return lisentersGroupkeyStatus; - } + private Map lisentersGroupkeyStatus; - public void setLisentersGroupkeyStatus( - Map lisentersGroupkeyStatus) { - this.lisentersGroupkeyStatus = lisentersGroupkeyStatus; - } + public Map getLisentersGroupkeyStatus() { + return lisentersGroupkeyStatus; + } + + public void setLisentersGroupkeyStatus( + Map lisentersGroupkeyStatus) { + this.lisentersGroupkeyStatus = lisentersGroupkeyStatus; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/SubInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/SubInfo.java index 0d06efb6a..aadeecd14 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/SubInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/SubInfo.java @@ -18,46 +18,56 @@ package com.alibaba.nacos.config.server.model; import java.sql.Timestamp; /** - * sub 数据结构体 - * @author Nacos + * sub 数据结构体 * + * @author Nacos */ public class SubInfo { - private String appName; - private String dataId; - private String group; - private String localIp; - private Timestamp date; - public String getAppName() { - return appName; - } - public String getDataId() { - return dataId; - } - public String getGroup() { - return group; - } - public Timestamp getDate() { - return new Timestamp(date.getTime()); - } - public void setAppName(String appName) { - this.appName = appName; - } - public void setDataId(String dataId) { - this.dataId = dataId; - } - public void setGroup(String group) { - this.group = group; - } - public void setDate(Timestamp date) { - this.date = new Timestamp(date.getTime()); - } - public String getLocalIp() { - return localIp; - } - public void setLocalIp(String localIp) { - this.localIp = localIp; - } + private String appName; + private String dataId; + private String group; + private String localIp; + private Timestamp date; + + public String getAppName() { + return appName; + } + + public String getDataId() { + return dataId; + } + + public String getGroup() { + return group; + } + + public Timestamp getDate() { + return new Timestamp(date.getTime()); + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public void setGroup(String group) { + this.group = group; + } + + public void setDate(Timestamp date) { + this.date = new Timestamp(date.getTime()); + } + + public String getLocalIp() { + return localIp; + } + + public void setLocalIp(String localIp) { + this.localIp = localIp; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/SubscriberStatus.java b/config/src/main/java/com/alibaba/nacos/config/server/model/SubscriberStatus.java index 9d49d1183..eba4032ab 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/SubscriberStatus.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/SubscriberStatus.java @@ -17,8 +17,8 @@ package com.alibaba.nacos.config.server.model; /** * subcriber status - * @author Nacos * + * @author Nacos */ public class SubscriberStatus { String groupKey; @@ -27,7 +27,7 @@ public class SubscriberStatus { Boolean status; String serverIp; - public SubscriberStatus(){} + public SubscriberStatus() {} public SubscriberStatus(String groupKey, Boolean status, String md5, Long lastTime) { this.groupKey = groupKey; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/TenantInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/TenantInfo.java new file mode 100644 index 000000000..43d988311 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/TenantInfo.java @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.config.server.model; + +/** + * tenant info + * + * @author Nacos + */ +public class TenantInfo { + + private String tenantId; + private String tenantName; + private String tenantDesc; + + public String getTenantId() { + return tenantId; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getTenantName() { + return tenantName; + } + + public void setTenantName(String tenantName) { + this.tenantName = tenantName; + } + + public String getTenantDesc() { + return tenantDesc; + } + + public void setTenantDesc(String tenantDesc) { + this.tenantDesc = tenantDesc; + } + +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationInfo.java index ef0968149..9936745ea 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationInfo.java @@ -15,98 +15,97 @@ */ package com.alibaba.nacos.config.server.model.app; -import com.alibaba.nacos.config.server.utils.SystemConfig; +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * app info - * - * @author Nacos * + * @author Nacos */ public class ApplicationInfo { - private static final long LOCK_EXPIRE_DURATION = 30 * 1000; - private static final long RECENTLY_DURATION = 24 * 60 * 60 * 1000; + private static final long LOCK_EXPIRE_DURATION = 30 * 1000; + private static final long RECENTLY_DURATION = 24 * 60 * 60 * 1000; - private String appName; + private String appName; - private boolean isDynamicCollectDisabled = false; + private boolean isDynamicCollectDisabled = false; - private long lastSubscribeInfoCollectedTime = 0L; + private long lastSubscribeInfoCollectedTime = 0L; - private String subInfoCollectLockOwner = null; + private String subInfoCollectLockOwner = null; - private long subInfoCollectLockExpireTime = 0L; + private long subInfoCollectLockExpireTime = 0L; - public ApplicationInfo(String appName) { - this.appName = appName; - } + public ApplicationInfo(String appName) { + this.appName = appName; + } - public String getAppName() { - return appName; - } + public String getAppName() { + return appName; + } - public void setAppName(String appName) { - this.appName = appName; - } + public void setAppName(String appName) { + this.appName = appName; + } - public boolean isDynamicCollectDisabled() { - return isDynamicCollectDisabled; - } + public boolean isDynamicCollectDisabled() { + return isDynamicCollectDisabled; + } - public void setDynamicCollectDisabled(boolean isDynamicCollectDisabled) { - this.isDynamicCollectDisabled = isDynamicCollectDisabled; - } + public void setDynamicCollectDisabled(boolean isDynamicCollectDisabled) { + this.isDynamicCollectDisabled = isDynamicCollectDisabled; + } - public long getLastSubscribeInfoCollectedTime() { - return lastSubscribeInfoCollectedTime; - } + public long getLastSubscribeInfoCollectedTime() { + return lastSubscribeInfoCollectedTime; + } - public void setLastSubscribeInfoCollectedTime( - long lastSubscribeInfoCollectedTime) { - this.lastSubscribeInfoCollectedTime = lastSubscribeInfoCollectedTime; - } + public void setLastSubscribeInfoCollectedTime( + long lastSubscribeInfoCollectedTime) { + this.lastSubscribeInfoCollectedTime = lastSubscribeInfoCollectedTime; + } - public String getSubInfoCollectLockOwner() { - return subInfoCollectLockOwner; - } + public String getSubInfoCollectLockOwner() { + return subInfoCollectLockOwner; + } - public void setSubInfoCollectLockOwner(String subInfoCollectLockOwner) { - this.subInfoCollectLockOwner = subInfoCollectLockOwner; - } + public void setSubInfoCollectLockOwner(String subInfoCollectLockOwner) { + this.subInfoCollectLockOwner = subInfoCollectLockOwner; + } - public long getSubInfoCollectLockExpireTime() { - return subInfoCollectLockExpireTime; - } + public long getSubInfoCollectLockExpireTime() { + return subInfoCollectLockExpireTime; + } - public void setSubInfoCollectLockExpireTime( - long subInfoCollectLockExpireTime) { - this.subInfoCollectLockExpireTime = subInfoCollectLockExpireTime; - } + public void setSubInfoCollectLockExpireTime( + long subInfoCollectLockExpireTime) { + this.subInfoCollectLockExpireTime = subInfoCollectLockExpireTime; + } - public boolean isSubInfoRecentlyCollected() { - if (System.currentTimeMillis() - this.lastSubscribeInfoCollectedTime < RECENTLY_DURATION) { - return true; - } - return false; - } + public boolean isSubInfoRecentlyCollected() { + if (System.currentTimeMillis() - this.lastSubscribeInfoCollectedTime < RECENTLY_DURATION) { + return true; + } + return false; + } - public boolean canCurrentServerOwnTheLock() { - boolean currentOwnerIsMe = subInfoCollectLockOwner==null? true:SystemConfig.LOCAL_IP - .equals(subInfoCollectLockOwner); + public boolean canCurrentServerOwnTheLock() { + boolean currentOwnerIsMe = subInfoCollectLockOwner == null || LOCAL_IP + .equals(subInfoCollectLockOwner); - if (currentOwnerIsMe) { - return true; - } - if (System.currentTimeMillis() - this.subInfoCollectLockExpireTime > LOCK_EXPIRE_DURATION) { - return true; - } + if (currentOwnerIsMe) { + return true; + } + if (System.currentTimeMillis() - this.subInfoCollectLockExpireTime > LOCK_EXPIRE_DURATION) { + return true; + } - return false; - } - - public String currentServer(){ - return SystemConfig.LOCAL_IP; - } + return false; + } + + public String currentServer() { + return LOCAL_IP; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationPublishRecord.java b/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationPublishRecord.java index fbb13bdef..4d2d4e843 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationPublishRecord.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/app/ApplicationPublishRecord.java @@ -14,36 +14,36 @@ * limitations under the License. */ package com.alibaba.nacos.config.server.model.app; + /** * Application Publish Record - * @author Nacos * + * @author Nacos */ public class ApplicationPublishRecord { - private String appName; - private GroupKey configInfo; + private String appName; + private GroupKey configInfo; - public ApplicationPublishRecord(String appName, String dataId, String groupId){ - this.appName = appName; - this.configInfo = new GroupKey(dataId, groupId); - } - - public String getAppName() { - return appName; - } + public ApplicationPublishRecord(String appName, String dataId, String groupId) { + this.appName = appName; + this.configInfo = new GroupKey(dataId, groupId); + } - public void setAppName(String appName) { - this.appName = appName; - } + public String getAppName() { + return appName; + } - public GroupKey getConfigInfo() { - return configInfo; - } + public void setAppName(String appName) { + this.appName = appName; + } - public void setConfigInfo(GroupKey configInfo) { - this.configInfo = configInfo; - } + public GroupKey getConfigInfo() { + return configInfo; + } + + public void setConfigInfo(GroupKey configInfo) { + this.configInfo = configInfo; + } - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/app/GroupKey.java b/config/src/main/java/com/alibaba/nacos/config/server/model/app/GroupKey.java index 51b5cf512..287d175a5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/app/GroupKey.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/app/GroupKey.java @@ -19,51 +19,49 @@ import com.alibaba.nacos.config.server.utils.GroupKey2; /** * config key util - * - * @author Nacos * + * @author Nacos */ public class GroupKey extends GroupKey2 { - private String dataId; - private String group; + private String dataId; + private String group; - public GroupKey(String dataId, String group) { - this.dataId = dataId; - this.group = group; - } + public GroupKey(String dataId, String group) { + this.dataId = dataId; + this.group = group; + } - public GroupKey(String groupKeyString) { - String[] groupKeys = parseKey(groupKeyString); - this.dataId = groupKeys[0]; - this.group = groupKeys[1]; - } + public GroupKey(String groupKeyString) { + String[] groupKeys = parseKey(groupKeyString); + this.dataId = groupKeys[0]; + this.group = groupKeys[1]; + } - public String getDataId() { - return dataId; - } + public String getDataId() { + return dataId; + } - public void setDataId(String dataId) { - this.dataId = dataId; - } + public void setDataId(String dataId) { + this.dataId = dataId; + } - public String getGroup() { - return group; - } + public String getGroup() { + return group; + } - public void setGroup(String group) { - this.group = group; - } + public void setGroup(String group) { + this.group = group; + } - public String toString() { - return dataId + "+" + group; - } - - - public String getGroupkeyString() { - return getKey(dataId, group); - } - - //TODO : equal as we use Set + public String toString() { + return dataId + "+" + group; + } + + public String getGroupkeyString() { + return getKey(dataId, group); + } + + //TODO : equal as we use Set } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/app/MonitorInfo.java b/config/src/main/java/com/alibaba/nacos/config/server/model/app/MonitorInfo.java index 9b5a7cfcc..088d106c5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/app/MonitorInfo.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/app/MonitorInfo.java @@ -17,59 +17,87 @@ package com.alibaba.nacos.config.server.model.app; /** * Created by qingliang on 2017/7/20. - * + * * @author Nacos */ public class MonitorInfo { - /** 可使用内存. */ + /** + * 可使用内存. + */ private long totalMemory; - /** 剩余内存. */ + /** + * 剩余内存. + */ private long freeMemory; - /** 最大可使用内存. */ + /** + * 最大可使用内存. + */ private volatile long maxMemory; - /** cpu使用率. */ + /** + * cpu使用率. + */ private double cpuRatio; - /** 系统负载. */ + /** + * 系统负载. + */ private double load; - /** ygc次数 */ + /** + * ygc次数 + */ private int ygc; - /** ygc时间 */ + /** + * ygc时间 + */ private double ygct; - /** fgc次数 */ + /** + * fgc次数 + */ private int fgc; - /** fgc时间 */ + /** + * fgc时间 + */ private double fgct; - /** gc时间 */ + /** + * gc时间 + */ private double gct; - public long getFreeMemory() { return freeMemory; } + public void setFreeMemory(long freeMemory) { this.freeMemory = freeMemory; } + public long getMaxMemory() { return maxMemory; } + public void setMaxMemory(long maxMemory) { this.maxMemory = maxMemory; } + public long getTotalMemory() { return totalMemory; } + public void setTotalMemory(long totalMemory) { this.totalMemory = totalMemory; } + public double getCpuRatio() { return cpuRatio; } + public void setCpuRatio(int cpuRatio) { this.cpuRatio = cpuRatio; } + public double getLoad() { return load; } + public void setLoad(int load) { this.load = load; } @@ -117,17 +145,17 @@ public class MonitorInfo { @Override public String toString() { return "MonitorInfo{" + - "totalMemory=" + totalMemory + - ", freeMemory=" + freeMemory + - ", maxMemory=" + maxMemory + - ", cpuRatio=" + cpuRatio + - ", load=" + load + - ", ygc=" + ygc + - ", ygct=" + ygct + - ", fgc=" + fgc + - ", fgct=" + fgct + - ", gct=" + gct + - '}'; + "totalMemory=" + totalMemory + + ", freeMemory=" + freeMemory + + ", maxMemory=" + maxMemory + + ", cpuRatio=" + cpuRatio + + ", load=" + load + + ", ygc=" + ygc + + ", ygct=" + ygct + + ", fgc=" + fgc + + ", fgct=" + fgct + + ", gct=" + gct + + '}'; } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/Capacity.java b/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/Capacity.java index 7afacbf85..9c83da41e 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/Capacity.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/Capacity.java @@ -19,95 +19,96 @@ import java.sql.Timestamp; /** * Capacity + * * @author hexu.hxy * @date 2018/3/13 */ public class Capacity { - private Long id; - private Integer quota; - private Integer usage; - private Integer maxSize; - private Integer maxAggrCount; - private Integer maxAggrSize; - private Timestamp gmtCreate; - private Timestamp gmtModified; + private Long id; + private Integer quota; + private Integer usage; + private Integer maxSize; + private Integer maxAggrCount; + private Integer maxAggrSize; + private Timestamp gmtCreate; + private Timestamp gmtModified; - public Long getId() { - return id; - } + public Long getId() { + return id; + } - public void setId(Long id) { - this.id = id; - } + public void setId(Long id) { + this.id = id; + } - public Integer getQuota() { - return quota; - } + public Integer getQuota() { + return quota; + } - public void setQuota(Integer quota) { - this.quota = quota; - } + public void setQuota(Integer quota) { + this.quota = quota; + } - public Integer getUsage() { - return usage; - } + public Integer getUsage() { + return usage; + } - public void setUsage(Integer usage) { - this.usage = usage; - } + public void setUsage(Integer usage) { + this.usage = usage; + } - public Integer getMaxSize() { - return maxSize; - } + public Integer getMaxSize() { + return maxSize; + } - public void setMaxSize(Integer maxSize) { - this.maxSize = maxSize; - } + public void setMaxSize(Integer maxSize) { + this.maxSize = maxSize; + } - public Integer getMaxAggrCount() { - return maxAggrCount; - } + public Integer getMaxAggrCount() { + return maxAggrCount; + } - public void setMaxAggrCount(Integer maxAggrCount) { - this.maxAggrCount = maxAggrCount; - } + public void setMaxAggrCount(Integer maxAggrCount) { + this.maxAggrCount = maxAggrCount; + } - public Integer getMaxAggrSize() { - return maxAggrSize; - } + public Integer getMaxAggrSize() { + return maxAggrSize; + } - public void setMaxAggrSize(Integer maxAggrSize) { - this.maxAggrSize = maxAggrSize; - } + public void setMaxAggrSize(Integer maxAggrSize) { + this.maxAggrSize = maxAggrSize; + } - public Timestamp getGmtCreate() { - if (gmtCreate == null) { - return null; - } - return new Timestamp(gmtCreate.getTime()); - } + public Timestamp getGmtCreate() { + if (gmtCreate == null) { + return null; + } + return new Timestamp(gmtCreate.getTime()); + } - public void setGmtCreate(Timestamp gmtCreate) { - if (gmtCreate == null) { - this.gmtCreate = null; - } else { - this.gmtCreate = new Timestamp(gmtCreate.getTime()); - } + public void setGmtCreate(Timestamp gmtCreate) { + if (gmtCreate == null) { + this.gmtCreate = null; + } else { + this.gmtCreate = new Timestamp(gmtCreate.getTime()); + } - } + } - public Timestamp getGmtModified() { - if (gmtModified == null) { - return null; - } - return new Timestamp(gmtModified.getTime()); - } + public Timestamp getGmtModified() { + if (gmtModified == null) { + return null; + } + return new Timestamp(gmtModified.getTime()); + } - public void setGmtModified(Timestamp gmtModified) { - if (gmtModified == null) { - this.gmtModified = null; - } else { - this.gmtModified = new Timestamp(gmtModified.getTime()); - } - } + public void setGmtModified(Timestamp gmtModified) { + if (gmtModified == null) { + this.gmtModified = null; + } else { + this.gmtModified = new Timestamp(gmtModified.getTime()); + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/GroupCapacity.java b/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/GroupCapacity.java index 3a8d612ee..cf91ea55c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/GroupCapacity.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/GroupCapacity.java @@ -17,17 +17,18 @@ package com.alibaba.nacos.config.server.model.capacity; /** * Group Capacity + * * @author hexu.hxy * @date 2018/3/13 */ public class GroupCapacity extends Capacity { - private String group; + private String group; - public String getGroup() { - return group; - } + public String getGroup() { + return group; + } - public void setGroup(String group) { - this.group = group; - } + public void setGroup(String group) { + this.group = group; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/TenantCapacity.java b/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/TenantCapacity.java index 9f85382eb..54ab49bdd 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/TenantCapacity.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/capacity/TenantCapacity.java @@ -17,17 +17,18 @@ package com.alibaba.nacos.config.server.model.capacity; /** * Tenant Capacity + * * @author hexu.hxy * @date 2018/3/13 */ public class TenantCapacity extends Capacity { - private String tenant; + private String tenant; - public String getTenant() { - return tenant; - } + public String getTenant() { + return tenant; + } - public void setTenant(String tenant) { - this.tenant = tenant; - } + public void setTenant(String tenant) { + this.tenant = tenant; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java index d9e5bf9c7..a79bf7cf8 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java @@ -15,21 +15,22 @@ */ package com.alibaba.nacos.config.server.monitor; -import static com.alibaba.nacos.config.server.utils.LogUtil.memoryLog; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; import com.alibaba.nacos.config.server.service.ClientTrackService; import com.alibaba.nacos.config.server.service.ConfigService; import com.alibaba.nacos.config.server.service.TimerTaskService; import com.alibaba.nacos.config.server.service.notify.AsyncNotifyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.nacos.config.server.utils.LogUtil.memoryLog; /** * Memory monitor - * - * @author Nacos * + * @author Nacos */ @Service public class MemoryMonitor { @@ -37,24 +38,23 @@ public class MemoryMonitor { public MemoryMonitor(AsyncNotifyService notifySingleService) { TimerTaskService.scheduleWithFixedDelay(new PrintMemoryTask(), DELAY_SECONDS, - DELAY_SECONDS, TimeUnit.SECONDS); - + DELAY_SECONDS, TimeUnit.SECONDS); + TimerTaskService.scheduleWithFixedDelay(new PrintGetConfigResponeTask(), DELAY_SECONDS, - DELAY_SECONDS, TimeUnit.SECONDS); + DELAY_SECONDS, TimeUnit.SECONDS); TimerTaskService.scheduleWithFixedDelay(new NotifyTaskQueueMonitorTask(notifySingleService), DELAY_SECONDS, - DELAY_SECONDS, TimeUnit.SECONDS); + DELAY_SECONDS, TimeUnit.SECONDS); } - static final long DELAY_SECONDS = 10; } -class PrintGetConfigResponeTask implements Runnable{ - @Override - public void run() { - memoryLog.info(ResponseMonitor.getStringForPrint()); - } +class PrintGetConfigResponeTask implements Runnable { + @Override + public void run() { + memoryLog.info(ResponseMonitor.getStringForPrint()); + } } class PrintMemoryTask implements Runnable { @@ -63,11 +63,11 @@ class PrintMemoryTask implements Runnable { int groupCount = ConfigService.groupCount(); int subClientCount = ClientTrackService.subscribeClientCount(); long subCount = ClientTrackService.subscriberCount(); - memoryLog.info("groupCount={}, subscriberClientCount={}, subscriberCount={}", groupCount, subClientCount, subCount); + memoryLog.info("groupCount={}, subscriberClientCount={}, subscriberCount={}", groupCount, subClientCount, + subCount); } } - class NotifyTaskQueueMonitorTask implements Runnable { final private AsyncNotifyService notifySingleService; @@ -77,15 +77,16 @@ class NotifyTaskQueueMonitorTask implements Runnable { @Override public void run() { - - memoryLog.info("notifySingleServiceThreadPool-{}, toNotifyTaskSize={}", - new Object[] {((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getClass().getName(), ((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getQueue().size() }); - -// for(Map.Entry entry: notifySingleService.getExecutors().entrySet()) { -// ThreadPoolExecutor pool = (ThreadPoolExecutor) entry.getValue(); -// String target = entry.getKey(); -// memoryLog.info("notifySingleServiceThreadPool-{}, toNotifyTaskSize={}", -// new Object[] { target, pool.getQueue().size() }); -// } + + memoryLog.info("notifySingleServiceThreadPool-{}, toNotifyTaskSize={}", + new Object[] {((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getClass().getName(), + ((ScheduledThreadPoolExecutor)notifySingleService.getExecutor()).getQueue().size()}); + + // for(Map.Entry entry: notifySingleService.getExecutors().entrySet()) { + // ThreadPoolExecutor pool = (ThreadPoolExecutor) entry.getValue(); + // String target = entry.getKey(); + // memoryLog.info("notifySingleServiceThreadPool-{}, toNotifyTaskSize={}", + // new Object[] { target, pool.getQueue().size() }); + // } } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/monitor/ResponseMonitor.java b/config/src/main/java/com/alibaba/nacos/config/server/monitor/ResponseMonitor.java index ea98ae7b0..2220dea3b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/monitor/ResponseMonitor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/monitor/ResponseMonitor.java @@ -20,78 +20,68 @@ import java.util.concurrent.atomic.AtomicLong; /** * Response Monitory - * - * @author Nacos * + * @author Nacos */ public class ResponseMonitor { - static AtomicLong[] getConfigCountDetail = new AtomicLong[8]; - static AtomicLong getConfigCount = new AtomicLong(); - private static final int MS_50 = 50; - private static final int MS_100 = 100; - private static final int MS_200 = 200; - private static final int MS_500 = 500; - private static final int MS_1000 = 1000; - private static final int MS_2000 = 2000; - private static final int MS_3000 = 3000; + static AtomicLong[] getConfigCountDetail = new AtomicLong[8]; + static AtomicLong getConfigCount = new AtomicLong(); + private static final int MS_50 = 50; + private static final int MS_100 = 100; + private static final int MS_200 = 200; + private static final int MS_500 = 500; + private static final int MS_1000 = 1000; + private static final int MS_2000 = 2000; + private static final int MS_3000 = 3000; - static{ - refresh(); - } - - public static void refresh(){ - for(int i = 0; i< getConfigCountDetail.length;i++){ - getConfigCountDetail[i] = new AtomicLong(); - } - } - - public static void addConfigTime(long time){ - getConfigCount.incrementAndGet(); - if(time < MS_50){ - getConfigCountDetail[0].incrementAndGet(); - } else if(time < MS_100) { - getConfigCountDetail[1].incrementAndGet(); - } else if (time < MS_200){ - getConfigCountDetail[2].incrementAndGet(); - } else if(time < MS_500){ - getConfigCountDetail[3].incrementAndGet(); - } else if(time < MS_1000){ - getConfigCountDetail[4].incrementAndGet(); - } else if(time < MS_2000){ - getConfigCountDetail[5].incrementAndGet(); - } else if(time < MS_3000){ - getConfigCountDetail[6].incrementAndGet(); - } else { - getConfigCountDetail[7].incrementAndGet(); - } - } - - public static String getStringForPrint(){ - DecimalFormat df = new DecimalFormat("##.0"); - StringBuilder s = new StringBuilder("getConfig monitor:\r\n"); - s.append("0-50ms:" + df.format(getConfigCountDetail[0].getAndSet(0)*100/ getConfigCount.get())).append("%\r\n"); - s.append("100-200ms:" + df.format(getConfigCountDetail[2].getAndSet(0)*100/ getConfigCount.get())).append("%\r\n"); - s.append("200-500ms:" + df.format(getConfigCountDetail[3].getAndSet(0)*100/ getConfigCount.get())).append("%\r\n"); - s.append("500-1000ms:" + df.format(getConfigCountDetail[4].getAndSet(0)*100/ getConfigCount.get())).append("%\r\n"); - s.append("1000-2000ms:" + df.format(getConfigCountDetail[5].getAndSet(0)*100/ getConfigCount.get())).append("%\r\n"); - s.append("2000-3000ms:" + df.format(getConfigCountDetail[6].getAndSet(0)*100/ getConfigCount.get())).append("%\r\n"); - s.append("3000以上ms:" + df.format(getConfigCountDetail[7].getAndSet(0)*100/ getConfigCount.getAndSet(0))).append("%\r\n"); - return s.toString(); - } - - public static void main(String[] args) { - ResponseMonitor.addConfigTime(10); - ResponseMonitor.addConfigTime(10); - ResponseMonitor.addConfigTime(10); - ResponseMonitor.addConfigTime(10); - ResponseMonitor.addConfigTime(100); - ResponseMonitor.addConfigTime(150); - ResponseMonitor.addConfigTime(250); - ResponseMonitor.addConfigTime(350); - ResponseMonitor.addConfigTime(750); - ResponseMonitor.addConfigTime(15000); - System.out.println(ResponseMonitor.getStringForPrint()); - System.out.println(ResponseMonitor.getStringForPrint()); - - } + static { + refresh(); + } + + public static void refresh() { + for (int i = 0; i < getConfigCountDetail.length; i++) { + getConfigCountDetail[i] = new AtomicLong(); + } + } + + public static void addConfigTime(long time) { + getConfigCount.incrementAndGet(); + if (time < MS_50) { + getConfigCountDetail[0].incrementAndGet(); + } else if (time < MS_100) { + getConfigCountDetail[1].incrementAndGet(); + } else if (time < MS_200) { + getConfigCountDetail[2].incrementAndGet(); + } else if (time < MS_500) { + getConfigCountDetail[3].incrementAndGet(); + } else if (time < MS_1000) { + getConfigCountDetail[4].incrementAndGet(); + } else if (time < MS_2000) { + getConfigCountDetail[5].incrementAndGet(); + } else if (time < MS_3000) { + getConfigCountDetail[6].incrementAndGet(); + } else { + getConfigCountDetail[7].incrementAndGet(); + } + } + + public static String getStringForPrint() { + DecimalFormat df = new DecimalFormat("##.0"); + StringBuilder s = new StringBuilder("getConfig monitor:\r\n"); + s.append("0-50ms:" + df.format(getConfigCountDetail[0].getAndSet(0) * 100 / getConfigCount.get())).append( + "%\r\n"); + s.append("100-200ms:" + df.format(getConfigCountDetail[2].getAndSet(0) * 100 / getConfigCount.get())).append( + "%\r\n"); + s.append("200-500ms:" + df.format(getConfigCountDetail[3].getAndSet(0) * 100 / getConfigCount.get())).append( + "%\r\n"); + s.append("500-1000ms:" + df.format(getConfigCountDetail[4].getAndSet(0) * 100 / getConfigCount.get())).append( + "%\r\n"); + s.append("1000-2000ms:" + df.format(getConfigCountDetail[5].getAndSet(0) * 100 / getConfigCount.get())).append( + "%\r\n"); + s.append("2000-3000ms:" + df.format(getConfigCountDetail[6].getAndSet(0) * 100 / getConfigCount.get())).append( + "%\r\n"); + s.append("3000以上ms:" + df.format(getConfigCountDetail[7].getAndSet(0) * 100 / getConfigCount.getAndSet(0))) + .append("%\r\n"); + return s.toString(); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/AggrWhitelist.java b/config/src/main/java/com/alibaba/nacos/config/server/service/AggrWhitelist.java index 7b7f65ead..da725496c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/AggrWhitelist.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/AggrWhitelist.java @@ -15,8 +15,10 @@ */ package com.alibaba.nacos.config.server.service; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; +import com.alibaba.nacos.config.server.utils.RegexParser; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; import java.io.StringReader; import java.util.ArrayList; @@ -24,15 +26,12 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.springframework.stereotype.Service; - -import com.alibaba.nacos.config.server.utils.RegexParser; - +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; /** * 聚合数据白名单。 + * * @author Nacos */ @Service @@ -63,7 +62,7 @@ public class AggrWhitelist { return; } defaultLog.warn("[aggr-dataIds] {}", content); - + try { List lines = IOUtils.readLines(new StringReader(content)); compile(lines); @@ -87,11 +86,11 @@ public class AggrWhitelist { static public List getWhiteList() { return AGGR_DATAID_WHITELIST.get(); } - + // ======================= static public final String AGGRIDS_METADATA = "com.alibaba.nacos.metadata.aggrIDs"; static final AtomicReference> AGGR_DATAID_WHITELIST = new AtomicReference>( - new ArrayList()); + new ArrayList()); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java index 56025a2e1..7102326c3 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/BasicDataSourceServiceImpl.java @@ -15,23 +15,10 @@ */ package com.alibaba.nacos.config.server.service; -import static com.alibaba.nacos.config.server.service.PersistService.CONFIG_INFO4BETA_ROW_MAPPER; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.PostConstruct; -import javax.sql.DataSource; - +import com.alibaba.nacos.config.server.utils.PropertyUtil; import org.apache.commons.dbcp.BasicDataSource; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.dao.DataAccessException; @@ -41,291 +28,305 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; -import com.alibaba.nacos.config.server.utils.PropertyUtil; +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.config.server.service.PersistService.CONFIG_INFO4BETA_ROW_MAPPER; +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; /** * Base data source - * @author Nacos * + * @author Nacos */ @Service("basicDataSourceService") public class BasicDataSourceServiceImpl implements DataSourceService { - private static final String JDBC_DRIVER_NAME = "com.mysql.jdbc.Driver"; + private static final String JDBC_DRIVER_NAME = "com.mysql.jdbc.Driver"; - /** - * JDBC执行超时时间, 单位秒 - */ - private int queryTimeout = 3; + @Autowired + private PropertyUtil propertyUtil; - private static final int TRANSACTION_QUERY_TIMEOUT = 5; + /** + * JDBC执行超时时间, 单位秒 + */ + private int queryTimeout = 3; - private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error"; + private static final int TRANSACTION_QUERY_TIMEOUT = 5; - private List dataSourceList = new ArrayList(); - private JdbcTemplate jt; - private DataSourceTransactionManager tm; - private TransactionTemplate tjt; + private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error"; - private JdbcTemplate testMasterJT; - private JdbcTemplate testMasterWritableJT; + private List dataSourceList = new ArrayList(); + private JdbcTemplate jt; + private DataSourceTransactionManager tm; + private TransactionTemplate tjt; - volatile private List testJTList; - volatile private List isHealthList; - private volatile int masterIndex; - private static Pattern ipPattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); + private JdbcTemplate testMasterJT; + private JdbcTemplate testMasterWritableJT; - @Autowired - private Environment env; + volatile private List testJTList; + volatile private List isHealthList; + private volatile int masterIndex; + private static Pattern ipPattern = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); - @PostConstruct - public void init() { - queryTimeout = NumberUtils - .toInt(System.getProperty("QUERYTIMEOUT"), 3); - jt = new JdbcTemplate(); - /** - * 设置最大记录数,防止内存膨胀 - */ - jt.setMaxRows(50000); - jt.setQueryTimeout(queryTimeout); + @Autowired + private Environment env; - testMasterJT = new JdbcTemplate(); - testMasterJT.setQueryTimeout(queryTimeout); + @PostConstruct + public void init() { + queryTimeout = NumberUtils.toInt(System.getProperty("QUERYTIMEOUT"), 3); + jt = new JdbcTemplate(); + /** + * 设置最大记录数,防止内存膨胀 + */ + jt.setMaxRows(50000); + jt.setQueryTimeout(queryTimeout); - testMasterWritableJT = new JdbcTemplate(); - /** - * 防止login接口因为主库不可用而rt太长 - */ - testMasterWritableJT.setQueryTimeout(1); - /** - * 数据库健康检测 - */ - testJTList = new ArrayList(); - isHealthList = new ArrayList(); + testMasterJT = new JdbcTemplate(); + testMasterJT.setQueryTimeout(queryTimeout); - tm = new DataSourceTransactionManager(); - tjt = new TransactionTemplate(tm); - /** - * 事务的超时时间需要与普通操作区分开 - */ - tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT); - if (!PropertyUtil.isStandaloneMode()) { - try { - reload(); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(DB_LOAD_ERROR_MSG); - } + testMasterWritableJT = new JdbcTemplate(); + /** + * 防止login接口因为主库不可用而rt太长 + */ + testMasterWritableJT.setQueryTimeout(1); + /** + * 数据库健康检测 + */ + testJTList = new ArrayList(); + isHealthList = new ArrayList(); - TimerTaskService.scheduleWithFixedDelay(new SelectMasterTask(), 10, 10, - TimeUnit.SECONDS); - TimerTaskService.scheduleWithFixedDelay(new CheckDBHealthTask(), 10, 10, - TimeUnit.SECONDS); - } - } + tm = new DataSourceTransactionManager(); + tjt = new TransactionTemplate(tm); + /** + * 事务的超时时间需要与普通操作区分开 + */ + tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT); + if (!STANDALONE_MODE || propertyUtil.isStandaloneUseMysql()) { + try { + reload(); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(DB_LOAD_ERROR_MSG); + } - public synchronized void reload() throws IOException { - List dblist = new ArrayList(); - try { - String val = null; - val = env.getProperty("db.num"); - if (null == val) { - throw new IllegalArgumentException("db.num is null"); - } - int dbNum = Integer.parseInt(val.trim()); + TimerTaskService.scheduleWithFixedDelay(new SelectMasterTask(), 10, 10, + TimeUnit.SECONDS); + TimerTaskService.scheduleWithFixedDelay(new CheckDBHealthTask(), 10, 10, + TimeUnit.SECONDS); + } + } - for (int i = 0; i < dbNum; i++) { - BasicDataSource ds = new BasicDataSource(); - ds.setDriverClassName(JDBC_DRIVER_NAME); + public synchronized void reload() throws IOException { + List dblist = new ArrayList(); + try { + String val = null; + val = env.getProperty("db.num"); + if (null == val) { + throw new IllegalArgumentException("db.num is null"); + } + int dbNum = Integer.parseInt(val.trim()); - val = env.getProperty("db.url." + i); - if (null == val) { - fatalLog.error("db.url." + i + " is null"); - throw new IllegalArgumentException(); - } - ds.setUrl(val.trim()); + for (int i = 0; i < dbNum; i++) { + BasicDataSource ds = new BasicDataSource(); + ds.setDriverClassName(JDBC_DRIVER_NAME); - val = env.getProperty("db.user"); - if (null == val) { - fatalLog.error("db.user is null"); - throw new IllegalArgumentException(); - } - ds.setUsername(val.trim()); + val = env.getProperty("db.url." + i); + if (null == val) { + fatalLog.error("db.url." + i + " is null"); + throw new IllegalArgumentException(); + } + ds.setUrl(val.trim()); - val = env.getProperty("db.password"); - if (null == val) { - fatalLog.error("db.password is null"); - throw new IllegalArgumentException(); - } - ds.setPassword(val.trim()); + val = env.getProperty("db.user"); + if (null == val) { + fatalLog.error("db.user is null"); + throw new IllegalArgumentException(); + } + ds.setUsername(val.trim()); - val = env.getProperty("db.initialSize"); - ds.setInitialSize(Integer.parseInt(defaultIfNull(val, "10"))); + val = env.getProperty("db.password"); + if (null == val) { + fatalLog.error("db.password is null"); + throw new IllegalArgumentException(); + } + ds.setPassword(val.trim()); - val = env.getProperty("db.maxActive"); - ds.setMaxActive(Integer.parseInt(defaultIfNull(val, "20"))); + val = env.getProperty("db.initialSize"); + ds.setInitialSize(Integer.parseInt(defaultIfNull(val, "10"))); - val = env.getProperty("db.maxIdle"); - ds.setMaxIdle(Integer.parseInt(defaultIfNull(val, "50"))); + val = env.getProperty("db.maxActive"); + ds.setMaxActive(Integer.parseInt(defaultIfNull(val, "20"))); - ds.setMaxWait(3000L); - ds.setPoolPreparedStatements(true); + val = env.getProperty("db.maxIdle"); + ds.setMaxIdle(Integer.parseInt(defaultIfNull(val, "50"))); - // 每10分钟检查一遍连接池 - ds.setTimeBetweenEvictionRunsMillis(TimeUnit.MINUTES - .toMillis(10L)); - ds.setTestWhileIdle(true); - ds.setValidationQuery("SELECT 1 FROM dual"); + ds.setMaxWait(3000L); + ds.setPoolPreparedStatements(true); - dblist.add(ds); + // 每10分钟检查一遍连接池 + ds.setTimeBetweenEvictionRunsMillis(TimeUnit.MINUTES + .toMillis(10L)); + ds.setTestWhileIdle(true); + ds.setValidationQuery("SELECT 1 FROM dual"); - JdbcTemplate jdbcTemplate = new JdbcTemplate(); - jdbcTemplate.setQueryTimeout(queryTimeout); - jdbcTemplate.setDataSource(ds); + dblist.add(ds); - testJTList.add(jdbcTemplate); - isHealthList.add(Boolean.TRUE); - } + JdbcTemplate jdbcTemplate = new JdbcTemplate(); + jdbcTemplate.setQueryTimeout(queryTimeout); + jdbcTemplate.setDataSource(ds); - if (dblist == null || dblist.size() == 0) { - throw new RuntimeException("no datasource available"); - } + testJTList.add(jdbcTemplate); + isHealthList.add(Boolean.TRUE); + } - dataSourceList = dblist; - new SelectMasterTask().run(); - new CheckDBHealthTask().run(); - } catch (RuntimeException e) { - fatalLog.error(DB_LOAD_ERROR_MSG, e); - throw new IOException(e); - } finally { - } - } + if (dblist == null || dblist.size() == 0) { + throw new RuntimeException("no datasource available"); + } - public boolean checkMasterWritable() { + dataSourceList = dblist; + new SelectMasterTask().run(); + new CheckDBHealthTask().run(); + } catch (RuntimeException e) { + fatalLog.error(DB_LOAD_ERROR_MSG, e); + throw new IOException(e); + } finally { + } + } - testMasterWritableJT.setDataSource(jt.getDataSource()); - /** - * 防止login接口因为主库不可用而rt太长 - */ - testMasterWritableJT.setQueryTimeout(1); - String sql = " select @@read_only "; + public boolean checkMasterWritable() { - try { - Integer result = testMasterWritableJT.queryForObject(sql, Integer.class); - if (result == null) { - return false; - } else { - return result.intValue() == 0 ? true : false; - } - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - return false; - } + testMasterWritableJT.setDataSource(jt.getDataSource()); + /** + * 防止login接口因为主库不可用而rt太长 + */ + testMasterWritableJT.setQueryTimeout(1); + String sql = " SELECT @@read_only "; - } + try { + Integer result = testMasterWritableJT.queryForObject(sql, Integer.class); + if (result == null) { + return false; + } else { + return result.intValue() == 0 ? true : false; + } + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + return false; + } - public JdbcTemplate getJdbcTemplate() { - return this.jt; - } + } - public TransactionTemplate getTransactionTemplate() { - return this.tjt; - } + public JdbcTemplate getJdbcTemplate() { + return this.jt; + } - public String getCurrentDBUrl() { - DataSource ds = this.jt.getDataSource(); - if (ds == null) { - return StringUtils.EMPTY; - } - BasicDataSource bds = (BasicDataSource) ds; - return bds.getUrl(); - } + public TransactionTemplate getTransactionTemplate() { + return this.tjt; + } - public String getHealth() { - for (int i = 0 ; i < isHealthList.size(); i++) { - if (!isHealthList.get(i)) { - if (i == masterIndex) { - /** - * 主库不健康 - */ - return "DOWN:" + getIpFromUrl(dataSourceList.get(i).getUrl()); - } else { - /** - * 从库不健康 - */ - return "WARN:" + getIpFromUrl(dataSourceList.get(i).getUrl()); - } - } - } + public String getCurrentDBUrl() { + DataSource ds = this.jt.getDataSource(); + if (ds == null) { + return StringUtils.EMPTY; + } + BasicDataSource bds = (BasicDataSource)ds; + return bds.getUrl(); + } - return "UP"; - } + public String getHealth() { + for (int i = 0; i < isHealthList.size(); i++) { + if (!isHealthList.get(i)) { + if (i == masterIndex) { + /** + * 主库不健康 + */ + return "DOWN:" + getIpFromUrl(dataSourceList.get(i).getUrl()); + } else { + /** + * 从库不健康 + */ + return "WARN:" + getIpFromUrl(dataSourceList.get(i).getUrl()); + } + } + } - private String getIpFromUrl(String url) { - - Matcher m = ipPattern.matcher(url); - if (m.find()) { - return m.group(); - } + return "UP"; + } - return ""; - } + private String getIpFromUrl(String url) { - static String defaultIfNull(String value, String defaultValue) { - return null == value ? defaultValue : value; - } + Matcher m = ipPattern.matcher(url); + if (m.find()) { + return m.group(); + } - class SelectMasterTask implements Runnable { - public void run() { - defaultLog.info("check master db."); - boolean isFound = false; + return ""; + } - int index = -1; - for (BasicDataSource ds : dataSourceList) { - index++; - testMasterJT.setDataSource(ds); - testMasterJT.setQueryTimeout(queryTimeout); - try { - testMasterJT - .update("delete from config_info where data_id='com.alibaba.nacos.testMasterDB'"); - if (jt.getDataSource() != ds) { - fatalLog.warn("[master-db] {}", ds.getUrl()); - } - jt.setDataSource(ds); - tm.setDataSource(ds); - isFound = true; - masterIndex = index; - break; - } catch (DataAccessException e) { // read only - e.printStackTrace(); // TODO remove - } - } + static String defaultIfNull(String value, String defaultValue) { + return null == value ? defaultValue : value; + } - if (!isFound) { - fatalLog.error("[master-db] master db not found."); - } - } - } + class SelectMasterTask implements Runnable { + public void run() { + defaultLog.info("check master db."); + boolean isFound = false; - @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") - class CheckDBHealthTask implements Runnable { - public void run() { - defaultLog.info("check db health."); - String sql = "SELECT * FROM config_info_beta WHERE id = 1"; + int index = -1; + for (BasicDataSource ds : dataSourceList) { + index++; + testMasterJT.setDataSource(ds); + testMasterJT.setQueryTimeout(queryTimeout); + try { + testMasterJT + .update("DELETE FROM config_info WHERE data_id='com.alibaba.nacos.testMasterDB'"); + if (jt.getDataSource() != ds) { + fatalLog.warn("[master-db] {}", ds.getUrl()); + } + jt.setDataSource(ds); + tm.setDataSource(ds); + isFound = true; + masterIndex = index; + break; + } catch (DataAccessException e) { // read only + e.printStackTrace(); // TODO remove + } + } - for (int i = 0; i < testJTList.size(); i++) { - JdbcTemplate jdbcTemplate = testJTList.get(i); - try { - jdbcTemplate.query(sql, CONFIG_INFO4BETA_ROW_MAPPER); - isHealthList.set(i, Boolean.TRUE); - } catch (DataAccessException e) { - if (i == masterIndex) { - fatalLog.error("[db-error] master db {} down.", getIpFromUrl(dataSourceList.get(i).getUrl())); - } else { - fatalLog.error("[db-error] slave db {} down.", getIpFromUrl(dataSourceList.get(i).getUrl())); - } - isHealthList.set(i, Boolean.FALSE); - } - } - } - } + if (!isFound) { + fatalLog.error("[master-db] master db not found."); + } + } + } + + @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") + class CheckDBHealthTask implements Runnable { + public void run() { + defaultLog.info("check db health."); + String sql = "SELECT * FROM config_info_beta WHERE id = 1"; + + for (int i = 0; i < testJTList.size(); i++) { + JdbcTemplate jdbcTemplate = testJTList.get(i); + try { + jdbcTemplate.query(sql, CONFIG_INFO4BETA_ROW_MAPPER); + isHealthList.set(i, Boolean.TRUE); + } catch (DataAccessException e) { + if (i == masterIndex) { + fatalLog.error("[db-error] master db {} down.", getIpFromUrl(dataSourceList.get(i).getUrl())); + } else { + fatalLog.error("[db-error] slave db {} down.", getIpFromUrl(dataSourceList.get(i).getUrl())); + } + isHealthList.set(i, Boolean.FALSE); + } + } + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ClientIpWhiteList.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ClientIpWhiteList.java index e3f011205..f35e65b32 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ClientIpWhiteList.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ClientIpWhiteList.java @@ -15,74 +15,74 @@ */ package com.alibaba.nacos.config.server.service; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; +import com.alibaba.nacos.config.server.model.ACLInfo; +import com.alibaba.nacos.config.server.utils.JSONUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import org.apache.commons.lang.StringUtils; -import org.springframework.stereotype.Service; +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; -import com.alibaba.nacos.config.server.model.ACLInfo; -import com.alibaba.nacos.config.server.utils.JSONUtils; /** * Client ip whitelist - * @author Nacos * + * @author Nacos */ @Service public class ClientIpWhiteList { - /** - * 判断指定的ip在白名单中 - */ - static public boolean isLegalClient(String clientIp) { - if (StringUtils.isBlank(clientIp)) { - throw new IllegalArgumentException(); - } - clientIp = clientIp.trim(); - if (CLIENT_IP_WHITELIST.get().contains(clientIp)) { - return true; - } - return false; - } - - /** - * whether start client ip whitelist - * - * @return true: enable ; false disable - */ - static public boolean isEnableWhitelist() { - return isOpen; - } + /** + * 判断指定的ip在白名单中 + */ + static public boolean isLegalClient(String clientIp) { + if (StringUtils.isBlank(clientIp)) { + throw new IllegalArgumentException(); + } + clientIp = clientIp.trim(); + if (CLIENT_IP_WHITELIST.get().contains(clientIp)) { + return true; + } + return false; + } - /** - * 传入内容,重新加载客户端ip白名单 - */ - static public void load(String content) { - if (StringUtils.isBlank(content)) { - defaultLog.warn("clientIpWhiteList is blank.close whitelist."); - isOpen = false; - CLIENT_IP_WHITELIST.get().clear(); - return; - } - defaultLog.warn("[clientIpWhiteList] {}", content); - try { - ACLInfo acl=(ACLInfo)JSONUtils.deserializeObject(content, ACLInfo.class); - isOpen = acl.getIsOpen(); - CLIENT_IP_WHITELIST.set(acl.getIps()); - } catch (Exception ioe) { - defaultLog.error( - "failed to load clientIpWhiteList, " + ioe.toString(), ioe); - } - } + /** + * whether start client ip whitelist + * + * @return true: enable ; false disable + */ + static public boolean isEnableWhitelist() { + return isOpen; + } - // ======================= + /** + * 传入内容,重新加载客户端ip白名单 + */ + static public void load(String content) { + if (StringUtils.isBlank(content)) { + defaultLog.warn("clientIpWhiteList is blank.close whitelist."); + isOpen = false; + CLIENT_IP_WHITELIST.get().clear(); + return; + } + defaultLog.warn("[clientIpWhiteList] {}", content); + try { + ACLInfo acl = (ACLInfo)JSONUtils.deserializeObject(content, ACLInfo.class); + isOpen = acl.getIsOpen(); + CLIENT_IP_WHITELIST.set(acl.getIps()); + } catch (Exception ioe) { + defaultLog.error( + "failed to load clientIpWhiteList, " + ioe.toString(), ioe); + } + } - static public final String CLIENT_IP_WHITELIST_METADATA = "com.alibaba.nacos.metadata.clientIpWhitelist"; + // ======================= - static final AtomicReference> CLIENT_IP_WHITELIST = new AtomicReference>( - new ArrayList()); - static Boolean isOpen = false; + static public final String CLIENT_IP_WHITELIST_METADATA = "com.alibaba.nacos.metadata.clientIpWhitelist"; + + static final AtomicReference> CLIENT_IP_WHITELIST = new AtomicReference>( + new ArrayList()); + static Boolean isOpen = false; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java index 448e6de47..92384a8c6 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java @@ -15,16 +15,16 @@ */ package com.alibaba.nacos.config.server.service; +import com.alibaba.nacos.config.server.model.SubscriberStatus; + import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import com.alibaba.nacos.config.server.model.SubscriberStatus; - - /** * 跟踪客户端md5的服务。 一段时间没有比较md5后,就删除IP对应的记录。 + * * @author Nacos */ public class ClientTrackService { @@ -37,28 +37,28 @@ public class ClientTrackService { record.groupKey2md5Map.putAll(clientMd5Map); } - static public void trackClientMd5(String ip, Map clientMd5Map, Map clientlastPollingTSMap) { + static public void trackClientMd5(String ip, Map clientMd5Map, + Map clientlastPollingTSMap) { ClientRecord record = getClientRecord(ip); record.lastTime = System.currentTimeMillis(); record.groupKey2md5Map.putAll(clientMd5Map); record.groupKey2pollingTsMap.putAll(clientlastPollingTSMap); } - + static public void trackClientMd5(String ip, String groupKey, String clientMd5) { ClientRecord record = getClientRecord(ip); record.lastTime = System.currentTimeMillis(); record.groupKey2md5Map.put(groupKey, clientMd5); record.groupKey2pollingTsMap.put(groupKey, record.lastTime); } - - + /** * 返回订阅者客户端个数 */ static public int subscribeClientCount() { return clientRecords.size(); } - + /** * 返回所有订阅者个数 */ @@ -73,12 +73,12 @@ public class ClientTrackService { /** * groupkey -> SubscriberStatus */ - static public Map listSubStatus(String ip){ + static public Map listSubStatus(String ip) { Map status = new HashMap(100); ClientRecord record = getClientRecord(ip); - if(record == null) { - return status; + if (record == null) { + return status; } for (Map.Entry entry : record.groupKey2md5Map.entrySet()) { @@ -111,7 +111,7 @@ public class ClientTrackService { } return subs; } - + /** * 指定订阅者IP,查找数据是否最新。 groupKey -> isUptodate */ @@ -125,13 +125,13 @@ public class ClientTrackService { } return result; } - + /** * 指定groupKey,查找所有订阅者以及数据是否最新。 IP -> isUptodate */ static public Map listSubscriberByGroup(String groupKey) { Map subs = new HashMap(100); - + for (ClientRecord clientRec : clientRecords.values()) { String clientMd5 = clientRec.groupKey2md5Map.get(groupKey); if (null != clientMd5) { @@ -141,7 +141,7 @@ public class ClientTrackService { } return subs; } - + /** * 找到指定clientIp对应的记录。 */ @@ -154,12 +154,12 @@ public class ClientTrackService { return clientRecords.get(clientIp); } - static public void refreshClientRecord(){ + static public void refreshClientRecord() { clientRecords = new ConcurrentHashMap(50); } /** - * 所有客户端记录。遍历 >> 新增/删除 + * 所有客户端记录。遍历 >> 新增/删除 */ static volatile ConcurrentMap clientRecords = new ConcurrentHashMap(); } @@ -173,7 +173,6 @@ class ClientRecord { final ConcurrentMap groupKey2md5Map; final ConcurrentMap groupKey2pollingTsMap; - ClientRecord(String clientIp) { ip = clientIp; groupKey2md5Map = new ConcurrentHashMap(20, 0.75f, 1); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigDataChangeEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigDataChangeEvent.java index ed9030e37..30d67ec43 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigDataChangeEvent.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigDataChangeEvent.java @@ -15,55 +15,54 @@ */ package com.alibaba.nacos.config.server.service; -import org.apache.commons.lang.StringUtils; - import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event; - +import org.apache.commons.lang3.StringUtils; /** * 指数据发布事件。 + * * @author Nacos */ public class ConfigDataChangeEvent implements Event { - final public boolean isBeta; + final public boolean isBeta; final public String dataId; final public String group; final public String tenant; final public String tag; final public long lastModifiedTs; - - public ConfigDataChangeEvent(String dataId, String group, long gmtModified) { - this(false, dataId, group, gmtModified); - } - - public ConfigDataChangeEvent(boolean isBeta, String dataId, String group, String tenant, long gmtModified) { - if (null == dataId || null == group) { - throw new IllegalArgumentException(); - } - this.isBeta = isBeta; - this.dataId = dataId; - this.group = group; - this.tenant = tenant; - this.tag = null; - this.lastModifiedTs = gmtModified; - } - public ConfigDataChangeEvent(boolean isBeta, String dataId, String group, long gmtModified) { - this(isBeta, dataId, group, StringUtils.EMPTY, gmtModified); - } - - public ConfigDataChangeEvent(boolean isBeta, String dataId, String group, String tenant, String tag, - long gmtModified) { - if (null == dataId || null == group) { - throw new IllegalArgumentException(); - } - this.isBeta = isBeta; - this.dataId = dataId; - this.group = group; - this.tenant = tenant; - this.tag = tag; - this.lastModifiedTs = gmtModified; - } + public ConfigDataChangeEvent(String dataId, String group, long gmtModified) { + this(false, dataId, group, gmtModified); + } + + public ConfigDataChangeEvent(boolean isBeta, String dataId, String group, String tenant, long gmtModified) { + if (null == dataId || null == group) { + throw new IllegalArgumentException(); + } + this.isBeta = isBeta; + this.dataId = dataId; + this.group = group; + this.tenant = tenant; + this.tag = null; + this.lastModifiedTs = gmtModified; + } + + public ConfigDataChangeEvent(boolean isBeta, String dataId, String group, long gmtModified) { + this(isBeta, dataId, group, StringUtils.EMPTY, gmtModified); + } + + public ConfigDataChangeEvent(boolean isBeta, String dataId, String group, String tenant, String tag, + long gmtModified) { + if (null == dataId || null == group) { + throw new IllegalArgumentException(); + } + this.isBeta = isBeta; + this.dataId = dataId; + this.group = group; + this.tenant = tenant; + this.tag = tag; + this.lastModifiedTs = gmtModified; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigService.java index 0460660b2..22c9ccf9d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigService.java @@ -15,44 +15,37 @@ */ package com.alibaba.nacos.config.server.service; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; -import static com.alibaba.nacos.config.server.utils.LogUtil.dumpLog; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; - -import com.alibaba.nacos.config.server.model.ConfigInfoBase; -import com.alibaba.nacos.config.server.utils.PropertyUtil; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.CacheItem; +import com.alibaba.nacos.config.server.model.ConfigInfoBase; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.MD5; +import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.event.EventDispatcher; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.config.server.utils.LogUtil.*; + /** * config service - * @author Nacos * + * @author Nacos */ public class ConfigService { - @Autowired - private static PersistService persistService; - + @Autowired + private static PersistService persistService; + static public int groupCount() { return CACHE.size(); } @@ -60,250 +53,254 @@ public class ConfigService { static public boolean hasGroupKey(String groupKey) { return CACHE.containsKey(groupKey); } - + /** * 保存配置文件,并缓存md5. */ - static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs) { - String groupKey = GroupKey2.getKey(dataId, group, tenant); + static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs) { + String groupKey = GroupKey2.getKey(dataId, group, tenant); makeSure(groupKey); final int lockResult = tryWriteLock(groupKey); assert (lockResult != 0); - + if (lockResult < 0) { dumpLog.warn("[dump-error] write lock failed. {}", groupKey); return false; } try { - final String md5 = MD5.getInstance().getMD5String(content); - if (md5.equals(ConfigService.getContentMd5(groupKey))) { - dumpLog.warn( - "[dump-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " - + "lastModifiedNew={}", - groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); - } else if (!PropertyUtil.isStandaloneMode()) { - DiskUtil.saveToDisk(dataId, group, tenant, content); - } + final String md5 = MD5.getInstance().getMD5String(content); + if (md5.equals(ConfigService.getContentMd5(groupKey))) { + dumpLog.warn( + "[dump-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " + + "lastModifiedNew={}", + groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); + } else if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + DiskUtil.saveToDisk(dataId, group, tenant, content); + } updateMd5(groupKey, md5, lastModifiedTs); return true; - } catch (IOException ioe) { - dumpLog.error("[dump-exception] save disk error. " + groupKey + ", " + ioe.toString(), ioe); - if (ioe.getMessage() != null) { - String errMsg = ioe.getMessage(); - if (NO_SPACE_CN.equals(errMsg) || NO_SPACE_EN.equals(errMsg) || errMsg.contains(DISK_QUATA_CN) - || errMsg.contains(DISK_QUATA_EN)) { - // 磁盘写满保护代码 - fatalLog.error("磁盘满自杀退出", ioe); - System.exit(0); - } - } - return false; - } finally { + } catch (IOException ioe) { + dumpLog.error("[dump-exception] save disk error. " + groupKey + ", " + ioe.toString(), ioe); + if (ioe.getMessage() != null) { + String errMsg = ioe.getMessage(); + if (NO_SPACE_CN.equals(errMsg) || NO_SPACE_EN.equals(errMsg) || errMsg.contains(DISK_QUATA_CN) + || errMsg.contains(DISK_QUATA_EN)) { + // 磁盘写满保护代码 + fatalLog.error("磁盘满自杀退出", ioe); + System.exit(0); + } + } + return false; + } finally { releaseWriteLock(groupKey); } } - + /** * 保存配置文件,并缓存md5. */ - static public boolean dumpBeta(String dataId, String group, String tenant, String content, long lastModifiedTs, String betaIps) { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - - makeSure(groupKey); - final int lockResult = tryWriteLock(groupKey); - assert (lockResult != 0); - - if (lockResult < 0) { - dumpLog.warn("[dump-beta-error] write lock failed. {}", groupKey); - return false; - } - - try { - final String md5 = MD5.getInstance().getMD5String(content); - if(md5.equals(ConfigService.getContentBetaMd5(groupKey))) { - dumpLog.warn( - "[dump-beta-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " - + "lastModifiedNew={}", - groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); - } else if (!PropertyUtil.isStandaloneMode()) { - DiskUtil.saveBetaToDisk(dataId, group, tenant, content); - } - String[] betaIpsArr = betaIps.split(","); - - updateBetaMd5(groupKey, md5, Arrays.asList(betaIpsArr), lastModifiedTs); - return true; - } catch (IOException ioe) { - dumpLog.error("[dump-beta-exception] save disk error. " + groupKey + ", " + ioe.toString(), - ioe); - return false; - } finally { - releaseWriteLock(groupKey); - } + static public boolean dumpBeta(String dataId, String group, String tenant, String content, long lastModifiedTs, + String betaIps) { + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + + makeSure(groupKey); + final int lockResult = tryWriteLock(groupKey); + assert (lockResult != 0); + + if (lockResult < 0) { + dumpLog.warn("[dump-beta-error] write lock failed. {}", groupKey); + return false; + } + + try { + final String md5 = MD5.getInstance().getMD5String(content); + if (md5.equals(ConfigService.getContentBetaMd5(groupKey))) { + dumpLog.warn( + "[dump-beta-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " + + "lastModifiedNew={}", + groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); + } else if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + DiskUtil.saveBetaToDisk(dataId, group, tenant, content); + } + String[] betaIpsArr = betaIps.split(","); + + updateBetaMd5(groupKey, md5, Arrays.asList(betaIpsArr), lastModifiedTs); + return true; + } catch (IOException ioe) { + dumpLog.error("[dump-beta-exception] save disk error. " + groupKey + ", " + ioe.toString(), + ioe); + return false; + } finally { + releaseWriteLock(groupKey); + } } - + /** * 保存配置文件,并缓存md5. */ - static public boolean dumpTag(String dataId, String group, String tenant, String tag, String content, long lastModifiedTs) { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - - makeSure(groupKey); - final int lockResult = tryWriteLock(groupKey); - assert (lockResult != 0); - - if (lockResult < 0) { - dumpLog.warn("[dump-tag-error] write lock failed. {}", groupKey); - return false; - } - - try { - final String md5 = MD5.getInstance().getMD5String(content); - if(md5.equals(ConfigService.getContentTagMd5(groupKey,tag))) { - dumpLog.warn( - "[dump-tag-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " - + "lastModifiedNew={}", - groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); - } else if (!PropertyUtil.isStandaloneMode()) { - DiskUtil.saveTagToDisk(dataId, group, tenant, tag, content); - } - - updateTagMd5(groupKey, tag, md5, lastModifiedTs); - return true; - } catch (IOException ioe) { - dumpLog.error("[dump-tag-exception] save disk error. " + groupKey + ", " + ioe.toString(), - ioe); - return false; - } finally { - releaseWriteLock(groupKey); - } + static public boolean dumpTag(String dataId, String group, String tenant, String tag, String content, + long lastModifiedTs) { + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + + makeSure(groupKey); + final int lockResult = tryWriteLock(groupKey); + assert (lockResult != 0); + + if (lockResult < 0) { + dumpLog.warn("[dump-tag-error] write lock failed. {}", groupKey); + return false; + } + + try { + final String md5 = MD5.getInstance().getMD5String(content); + if (md5.equals(ConfigService.getContentTagMd5(groupKey, tag))) { + dumpLog.warn( + "[dump-tag-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " + + "lastModifiedNew={}", + groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); + } else if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + DiskUtil.saveTagToDisk(dataId, group, tenant, tag, content); + } + + updateTagMd5(groupKey, tag, md5, lastModifiedTs); + return true; + } catch (IOException ioe) { + dumpLog.error("[dump-tag-exception] save disk error. " + groupKey + ", " + ioe.toString(), + ioe); + return false; + } finally { + releaseWriteLock(groupKey); + } } - + /** * 保存配置文件,并缓存md5. */ static public boolean dumpChange(String dataId, String group, String tenant, String content, long lastModifiedTs) { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - - makeSure(groupKey); - final int lockResult = tryWriteLock(groupKey); - assert (lockResult != 0); - - if (lockResult < 0) { - dumpLog.warn("[dump-error] write lock failed. {}", groupKey); - return false; - } - - try { - final String md5 = MD5.getInstance().getMD5String(content); - if (!PropertyUtil.isStandaloneMode()) { - String loacalMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant); - if(md5.equals(loacalMd5)) { - dumpLog.warn( - "[dump-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " - + "lastModifiedNew={}", - groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); - } else { - DiskUtil.saveToDisk(dataId, group, tenant, content); - } - } - updateMd5(groupKey, md5, lastModifiedTs); - return true; - } catch (IOException ioe) { - dumpLog.error("[dump-exception] save disk error. " + groupKey + ", " + ioe.toString(), - ioe); - return false; - } finally { - releaseWriteLock(groupKey); - } + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + + makeSure(groupKey); + final int lockResult = tryWriteLock(groupKey); + assert (lockResult != 0); + + if (lockResult < 0) { + dumpLog.warn("[dump-error] write lock failed. {}", groupKey); + return false; + } + + try { + final String md5 = MD5.getInstance().getMD5String(content); + if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + String loacalMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant); + if (md5.equals(loacalMd5)) { + dumpLog.warn( + "[dump-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, " + + "lastModifiedNew={}", + groupKey, md5, ConfigService.getLastModifiedTs(groupKey), lastModifiedTs); + } else { + DiskUtil.saveToDisk(dataId, group, tenant, content); + } + } + updateMd5(groupKey, md5, lastModifiedTs); + return true; + } catch (IOException ioe) { + dumpLog.error("[dump-exception] save disk error. " + groupKey + ", " + ioe.toString(), + ioe); + return false; + } finally { + releaseWriteLock(groupKey); + } } - - static public void reloadConfig() - { - String aggreds = null; - try { - if (PropertyUtil.isStandaloneMode()) { - ConfigInfoBase config = persistService.findConfigInfoBase(AggrWhitelist.AGGRIDS_METADATA, "DEFAULT_GROUP"); - if (config != null) { - aggreds = config.getContent(); - } - } else { - aggreds = DiskUtil.getConfig(AggrWhitelist.AGGRIDS_METADATA, - "DEFAULT_GROUP", StringUtils.EMPTY); - } - if (aggreds != null) { - AggrWhitelist.load(aggreds); - } - } catch (IOException e) { - dumpLog.error("reload fail:" + AggrWhitelist.AGGRIDS_METADATA, e); - } - String clientIpWhitelist = null; - try { - if (PropertyUtil.isStandaloneMode()) { - ConfigInfoBase config = persistService.findConfigInfoBase(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA, "DEFAULT_GROUP"); - if (config != null) { - clientIpWhitelist = config.getContent(); - } - } else { - clientIpWhitelist = DiskUtil.getConfig(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA, "DEFAULT_GROUP", - StringUtils.EMPTY); - } - if (clientIpWhitelist != null) { - ClientIpWhiteList.load(clientIpWhitelist); - } - } catch (IOException e) { - dumpLog.error("reload fail:" - + ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA, e); - } + static public void reloadConfig() { + String aggreds = null; + try { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + ConfigInfoBase config = persistService.findConfigInfoBase(AggrWhitelist.AGGRIDS_METADATA, + "DEFAULT_GROUP"); + if (config != null) { + aggreds = config.getContent(); + } + } else { + aggreds = DiskUtil.getConfig(AggrWhitelist.AGGRIDS_METADATA, + "DEFAULT_GROUP", StringUtils.EMPTY); + } + if (aggreds != null) { + AggrWhitelist.load(aggreds); + } + } catch (IOException e) { + dumpLog.error("reload fail:" + AggrWhitelist.AGGRIDS_METADATA, e); + } - String switchContent= null; - try { - if (PropertyUtil.isStandaloneMode()) { - ConfigInfoBase config = persistService.findConfigInfoBase(SwitchService.SWITCH_META_DATAID, "DEFAULT_GROUP"); - if (config != null) { - switchContent = config.getContent(); - } - } else { - switchContent = DiskUtil.getConfig( - SwitchService.SWITCH_META_DATAID, "DEFAULT_GROUP", StringUtils.EMPTY); - } - if (switchContent != null) { - SwitchService.load(switchContent); - } - } catch (IOException e) { - dumpLog.error("reload fail:" + SwitchService.SWITCH_META_DATAID, e); - } + String clientIpWhitelist = null; + try { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + ConfigInfoBase config = persistService.findConfigInfoBase( + ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA, "DEFAULT_GROUP"); + if (config != null) { + clientIpWhitelist = config.getContent(); + } + } else { + clientIpWhitelist = DiskUtil.getConfig(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA, "DEFAULT_GROUP", + StringUtils.EMPTY); + } + if (clientIpWhitelist != null) { + ClientIpWhiteList.load(clientIpWhitelist); + } + } catch (IOException e) { + dumpLog.error("reload fail:" + + ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA, e); + } + + String switchContent = null; + try { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + ConfigInfoBase config = persistService.findConfigInfoBase(SwitchService.SWITCH_META_DATAID, + "DEFAULT_GROUP"); + if (config != null) { + switchContent = config.getContent(); + } + } else { + switchContent = DiskUtil.getConfig( + SwitchService.SWITCH_META_DATAID, "DEFAULT_GROUP", StringUtils.EMPTY); + } + if (switchContent != null) { + SwitchService.load(switchContent); + } + } catch (IOException e) { + dumpLog.error("reload fail:" + SwitchService.SWITCH_META_DATAID, e); + } } - - static public List checkMd5() { - List diffList = new ArrayList(); - long startTime = System.currentTimeMillis(); - for (Entry entry : CACHE.entrySet()) { - String groupKey = entry.getKey(); - String[] dg = GroupKey.parseKey(groupKey); - String dataId = dg[0]; - String group = dg[1]; - String tenant = dg[2]; - try { - String loacalMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant); - if (!entry.getValue().md5.equals(loacalMd5)) { - defaultLog.warn("[md5-different] dataId:{},group:{}", - dataId, group); - diffList.add(groupKey); - } - } catch (IOException e) { - defaultLog.error("getLocalConfigMd5 fail,dataId:{},group:{}", - dataId, group); - } - } - long endTime = System.currentTimeMillis(); - defaultLog.warn("checkMd5 cost:{}; diffCount:{}", endTime - startTime, - diffList.size()); - return diffList; - } - + + static public List checkMd5() { + List diffList = new ArrayList(); + long startTime = System.currentTimeMillis(); + for (Entry entry : CACHE.entrySet()) { + String groupKey = entry.getKey(); + String[] dg = GroupKey.parseKey(groupKey); + String dataId = dg[0]; + String group = dg[1]; + String tenant = dg[2]; + try { + String loacalMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant); + if (!entry.getValue().md5.equals(loacalMd5)) { + defaultLog.warn("[md5-different] dataId:{},group:{}", + dataId, group); + diffList.add(groupKey); + } + } catch (IOException e) { + defaultLog.error("getLocalConfigMd5 fail,dataId:{},group:{}", + dataId, group); + } + } + long endTime = System.currentTimeMillis(); + defaultLog.warn("checkMd5 cost:{}; diffCount:{}", endTime - startTime, + diffList.size()); + return diffList; + } + /** * 删除配置文件,删除缓存。 */ @@ -317,18 +314,18 @@ public class ConfigService { dumpLog.info("[remove-ok] {} not exist.", groupKey); return true; } - /** - * 加锁失败 - */ - if (lockResult < 0) { + /** + * 加锁失败 + */ + if (lockResult < 0) { dumpLog.warn("[remove-error] write lock failed. {}", groupKey); return false; } try { - if (!PropertyUtil.isStandaloneMode()) { - DiskUtil.removeConfigInfo(dataId, group, tenant); - } + if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + DiskUtil.removeConfigInfo(dataId, group, tenant); + } CACHE.remove(groupKey); EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey)); @@ -337,120 +334,120 @@ public class ConfigService { releaseWriteLock(groupKey); } } + /** * 删除配置文件,删除缓存。 */ static public boolean removeBeta(String dataId, String group, String tenant) { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - final int lockResult = tryWriteLock(groupKey); - /** - * 数据不存在 - */ - if (0 == lockResult) { - dumpLog.info("[remove-ok] {} not exist.", groupKey); - return true; - } - /** - * 加锁失败 - */ - if (lockResult < 0) { - dumpLog.warn("[remove-error] write lock failed. {}", groupKey); - return false; - } - - try { - if (!PropertyUtil.isStandaloneMode()) { - DiskUtil.removeConfigInfo4Beta(dataId, group, tenant); - } - EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, true, CACHE.get(groupKey).getIps4Beta())); - CACHE.get(groupKey).setBeta(false); - CACHE.get(groupKey).setIps4Beta(null); - CACHE.get(groupKey).setMd54Beta(Constants.NULL); - return true; - } finally { - releaseWriteLock(groupKey); - } + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + final int lockResult = tryWriteLock(groupKey); + /** + * 数据不存在 + */ + if (0 == lockResult) { + dumpLog.info("[remove-ok] {} not exist.", groupKey); + return true; + } + /** + * 加锁失败 + */ + if (lockResult < 0) { + dumpLog.warn("[remove-error] write lock failed. {}", groupKey); + return false; + } + + try { + if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + DiskUtil.removeConfigInfo4Beta(dataId, group, tenant); + } + EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, true, CACHE.get(groupKey).getIps4Beta())); + CACHE.get(groupKey).setBeta(false); + CACHE.get(groupKey).setIps4Beta(null); + CACHE.get(groupKey).setMd54Beta(Constants.NULL); + return true; + } finally { + releaseWriteLock(groupKey); + } } - + /** * 删除配置文件,删除缓存。 */ static public boolean removeTag(String dataId, String group, String tenant, String tag) { - final String groupKey = GroupKey2.getKey(dataId, group, tenant); - final int lockResult = tryWriteLock(groupKey); - /** - * 数据不存在 - */ - if (0 == lockResult) { - dumpLog.info("[remove-ok] {} not exist.", groupKey); - return true; - } - /** - * 加锁失败 - */ - if (lockResult < 0) { - dumpLog.warn("[remove-error] write lock failed. {}", groupKey); - return false; - } - - try { - if (!PropertyUtil.isStandaloneMode()) { - DiskUtil.removeConfigInfo4Tag(dataId, group, tenant, tag); - } + final String groupKey = GroupKey2.getKey(dataId, group, tenant); + final int lockResult = tryWriteLock(groupKey); + /** + * 数据不存在 + */ + if (0 == lockResult) { + dumpLog.info("[remove-ok] {} not exist.", groupKey); + return true; + } + /** + * 加锁失败 + */ + if (lockResult < 0) { + dumpLog.warn("[remove-error] write lock failed. {}", groupKey); + return false; + } - CacheItem ci = CACHE.get(groupKey); - ci.tagMd5.remove(tag); - ci.tagLastModifiedTs.remove(tag); - EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, false, null, tag)); - return true; - } finally { - releaseWriteLock(groupKey); - } + try { + if (!STANDALONE_MODE || PropertyUtil.isStandaloneUseMysql()) { + DiskUtil.removeConfigInfo4Tag(dataId, group, tenant, tag); + } + + CacheItem ci = CACHE.get(groupKey); + ci.tagMd5.remove(tag); + ci.tagLastModifiedTs.remove(tag); + EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, false, null, tag)); + return true; + } finally { + releaseWriteLock(groupKey); + } } - + public static void updateMd5(String groupKey, String md5, long lastModifiedTs) { CacheItem cache = makeSure(groupKey); - if (cache.md5 ==null || !cache.md5.equals(md5)) { + if (cache.md5 == null || !cache.md5.equals(md5)) { cache.md5 = md5; cache.lastModifiedTs = lastModifiedTs; EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey)); } } - - public static void updateBetaMd5(String groupKey, String md5, List ips4Beta, long lastModifiedTs) { - CacheItem cache = makeSure(groupKey); - if (cache.md54Beta ==null || !cache.md54Beta.equals(md5)) { - cache.isBeta = true; - cache.md54Beta = md5; - cache.lastModifiedTs4Beta = lastModifiedTs; - cache.ips4Beta = ips4Beta; - EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, true, ips4Beta)); - } - } - - public static void updateTagMd5(String groupKey, String tag, String md5, long lastModifiedTs) { - CacheItem cache = makeSure(groupKey); - if (cache.tagMd5 == null) { - Map tagMd5Tmp = new HashMap(1); - tagMd5Tmp.put(tag, md5); - cache.tagMd5 = tagMd5Tmp; - if (cache.tagLastModifiedTs == null) { - Map tagLastModifiedTsTmp = new HashMap(1); - tagLastModifiedTsTmp.put(tag, lastModifiedTs); - cache.tagLastModifiedTs = tagLastModifiedTsTmp; - } else { - cache.tagLastModifiedTs.put(tag, lastModifiedTs); - } - EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, false, null, tag)); - return; - } - if (cache.tagMd5.get(tag) == null || !cache.tagMd5.get(tag).equals(md5)) { - cache.tagMd5.put(tag, md5); - cache.tagLastModifiedTs.put(tag, lastModifiedTs); - EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, false, null, tag)); - } - } + public static void updateBetaMd5(String groupKey, String md5, List ips4Beta, long lastModifiedTs) { + CacheItem cache = makeSure(groupKey); + if (cache.md54Beta == null || !cache.md54Beta.equals(md5)) { + cache.isBeta = true; + cache.md54Beta = md5; + cache.lastModifiedTs4Beta = lastModifiedTs; + cache.ips4Beta = ips4Beta; + EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, true, ips4Beta)); + } + } + + public static void updateTagMd5(String groupKey, String tag, String md5, long lastModifiedTs) { + CacheItem cache = makeSure(groupKey); + if (cache.tagMd5 == null) { + Map tagMd5Tmp = new HashMap(1); + tagMd5Tmp.put(tag, md5); + cache.tagMd5 = tagMd5Tmp; + if (cache.tagLastModifiedTs == null) { + Map tagLastModifiedTsTmp = new HashMap(1); + tagLastModifiedTsTmp.put(tag, lastModifiedTs); + cache.tagLastModifiedTs = tagLastModifiedTsTmp; + } else { + cache.tagLastModifiedTs.put(tag, lastModifiedTs); + } + EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, false, null, tag)); + return; + } + if (cache.tagMd5.get(tag) == null || !cache.tagMd5.get(tag).equals(md5)) { + cache.tagMd5.put(tag, md5); + cache.tagLastModifiedTs.put(tag, lastModifiedTs); + EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey, false, null, tag)); + } + } /** * 返回cache的md5。零长度字符串表示没有该数据。 @@ -459,57 +456,57 @@ public class ConfigService { CacheItem item = CACHE.get(groupKey); return (null != item) ? item.md5 : Constants.NULL; } - + /** * 返回cache的md5。零长度字符串表示没有该数据。 */ static public String getContentBetaMd5(String groupKey) { - CacheItem item = CACHE.get(groupKey); - return (null != item) ? item.md54Beta : Constants.NULL; + CacheItem item = CACHE.get(groupKey); + return (null != item) ? item.md54Beta : Constants.NULL; } - + /** * 返回cache的md5。零长度字符串表示没有该数据。 */ static public String getContentTagMd5(String groupKey, String tag) { - CacheItem item = CACHE.get(groupKey); - if (item == null) { - return Constants.NULL; - } - if (item.tagMd5 == null) { - return Constants.NULL; - } - return item.tagMd5.get(tag); + CacheItem item = CACHE.get(groupKey); + if (item == null) { + return Constants.NULL; + } + if (item.tagMd5 == null) { + return Constants.NULL; + } + return item.tagMd5.get(tag); + } + + /** + * 返回beta Ip列表 + */ + static public List getBetaIps(String groupKey) { + CacheItem item = CACHE.get(groupKey); + return (null != item) ? item.getIps4Beta() : Collections.emptyList(); + } + + /** + * 返回cache。 + */ + static public CacheItem getContentCache(String groupKey) { + return CACHE.get(groupKey); } - - /** - * 返回beta Ip列表 - */ - static public List getBetaIps(String groupKey) { - CacheItem item = CACHE.get(groupKey); - return (null != item) ? item.getIps4Beta() : Collections.emptyList(); - } - /** - * 返回cache。 - */ - static public CacheItem getContentCache(String groupKey) { - return CACHE.get(groupKey); - } - static public String getContentMd5(String groupKey, String ip, String tag) { - CacheItem item = CACHE.get(groupKey); - if (item != null && item.isBeta) { - if (item.ips4Beta.contains(ip)) { - return item.md54Beta; - } - } - if (item != null && item.tagMd5 != null && item.tagMd5.size() > 0) { - if (StringUtils.isNotBlank(tag) && item.tagMd5.containsKey(tag)) { - return item.tagMd5.get(tag); - } - } - return (null != item) ? item.md5 : Constants.NULL; + CacheItem item = CACHE.get(groupKey); + if (item != null && item.isBeta) { + if (item.ips4Beta.contains(ip)) { + return item.md54Beta; + } + } + if (item != null && item.tagMd5 != null && item.tagMd5.size() > 0) { + if (StringUtils.isNotBlank(tag) && item.tagMd5.containsKey(tag)) { + return item.tagMd5.get(tag); + } + } + return (null != item) ? item.md5 : Constants.NULL; } static public long getLastModifiedTs(String groupKey) { @@ -521,15 +518,15 @@ public class ConfigService { String serverMd5 = ConfigService.getContentMd5(groupKey); return StringUtils.equals(md5, serverMd5); } - + static public boolean isUptodate(String groupKey, String md5, String ip, String tag) { - String serverMd5 = ConfigService.getContentMd5(groupKey, ip, tag); - return StringUtils.equals(md5, serverMd5); + String serverMd5 = ConfigService.getContentMd5(groupKey, ip, tag); + return StringUtils.equals(md5, serverMd5); } /** * 给数据加读锁。如果成功,后面必须调用{@link #releaseReadLock(String)},失败则不需要。 - * + * * @param groupKey * @return 零表示没有数据,失败。正数表示成功,负数表示有写锁导致加锁失败。 */ @@ -541,17 +538,17 @@ public class ConfigService { } return result; } - + static public void releaseReadLock(String groupKey) { CacheItem item = CACHE.get(groupKey); if (null != item) { item.rwLock.releaseReadLock(); } } - + /** * 给数据加写锁。如果成功,后面必须调用{@link #releaseWriteLock(String)},失败则不需要。 - * + * * @param groupKey * @return 零表示没有数据,失败。正数表示成功,负数表示加锁失败。 */ @@ -563,15 +560,14 @@ public class ConfigService { } return result; } - + static void releaseWriteLock(String groupKey) { CacheItem groupItem = CACHE.get(groupKey); if (null != groupItem) { groupItem.rwLock.releaseWriteLock(); } } - - + static CacheItem makeSure(final String groupKey) { CacheItem item = CACHE.get(groupKey); if (null != item) { @@ -580,18 +576,17 @@ public class ConfigService { CacheItem tmp = new CacheItem(groupKey); item = CACHE.putIfAbsent(groupKey, tmp); return (null == item) ? tmp : item; - } - + } - private final static String NO_SPACE_CN = "设备上没有空间"; - private final static String NO_SPACE_EN = "No space left on device"; - private final static String DISK_QUATA_CN = "超出磁盘限额"; - private final static String DISK_QUATA_EN = "Disk quota exceeded"; + private final static String NO_SPACE_CN = "设备上没有空间"; + private final static String NO_SPACE_EN = "No space left on device"; + private final static String DISK_QUATA_CN = "超出磁盘限额"; + private final static String DISK_QUATA_EN = "Disk quota exceeded"; static final Logger log = LoggerFactory.getLogger(ConfigService.class); /** * groupKey -> cacheItem - */ + */ static private final ConcurrentHashMap CACHE = - new ConcurrentHashMap(); + new ConcurrentHashMap(); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigSubService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigSubService.java index 759f6f1db..86f3e32b3 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigSubService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigSubService.java @@ -15,30 +15,6 @@ */ package com.alibaba.nacos.config.server.service; -import java.net.HttpURLConnection; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletionService; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.apache.commons.lang.StringUtils; -import org.codehaus.jackson.type.TypeReference; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.service.notify.NotifyService; @@ -46,222 +22,237 @@ import com.alibaba.nacos.config.server.utils.JSONUtils; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.RunningConfigUtils; import com.alibaba.nacos.config.server.utils.ThreadUtil; +import org.apache.commons.lang3.StringUtils; +import org.codehaus.jackson.type.TypeReference; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.net.HttpURLConnection; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; + /** * config sub service - * @author Nacos * + * @author Nacos */ @Service public class ConfigSubService { - private ScheduledExecutorService scheduler; + private ScheduledExecutorService scheduler; - private ServerListService serverListService; + private ServerListService serverListService; - @Autowired - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ConfigSubService(ServerListService serverListService1) { - this.serverListService = serverListService1; + @Autowired + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public ConfigSubService(ServerListService serverListService1) { + this.serverListService = serverListService1; - scheduler = Executors.newScheduledThreadPool( - ThreadUtil.getSuitableThreadCount(), new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("com.alibaba.nacos.ConfigSubService"); - return t; - } - }); - } + scheduler = Executors.newScheduledThreadPool( + ThreadUtil.getSuitableThreadCount(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("com.alibaba.nacos.ConfigSubService"); + return t; + } + }); + } - protected ConfigSubService() { + protected ConfigSubService() { - } + } - /** - * 获得调用的URL - * @param ip ip - * @param relativePath path - * @return all path - */ - private String getUrl(String ip, String relativePath) { - return "http://" + ip + RunningConfigUtils.getContextPath() + relativePath; - } + /** + * 获得调用的URL + * + * @param ip ip + * @param relativePath path + * @return all path + */ + private String getUrl(String ip, String relativePath) { + return "http://" + ip + RunningConfigUtils.getContextPath() + relativePath; + } - private List runCollectionJob(String url, Map params, - CompletionService completionService, - List resultList) { + private List runCollectionJob(String url, Map params, + CompletionService completionService, + List resultList) { - List ipList = serverListService.getServerList(); - List collectionResult = new ArrayList( - ipList.size()); - // 提交查询任务 - for (String ip : ipList) { - try { - completionService.submit(new Job(ip, url, params)); - } catch (Exception e) { // 发送请求失败 - LogUtil.defaultLog - .warn("Get client info from {} with exception: {} during submit job", - ip, e.getMessage()); - } - } - // 获取结果并合并 - SampleResult sampleResults = null; - for (int i = 0; i < ipList.size(); i++) { - try { - Future f = completionService.poll(1000, - TimeUnit.MILLISECONDS); - try { - if (f != null) { - sampleResults = f.get(500, TimeUnit.MILLISECONDS); - if (sampleResults != null) { - collectionResult.add(sampleResults); - } - } else { - LogUtil.defaultLog - .warn("The task in ip: {} did not completed in 1000ms ", - ipList.get(i)); - } - } catch (TimeoutException e) { - if (f != null) { - f.cancel(true); - } - LogUtil.defaultLog.warn( - "get task result with TimeoutException: {} ", e - .getMessage()); - } - } catch (InterruptedException e) { - LogUtil.defaultLog.warn( - "get task result with InterruptedException: {} ", e - .getMessage()); - } catch (ExecutionException e) { - LogUtil.defaultLog.warn( - "get task result with ExecutionException: {} ", e - .getMessage()); - } - } - return collectionResult; - } + List ipList = serverListService.getServerList(); + List collectionResult = new ArrayList( + ipList.size()); + // 提交查询任务 + for (String ip : ipList) { + try { + completionService.submit(new Job(ip, url, params)); + } catch (Exception e) { // 发送请求失败 + LogUtil.defaultLog + .warn("Get client info from {} with exception: {} during submit job", + ip, e.getMessage()); + } + } + // 获取结果并合并 + SampleResult sampleResults = null; + for (int i = 0; i < ipList.size(); i++) { + try { + Future f = completionService.poll(1000, + TimeUnit.MILLISECONDS); + try { + if (f != null) { + sampleResults = f.get(500, TimeUnit.MILLISECONDS); + if (sampleResults != null) { + collectionResult.add(sampleResults); + } + } else { + LogUtil.defaultLog + .warn("The task in ip: {} did not completed in 1000ms ", + ipList.get(i)); + } + } catch (TimeoutException e) { + if (f != null) { + f.cancel(true); + } + LogUtil.defaultLog.warn( + "get task result with TimeoutException: {} ", e + .getMessage()); + } + } catch (InterruptedException e) { + LogUtil.defaultLog.warn( + "get task result with InterruptedException: {} ", e + .getMessage()); + } catch (ExecutionException e) { + LogUtil.defaultLog.warn( + "get task result with ExecutionException: {} ", e + .getMessage()); + } + } + return collectionResult; + } - public SampleResult mergeSampleResult(SampleResult sampleCollectResult, List sampleResults) { - SampleResult mergeResult = new SampleResult(); - Map lisentersGroupkeyStatus = null; - if (sampleCollectResult.getLisentersGroupkeyStatus() == null - || sampleCollectResult.getLisentersGroupkeyStatus().isEmpty()) { - lisentersGroupkeyStatus = new HashMap(10); - } else { - lisentersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus(); - } + public SampleResult mergeSampleResult(SampleResult sampleCollectResult, List sampleResults) { + SampleResult mergeResult = new SampleResult(); + Map lisentersGroupkeyStatus = null; + if (sampleCollectResult.getLisentersGroupkeyStatus() == null + || sampleCollectResult.getLisentersGroupkeyStatus().isEmpty()) { + lisentersGroupkeyStatus = new HashMap(10); + } else { + lisentersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus(); + } - for (SampleResult sampleResult : sampleResults) { - Map lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus(); - for (Map.Entry entry : lisentersGroupkeyStatusTmp.entrySet()) { - lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue()); - } - } - mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); - return mergeResult; - } + for (SampleResult sampleResult : sampleResults) { + Map lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus(); + for (Map.Entry entry : lisentersGroupkeyStatusTmp.entrySet()) { + lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue()); + } + } + mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); + return mergeResult; + } - /** - * 去每个Nacos Server节点查询订阅者的任务 - * @author Nacos - */ - class Job implements Callable { - private String ip; - private String url; - private Map params; + /** + * 去每个Nacos Server节点查询订阅者的任务 + * + * @author Nacos + */ + class Job implements Callable { + private String ip; + private String url; + private Map params; - public Job(String ip, String url, Map params) { - this.ip = ip; - this.url = url; - this.params = params; - } + public Job(String ip, String url, Map params) { + this.ip = ip; + this.url = url; + this.params = params; + } - @Override - public SampleResult call() throws Exception { + @Override + public SampleResult call() throws Exception { - try { - StringBuilder paramUrl = new StringBuilder(); - for (Map.Entry param : params.entrySet()) { - paramUrl.append("&").append(param.getKey()).append("=") - .append(URLEncoder.encode(param.getValue(), Constants.ENCODE)); - } + try { + StringBuilder paramUrl = new StringBuilder(); + for (Map.Entry param : params.entrySet()) { + paramUrl.append("&").append(param.getKey()).append("=") + .append(URLEncoder.encode(param.getValue(), Constants.ENCODE)); + } - String urlAll = getUrl(ip, url) + "?" + paramUrl; - com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult result = NotifyService - .invokeURL(urlAll, null, Constants.ENCODE); - /** - * http code 200 - */ - if (result.code == HttpURLConnection.HTTP_OK) { - String json = result.content; - Object resultObj = JSONUtils.deserializeObject(json, - new TypeReference() { - }); - return (SampleResult) resultObj; + String urlAll = getUrl(ip, url) + "?" + paramUrl; + com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult result = NotifyService + .invokeURL(urlAll, null, Constants.ENCODE); + /** + * http code 200 + */ + if (result.code == HttpURLConnection.HTTP_OK) { + String json = result.content; + Object resultObj = JSONUtils.deserializeObject(json, + new TypeReference() { + }); + return (SampleResult)resultObj; - } else { + } else { - LogUtil.defaultLog.info( - "Can not get clientInfo from {} with {}", ip, - result.code); - return null; - } - } catch (Exception e) { - LogUtil.defaultLog.warn( - "Get client info from {} with exception: {}", ip, e - .getMessage()); - return null; - } - } - } + LogUtil.defaultLog.info( + "Can not get clientInfo from {} with {}", ip, + result.code); + return null; + } + } catch (Exception e) { + LogUtil.defaultLog.warn( + "Get client info from {} with exception: {}", ip, e + .getMessage()); + return null; + } + } + } - public SampleResult getCollectSampleResult(String dataId, String group, String tenant, int sampleTime) - throws Exception { - List resultList = new ArrayList(); - String url = Constants.COMMUNICATION_CONTROLLER_PATH + "/configWatchers"; - Map params =new HashMap(5); - params.put("dataId", dataId); - params.put("group", group); - if (!StringUtils.isBlank(tenant)) { - params.put("tenant", tenant); - } - BlockingQueue> queue = new LinkedBlockingDeque>( - serverListService.getServerList().size()); - CompletionService completionService = new ExecutorCompletionService(scheduler, - queue); + public SampleResult getCollectSampleResult(String dataId, String group, String tenant, int sampleTime) + throws Exception { + List resultList = new ArrayList(); + String url = Constants.COMMUNICATION_CONTROLLER_PATH + "/configWatchers"; + Map params = new HashMap(5); + params.put("dataId", dataId); + params.put("group", group); + if (!StringUtils.isBlank(tenant)) { + params.put("tenant", tenant); + } + BlockingQueue> queue = new LinkedBlockingDeque>( + serverListService.getServerList().size()); + CompletionService completionService = new ExecutorCompletionService(scheduler, + queue); - SampleResult sampleCollectResult = new SampleResult(); - for (int i = 0; i < sampleTime; i++) { - List sampleResults = runCollectionJob(url, params, completionService, resultList); - if (sampleResults != null) { - sampleCollectResult = mergeSampleResult(sampleCollectResult, sampleResults); - } - } - return sampleCollectResult; - } - - public SampleResult getCollectSampleResultByIp(String ip, int sampleTime) - throws Exception { - List resultList = new ArrayList(10); - String url = Constants.COMMUNICATION_CONTROLLER_PATH + "/watcherConfigs"; - Map params =new HashMap(50); - params.put("ip", ip); - BlockingQueue> queue = new LinkedBlockingDeque>( - serverListService.getServerList().size()); - CompletionService completionService = new ExecutorCompletionService(scheduler, - queue); - - SampleResult sampleCollectResult = new SampleResult(); - for (int i = 0; i < sampleTime; i++) { - List sampleResults = runCollectionJob(url, params, completionService, resultList); - if (sampleResults != null) { - sampleCollectResult = mergeSampleResult(sampleCollectResult, sampleResults); - } - } - return sampleCollectResult; - } + SampleResult sampleCollectResult = new SampleResult(); + for (int i = 0; i < sampleTime; i++) { + List sampleResults = runCollectionJob(url, params, completionService, resultList); + if (sampleResults != null) { + sampleCollectResult = mergeSampleResult(sampleCollectResult, sampleResults); + } + } + return sampleCollectResult; + } + + public SampleResult getCollectSampleResultByIp(String ip, int sampleTime) + throws Exception { + List resultList = new ArrayList(10); + String url = Constants.COMMUNICATION_CONTROLLER_PATH + "/watcherConfigs"; + Map params = new HashMap(50); + params.put("ip", ip); + BlockingQueue> queue = new LinkedBlockingDeque>( + serverListService.getServerList().size()); + CompletionService completionService = new ExecutorCompletionService(scheduler, + queue); + + SampleResult sampleCollectResult = new SampleResult(); + for (int i = 0; i < sampleTime; i++) { + List sampleResults = runCollectionJob(url, params, completionService, resultList); + if (sampleResults != null) { + sampleCollectResult = mergeSampleResult(sampleCollectResult, sampleResults); + } + } + return sampleCollectResult; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/DataSourceService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/DataSourceService.java index af4cd63db..a64a22a6b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/DataSourceService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/DataSourceService.java @@ -22,51 +22,49 @@ import java.io.IOException; /** * datasource interface - * - * @author Nacos * + * @author Nacos */ public interface DataSourceService { - /** - * reload - * - * @throws IOException - * exception - */ - void reload() throws IOException; + /** + * reload + * + * @throws IOException exception + */ + void reload() throws IOException; - /** - * check master db - * - * @return is master - */ - boolean checkMasterWritable(); + /** + * check master db + * + * @return is master + */ + boolean checkMasterWritable(); - /** - * get jdbc template - * - * @return JdbcTemplate - */ - JdbcTemplate getJdbcTemplate(); + /** + * get jdbc template + * + * @return JdbcTemplate + */ + JdbcTemplate getJdbcTemplate(); - /** - * get transaction template - * - * @return TransactionTemplate - */ - TransactionTemplate getTransactionTemplate(); + /** + * get transaction template + * + * @return TransactionTemplate + */ + TransactionTemplate getTransactionTemplate(); - /** - * get current db url - * - * @return - */ - String getCurrentDBUrl(); + /** + * get current db url + * + * @return + */ + String getCurrentDBUrl(); - /** - * get heath - * - * @return heath info - */ - String getHealth(); + /** + * get heath + * + * @return heath info + */ + String getHealth(); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/DiskUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/service/DiskUtil.java index 24c1797af..1865eca79 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/DiskUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/DiskUtil.java @@ -15,56 +15,45 @@ */ package com.alibaba.nacos.config.server.service; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.utils.LogUtil; +import com.alibaba.nacos.config.server.utils.MD5; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.utils.LogUtil; -import com.alibaba.nacos.config.server.utils.MD5; - +import static com.alibaba.nacos.common.util.SystemUtils.NACOS_HOME; /** * 磁盘操作工具类。 - * + *

* 只有一个dump线程。 - * + * * @author jiuRen */ public class DiskUtil { - static final Logger logger = LoggerFactory.getLogger(DiskUtil.class); - static String APP_HOME = System.getProperty("user.home") + File.separator + "nacos"; - static final String BASE_DIR = File.separator + "data" + File.separator + "config-data"; - static final String TENANT_BASE_DIR = File.separator + "data" + File.separator + "tenant-config-data"; - static final String BETA_DIR = File.separator + "data" + File.separator + "beta-data"; - static final String TENANT_BETA_DIR = File.separator + "data " + File.separator + "tenant-beta-data"; - static final String TAG_DIR = File.separator + "data" + File.separator + "tag-data"; - static final String TENANT_TAG_DIR = File.separator + "data" + File.separator + "tag-beta-data"; - static String SERVERLIST_DIR = File.separator + "conf"; - static String SERVERLIST_FILENAME = "cluster.conf"; + static final Logger logger = LoggerFactory.getLogger(DiskUtil.class); + static final String BASE_DIR = File.separator + "data" + File.separator + "config-data"; + static final String TENANT_BASE_DIR = File.separator + "data" + File.separator + "tenant-config-data"; + static final String BETA_DIR = File.separator + "data" + File.separator + "beta-data"; + static final String TENANT_BETA_DIR = File.separator + "data" + File.separator + "tenant-beta-data"; + static final String TAG_DIR = File.separator + "data" + File.separator + "tag-data"; + static final String TENANT_TAG_DIR = File.separator + "data" + File.separator + "tag-beta-data"; - static { - String nacosDir = System.getProperty("nacos.home"); - if (!StringUtils.isBlank(nacosDir)) { - APP_HOME = nacosDir; - } - } + static public void saveHeartBeatToDisk(String heartBeatTime) + throws IOException { + FileUtils.writeStringToFile(heartBeatFile(), heartBeatTime, + Constants.ENCODE); + } - - static public void saveHeartBeatToDisk(String heartBeatTime) - throws IOException { - FileUtils.writeStringToFile(heartBeatFile(), heartBeatTime, - Constants.ENCODE); - } - /** * 保存配置信息到磁盘 */ @@ -72,189 +61,172 @@ public class DiskUtil { File targetFile = targetFile(dataId, group, tenant); FileUtils.writeStringToFile(targetFile, content, Constants.ENCODE); } - + /** * 保存配置信息到磁盘 */ static public void saveBetaToDisk(String dataId, String group, String tenant, String content) throws IOException { - File targetFile = targetBetaFile(dataId, group, tenant); - FileUtils.writeStringToFile(targetFile, content, Constants.ENCODE); + File targetFile = targetBetaFile(dataId, group, tenant); + FileUtils.writeStringToFile(targetFile, content, Constants.ENCODE); + } + + /** + * 保存配置信息到磁盘 + */ + static public void saveTagToDisk(String dataId, String group, String tenant, String tag, String content) + throws IOException { + File targetFile = targetTagFile(dataId, group, tenant, tag); + FileUtils.writeStringToFile(targetFile, content, Constants.ENCODE); + } + + /** + * 删除磁盘上的配置文件 + */ + static public void removeConfigInfo(String dataId, String group, String tenant) { + FileUtils.deleteQuietly(targetFile(dataId, group, tenant)); } - - /** - * 保存配置信息到磁盘 - */ - static public void saveTagToDisk(String dataId, String group, String tenant, String tag, String content) - throws IOException { - File targetFile = targetTagFile(dataId, group, tenant, tag); - FileUtils.writeStringToFile(targetFile, content, Constants.ENCODE); - } - /** - * 删除磁盘上的配置文件 - */ - static public void removeConfigInfo(String dataId, String group, String tenant) { - FileUtils.deleteQuietly(targetFile(dataId, group, tenant)); - } - /** * 删除磁盘上的配置文件 */ static public void removeConfigInfo4Beta(String dataId, String group, String tenant) { - - FileUtils.deleteQuietly(targetBetaFile(dataId, group, tenant)); + + FileUtils.deleteQuietly(targetBetaFile(dataId, group, tenant)); } - - /** - * 删除磁盘上的配置文件 - */ - static public void removeConfigInfo4Tag(String dataId, String group, String tenant, String tag) { - FileUtils.deleteQuietly(targetTagFile(dataId, group, tenant, tag)); - } + /** + * 删除磁盘上的配置文件 + */ + static public void removeConfigInfo4Tag(String dataId, String group, String tenant, String tag) { - static public void removeHeartHeat() { - FileUtils.deleteQuietly(heartBeatFile()); - } + FileUtils.deleteQuietly(targetTagFile(dataId, group, tenant, tag)); + } + + static public void removeHeartHeat() { + FileUtils.deleteQuietly(heartBeatFile()); + } /** * 返回服务端缓存文件的路径 */ - static public File targetFile(String dataId, String group, String tenant) { - File file = null; - if (StringUtils.isBlank(tenant)) { - file = new File(APP_HOME, BASE_DIR); - } else { - file = new File(APP_HOME, TENANT_BASE_DIR); - file = new File(file, tenant); - } - file = new File(file, group); - file = new File(file, dataId); - return file; - } - + static public File targetFile(String dataId, String group, String tenant) { + File file = null; + if (StringUtils.isBlank(tenant)) { + file = new File(NACOS_HOME, BASE_DIR); + } else { + file = new File(NACOS_HOME, TENANT_BASE_DIR); + file = new File(file, tenant); + } + file = new File(file, group); + file = new File(file, dataId); + return file; + } + /** * 返回服务端beta缓存文件的路径 */ - static public File targetBetaFile(String dataId, String group, String tenant) { - File file = null; - if (StringUtils.isBlank(tenant)) { - file = new File(APP_HOME, BETA_DIR); - } else { - file = new File(APP_HOME, TENANT_BETA_DIR); - file = new File(file, tenant); - } - file = new File(file, group); - file = new File(file, dataId); - return file; - } - - /** - * 返回服务端Tag缓存文件的路径 - */ - static public File targetTagFile(String dataId, String group, String tenant, String tag) { - File file = null; - if (StringUtils.isBlank(tenant)) { - file = new File(APP_HOME, TAG_DIR); - } else { - file = new File(APP_HOME, TENANT_TAG_DIR); - file = new File(file, tenant); - } - file = new File(file, group); - file = new File(file, dataId); - file = new File(file, tag); - return file; - } - - static public String getConfig(String dataId, String group, String tenant) - throws IOException { - FileInputStream fis = null; - File file = targetFile(dataId, group, tenant); - if (file.exists()) { - try { - fis = new FileInputStream(file); - } catch (FileNotFoundException e) { - return StringUtils.EMPTY; - } - String content = IOUtils.toString(fis, Constants.ENCODE); - return content; - } else { - return StringUtils.EMPTY; - } - } - - static public String getLocalConfigMd5(String dataId, String group, String tenant) - throws IOException { - return MD5.getInstance().getMD5String(getConfig(dataId, group, tenant)); - } - - static public File heartBeatFile() { - return new File(APP_HOME, "status/heartBeat.txt"); - } + static public File targetBetaFile(String dataId, String group, String tenant) { + File file = null; + if (StringUtils.isBlank(tenant)) { + file = new File(NACOS_HOME, BETA_DIR); + } else { + file = new File(NACOS_HOME, TENANT_BETA_DIR); + file = new File(file, tenant); + } + file = new File(file, group); + file = new File(file, dataId); + return file; + } + + /** + * 返回服务端Tag缓存文件的路径 + */ + static public File targetTagFile(String dataId, String group, String tenant, String tag) { + File file = null; + if (StringUtils.isBlank(tenant)) { + file = new File(NACOS_HOME, TAG_DIR); + } else { + file = new File(NACOS_HOME, TENANT_TAG_DIR); + file = new File(file, tenant); + } + file = new File(file, group); + file = new File(file, dataId); + file = new File(file, tag); + return file; + } + + static public String getConfig(String dataId, String group, String tenant) + throws IOException { + FileInputStream fis = null; + File file = targetFile(dataId, group, tenant); + if (file.exists()) { + try { + fis = new FileInputStream(file); + } catch (FileNotFoundException e) { + return StringUtils.EMPTY; + } + String content = IOUtils.toString(fis, Constants.ENCODE); + return content; + } else { + return StringUtils.EMPTY; + } + } + + static public String getLocalConfigMd5(String dataId, String group, String tenant) + throws IOException { + return MD5.getInstance().getMD5String(getConfig(dataId, group, tenant)); + } + + static public File heartBeatFile() { + return new File(NACOS_HOME, "status/heartBeat.txt"); + } static public String relativePath(String dataId, String group) { return BASE_DIR + "/" + dataId + "/" + group; } static public void clearAll() { - File file = new File(APP_HOME, BASE_DIR); + File file = new File(NACOS_HOME, BASE_DIR); if (FileUtils.deleteQuietly(file)) { LogUtil.defaultLog.info("clear all config-info success."); } else { LogUtil.defaultLog.warn("clear all config-info failed."); } - File fileTenant = new File(APP_HOME, TENANT_BASE_DIR); + File fileTenant = new File(NACOS_HOME, TENANT_BASE_DIR); if (FileUtils.deleteQuietly(fileTenant)) { - LogUtil.defaultLog.info("clear all config-info-tenant success."); + LogUtil.defaultLog.info("clear all config-info-tenant success."); } else { - LogUtil.defaultLog.warn("clear all config-info-tenant failed."); + LogUtil.defaultLog.warn("clear all config-info-tenant failed."); } } - + static public void clearAllBeta() { - File file = new File(APP_HOME, BETA_DIR); - if (FileUtils.deleteQuietly(file)) { - LogUtil.defaultLog.info("clear all config-info-beta success."); - } else { - LogUtil.defaultLog.warn("clear all config-info-beta failed."); - } - File fileTenant = new File(APP_HOME, TENANT_BETA_DIR); - if (FileUtils.deleteQuietly(fileTenant)) { - LogUtil.defaultLog.info("clear all config-info-beta-tenant success."); - } else { - LogUtil.defaultLog.warn("clear all config-info-beta-tenant failed."); - } + File file = new File(NACOS_HOME, BETA_DIR); + if (FileUtils.deleteQuietly(file)) { + LogUtil.defaultLog.info("clear all config-info-beta success."); + } else { + LogUtil.defaultLog.warn("clear all config-info-beta failed."); + } + File fileTenant = new File(NACOS_HOME, TENANT_BETA_DIR); + if (FileUtils.deleteQuietly(fileTenant)) { + LogUtil.defaultLog.info("clear all config-info-beta-tenant success."); + } else { + LogUtil.defaultLog.warn("clear all config-info-beta-tenant failed."); + } } - + static public void clearAllTag() { - File file = new File(APP_HOME, TAG_DIR); - if (FileUtils.deleteQuietly(file)) { - LogUtil.defaultLog.info("clear all config-info-tag success."); - } else { - LogUtil.defaultLog.warn("clear all config-info-tag failed."); - } - File fileTenant = new File(APP_HOME, TENANT_TAG_DIR); - if (FileUtils.deleteQuietly(fileTenant)) { - LogUtil.defaultLog.info("clear all config-info-tag-tenant success."); - } else { - LogUtil.defaultLog.warn("clear all config-info-tag-tenant failed."); - } + File file = new File(NACOS_HOME, TAG_DIR); + if (FileUtils.deleteQuietly(file)) { + LogUtil.defaultLog.info("clear all config-info-tag success."); + } else { + LogUtil.defaultLog.warn("clear all config-info-tag failed."); + } + File fileTenant = new File(NACOS_HOME, TENANT_TAG_DIR); + if (FileUtils.deleteQuietly(fileTenant)) { + LogUtil.defaultLog.info("clear all config-info-tag-tenant success."); + } else { + LogUtil.defaultLog.warn("clear all config-info-tag-tenant failed."); + } } - - public static String getServerList() throws IOException { - FileInputStream fis = null; - File file = new File(APP_HOME, SERVERLIST_DIR); - file = new File(file, SERVERLIST_FILENAME); - if (file.exists()) { - try { - fis = new FileInputStream(file); - } catch (FileNotFoundException e) { - return StringUtils.EMPTY; - } - String content = IOUtils.toString(fis, Constants.ENCODE); - return content; - } else { - return StringUtils.EMPTY; - } - } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/DynamicDataSource.java b/config/src/main/java/com/alibaba/nacos/config/server/service/DynamicDataSource.java index b720aaf3e..c1503f3d4 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/DynamicDataSource.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/DynamicDataSource.java @@ -16,17 +16,24 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.config.server.utils.PropertyUtil; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; + /** * datasource adapter - * @author Nacos * + * @author Nacos */ @Component public class DynamicDataSource implements ApplicationContextAware { + + @Autowired + private PropertyUtil propertyUtil; + private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) { @@ -40,7 +47,7 @@ public class DynamicDataSource implements ApplicationContextAware { public DataSourceService getDataSource() { DataSourceService dataSourceService = null; - if (PropertyUtil.isStandaloneMode()) { + if (STANDALONE_MODE && !propertyUtil.isStandaloneUseMysql()) { dataSourceService = (DataSourceService)applicationContext.getBean("localDataSourceService"); } else { dataSourceService = (DataSourceService)applicationContext.getBean("basicDataSourceService"); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataChangeEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataChangeEvent.java index c88a88982..d687b9237 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataChangeEvent.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataChangeEvent.java @@ -15,12 +15,13 @@ */ package com.alibaba.nacos.config.server.service; -import java.util.List; - import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event; +import java.util.List; + /** * 本地数据发生变更的事件。 + * * @author Nacos */ public class LocalDataChangeEvent implements Event { @@ -28,26 +29,25 @@ public class LocalDataChangeEvent implements Event { final public boolean isBeta; final public List betaIps; final public String tag; - - + public LocalDataChangeEvent(String groupKey) { this.groupKey = groupKey; - this.isBeta = false; + this.isBeta = false; this.betaIps = null; this.tag = null; } - public LocalDataChangeEvent(String groupKey, boolean isBeta, List betaIps) { - this.groupKey = groupKey; - this.isBeta = isBeta; - this.betaIps = betaIps; - this.tag = null; - } - - public LocalDataChangeEvent(String groupKey, boolean isBeta, List betaIps, String tag) { - this.groupKey = groupKey; - this.isBeta = isBeta; - this.betaIps = betaIps; - this.tag = tag; - } + public LocalDataChangeEvent(String groupKey, boolean isBeta, List betaIps) { + this.groupKey = groupKey; + this.isBeta = isBeta; + this.betaIps = betaIps; + this.tag = null; + } + + public LocalDataChangeEvent(String groupKey, boolean isBeta, List betaIps, String tag) { + this.groupKey = groupKey; + this.isBeta = isBeta; + this.betaIps = betaIps; + this.tag = tag; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataSourceServiceImpl.java b/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataSourceServiceImpl.java index 527ce5503..81f0f05df 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataSourceServiceImpl.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/LocalDataSourceServiceImpl.java @@ -15,6 +15,21 @@ */ package com.alibaba.nacos.config.server.service; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.utils.LogUtil; +import com.alibaba.nacos.config.server.utils.PropertyUtil; +import com.alibaba.nacos.config.server.utils.StringUtils; +import org.apache.commons.dbcp.BasicDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; @@ -25,47 +40,36 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; -import javax.sql.DataSource; - -import com.alibaba.nacos.config.server.utils.PropertyUtil; -import org.apache.commons.dbcp.BasicDataSource; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; -import org.springframework.stereotype.Service; -import org.springframework.transaction.support.TransactionTemplate; - -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.utils.LogUtil; -import com.alibaba.nacos.config.server.utils.StringUtils; +import static com.alibaba.nacos.common.util.SystemUtils.NACOS_HOME; +import static com.alibaba.nacos.common.util.SystemUtils.NACOS_HOME_KEY; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; /** * local data source - * - * @author Nacos * + * @author Nacos */ @Service("localDataSourceService") public class LocalDataSourceServiceImpl implements DataSourceService { - private static final String JDBC_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; - private static final String DERBY_BASE_DIR = "data" + File.separator + "derby-data"; - private static String appHome = System.getProperty("user.home") + File.separator + "nacos"; - private static final String NACOS_HOME_KEY = "nacos.home"; - private static final String USER_NAME = "nacos"; - private static final String PASSWORD = "nacos"; + + private static final Logger logger = LoggerFactory.getLogger(LocalDataSourceServiceImpl.class); + + private static final String JDBC_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; + private static final String DERBY_BASE_DIR = "data" + File.separator + "derby-data"; + private static final String USER_NAME = "nacos"; + private static final String PASSWORD = "nacos"; private JdbcTemplate jt; private TransactionTemplate tjt; + @Autowired + private PropertyUtil propertyUtil; + @PostConstruct public void init() { - String nacosBaseDir = System.getProperty(NACOS_HOME_KEY); - if (!StringUtils.isBlank(nacosBaseDir)) { - setAppHome(nacosBaseDir); - } BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName(JDBC_DRIVER_NAME); - ds.setUrl("jdbc:derby:" + appHome + File.separator + DERBY_BASE_DIR + ";create=true"); + ds.setUrl("jdbc:derby:" + NACOS_HOME + File.separator + DERBY_BASE_DIR + ";create=true"); ds.setUsername(USER_NAME); ds.setPassword(PASSWORD); ds.setInitialSize(20); @@ -74,7 +78,7 @@ public class LocalDataSourceServiceImpl implements DataSourceService { ds.setMaxWait(10000L); ds.setPoolPreparedStatements(true); ds.setTimeBetweenEvictionRunsMillis(TimeUnit.MINUTES - .toMillis(10L)); + .toMillis(10L)); ds.setTestWhileIdle(true); jt = new JdbcTemplate(); @@ -86,23 +90,26 @@ public class LocalDataSourceServiceImpl implements DataSourceService { tm.setDataSource(ds); tjt.setTimeout(5000); - if (PropertyUtil.isStandaloneMode()) { + if (STANDALONE_MODE && !propertyUtil.isStandaloneUseMysql()) { reload(); } } @Override - public void reload() { - DataSource ds = jt.getDataSource(); - if (ds == null) { - throw new RuntimeException("datasource is null"); - } - try { - execute(ds.getConnection(), "schema.sql"); - } catch (Exception e) { - throw new RuntimeException("load schema.sql error." + e); - } - } + public void reload() { + DataSource ds = jt.getDataSource(); + if (ds == null) { + throw new RuntimeException("datasource is null"); + } + try { + execute(ds.getConnection(), "META-INF/schema.sql"); + } catch (Exception e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + throw new RuntimeException("load schema.sql error." + e); + } + } @Override public boolean checkMasterWritable() { @@ -121,7 +128,7 @@ public class LocalDataSourceServiceImpl implements DataSourceService { @Override public String getCurrentDBUrl() { - return "jdbc:derby:" + appHome + File.separator + DERBY_BASE_DIR + ";create=true"; + return "jdbc:derby:" + NACOS_HOME + File.separator + DERBY_BASE_DIR + ";create=true"; } @Override @@ -131,6 +138,7 @@ public class LocalDataSourceServiceImpl implements DataSourceService { /** * 读取SQL文件 + * * @param sqlFile sql * @return sqls * @throws Exception Exception @@ -139,21 +147,22 @@ public class LocalDataSourceServiceImpl implements DataSourceService { List sqlList = new ArrayList(); InputStream sqlFileIn = null; try { - if (StringUtils.isBlank(System.getProperty(NACOS_HOME_KEY))) { + if (StringUtils.isBlank(System.getProperty(NACOS_HOME_KEY))) { ClassLoader classLoader = getClass().getClassLoader(); URL url = classLoader.getResource(sqlFile); sqlFileIn = url.openStream(); } else { - File file = new File(System.getProperty(NACOS_HOME_KEY) + File.separator + "conf" + File.separator + sqlFile); + File file = new File( + System.getProperty(NACOS_HOME_KEY) + File.separator + "conf" + File.separator + "schema.sql"); sqlFileIn = new FileInputStream(file); } StringBuffer sqlSb = new StringBuffer(); byte[] buff = new byte[1024]; int byteRead = 0; - while ((byteRead = sqlFileIn.read(buff)) != -1) { - sqlSb.append(new String(buff, 0, byteRead, Constants.ENCODE)); - } + while ((byteRead = sqlFileIn.read(buff)) != -1) { + sqlSb.append(new String(buff, 0, byteRead, Constants.ENCODE)); + } String[] sqlArr = sqlSb.toString().split(";"); for (int i = 0; i < sqlArr.length; i++) { @@ -163,43 +172,39 @@ public class LocalDataSourceServiceImpl implements DataSourceService { } } return sqlList; - } catch (Exception ex) { - throw new Exception(ex.getMessage()); - } finally { - if (sqlFileIn != null) { - sqlFileIn.close(); - } - } + } catch (Exception ex) { + throw new Exception(ex.getMessage()); + } finally { + if (sqlFileIn != null) { + sqlFileIn.close(); + } + } } /** * 执行SQL语句 - * @param conn connect + * + * @param conn connect * @param sqlFile sql * @throws Exception Exception */ - public void execute(Connection conn, String sqlFile) throws Exception { + private void execute(Connection conn, String sqlFile) throws Exception { Statement stmt = null; - List sqlList = loadSql(sqlFile); - stmt = conn.createStatement(); - for (String sql : sqlList) { - try { - stmt.execute(sql); - } catch (Exception e) { - LogUtil.defaultLog.info(e.getMessage()); + try { + List sqlList = loadSql(sqlFile); + stmt = conn.createStatement(); + for (String sql : sqlList) { + try { + stmt.execute(sql); + } catch (Exception e) { + LogUtil.defaultLog.info(e.getMessage()); + } + } + } finally { + if (stmt != null) { + stmt.close(); } - } - stmt.close(); } - public static String getAppHome() { - return appHome; - } - - public static void setAppHome(String appHome) { - LocalDataSourceServiceImpl.appHome = appHome; - } - - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java new file mode 100755 index 000000000..ff7b9e9a6 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java @@ -0,0 +1,501 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.config.server.service; + +import com.alibaba.nacos.config.server.model.SampleResult; +import com.alibaba.nacos.config.server.utils.GroupKey; +import com.alibaba.nacos.config.server.utils.LogUtil; +import com.alibaba.nacos.config.server.utils.MD5Util; +import com.alibaba.nacos.config.server.utils.RequestUtil; +import com.alibaba.nacos.config.server.utils.event.EventDispatcher.AbstractEventListener; +import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import javax.servlet.AsyncContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.concurrent.*; + +import static com.alibaba.nacos.config.server.utils.LogUtil.memoryLog; +import static com.alibaba.nacos.config.server.utils.LogUtil.pullLog; + +/** + * 长轮询服务。负责处理 + * + * @author Nacos + */ +@Service +public class LongPollingService extends AbstractEventListener { + + private static final int FIXED_POLLING_INTERVAL_MS = 10000; + + private static final int SAMPLE_PERIOD = 100; + + private static final int SAMPLE_TIMES = 3; + + private static final String TRUE_STR = "true"; + + private Map retainIps = new ConcurrentHashMap(); + + private static boolean isFixedPolling() { + return SwitchService.getSwitchBoolean(SwitchService.FIXED_POLLING, false); + } + + private static int getFixedPollingInterval() { + return SwitchService.getSwitchInteger(SwitchService.FIXED_POLLING_INTERVAL, FIXED_POLLING_INTERVAL_MS); + } + + public boolean isClientLongPolling(String clientIp) { + return getClientPollingRecord(clientIp) != null; + } + + public Map getClientSubConfigInfo(String clientIp) { + ClientLongPulling record = getClientPollingRecord(clientIp); + + if (record == null) { + return Collections.emptyMap(); + } + + return record.clientMd5Map; + } + + public SampleResult getSubscribleInfo(String dataId, String group, String tenant) { + String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); + SampleResult sampleResult = new SampleResult(); + Map lisentersGroupkeyStatus = new HashMap(50); + + for (ClientLongPulling clientLongPulling : allSubs) { + if (clientLongPulling.clientMd5Map.containsKey(groupKey)) { + lisentersGroupkeyStatus.put(clientLongPulling.ip, clientLongPulling.clientMd5Map.get(groupKey)); + } + } + sampleResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); + return sampleResult; + } + + public SampleResult getSubscribleInfoByIp(String clientIp) { + SampleResult sampleResult = new SampleResult(); + Map lisentersGroupkeyStatus = new HashMap(50); + + for (ClientLongPulling clientLongPulling : allSubs) { + if (clientLongPulling.ip.equals(clientIp)) { + // 一个ip可能有多个监听 + if (!lisentersGroupkeyStatus.equals(clientLongPulling.clientMd5Map)) { + lisentersGroupkeyStatus.putAll(clientLongPulling.clientMd5Map); + } + } + } + sampleResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); + return sampleResult; + } + + /** + * 聚合采样结果中的采样ip和监听配置的信息;合并策略用后面的覆盖前面的是没有问题的 + * + * @param sampleResults sample Results + * @return Results + */ + public SampleResult mergeSampleResult(List sampleResults) { + SampleResult mergeResult = new SampleResult(); + Map lisentersGroupkeyStatus = new HashMap(50); + for (SampleResult sampleResult : sampleResults) { + Map lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus(); + for (Map.Entry entry : lisentersGroupkeyStatusTmp.entrySet()) { + lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue()); + } + } + mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); + return mergeResult; + } + + public Map> collectApplicationSubscribeConfigInfos() { + if (allSubs == null || allSubs.isEmpty()) { + return null; + } + HashMap> app2Groupkeys = new HashMap>(50); + for (ClientLongPulling clientLongPulling : allSubs) { + if (StringUtils.isEmpty(clientLongPulling.appName) || "unknown".equalsIgnoreCase( + clientLongPulling.appName)) { + continue; + } + Set appSubscribeConfigs = app2Groupkeys.get(clientLongPulling.appName); + Set clientSubscribeConfigs = clientLongPulling.clientMd5Map.keySet(); + if (appSubscribeConfigs == null) { + appSubscribeConfigs = new HashSet(clientSubscribeConfigs.size()); + } + appSubscribeConfigs.addAll(clientSubscribeConfigs); + app2Groupkeys.put(clientLongPulling.appName, appSubscribeConfigs); + } + + return app2Groupkeys; + } + + public SampleResult getCollectSubscribleInfo(String dataId, String group, String tenant) { + List sampleResultLst = new ArrayList(50); + for (int i = 0; i < SAMPLE_TIMES; i++) { + SampleResult sampleTmp = getSubscribleInfo(dataId, group, tenant); + if (sampleTmp != null) { + sampleResultLst.add(sampleTmp); + } + if (i < SAMPLE_TIMES - 1) { + try { + Thread.sleep(SAMPLE_PERIOD); + } catch (InterruptedException e) { + LogUtil.clientLog.error("sleep wrong", e); + } + } + } + + SampleResult sampleResult = mergeSampleResult(sampleResultLst); + return sampleResult; + } + + public SampleResult getCollectSubscribleInfoByIp(String ip) { + SampleResult sampleResult = new SampleResult(); + sampleResult.setLisentersGroupkeyStatus(new HashMap(50)); + for (int i = 0; i < SAMPLE_TIMES; i++) { + SampleResult sampleTmp = getSubscribleInfoByIp(ip); + if (sampleTmp != null) { + if (sampleTmp.getLisentersGroupkeyStatus() != null + && !sampleResult.getLisentersGroupkeyStatus().equals(sampleTmp.getLisentersGroupkeyStatus())) { + sampleResult.getLisentersGroupkeyStatus().putAll(sampleTmp.getLisentersGroupkeyStatus()); + } + } + if (i < SAMPLE_TIMES - 1) { + try { + Thread.sleep(SAMPLE_PERIOD); + } catch (InterruptedException e) { + LogUtil.clientLog.error("sleep wrong", e); + } + } + } + return sampleResult; + } + + private ClientLongPulling getClientPollingRecord(String clientIp) { + if (allSubs == null) { + return null; + } + + for (ClientLongPulling clientLongPulling : allSubs) { + HttpServletRequest request = (HttpServletRequest)clientLongPulling.asyncContext.getRequest(); + + if (clientIp.equals(RequestUtil.getRemoteIp(request))) { + return clientLongPulling; + } + } + + return null; + } + + public void addLongPullingClient(HttpServletRequest req, HttpServletResponse rsp, Map clientMd5Map, + int probeRequestSize) { + + String str = req.getHeader(LongPollingService.LONG_PULLING_HEADER); + String noHangUpFlag = req.getHeader(LongPollingService.LONG_PULLING_NO_HANG_UP_HEADER); + String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); + String tag = req.getHeader("Vipserver-Tag"); + int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); + /** + * 提前500ms返回响应,为避免客户端超时 @qiaoyi.dingqy 2013.10.22改动 add delay time for LoadBalance + */ + long timeout = Math.max(10000, Long.parseLong(str) - delayTime); + if (isFixedPolling()) { + timeout = Math.max(10000, getFixedPollingInterval()); + // do nothing but set fix polling timeout + } else { + long start = System.currentTimeMillis(); + List changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map); + if (changedGroups.size() > 0) { + generateResponse(req, rsp, changedGroups); + LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", + System.currentTimeMillis() - start, "instant", RequestUtil.getRemoteIp(req), "polling", + clientMd5Map.size(), probeRequestSize, changedGroups.size()); + return; + } else if (noHangUpFlag != null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) { + LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "nohangup", + RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize, + changedGroups.size()); + return; + } + } + String ip = RequestUtil.getRemoteIp(req); + // 一定要由HTTP线程调用,否则离开后容器会立即发送响应 + final AsyncContext asyncContext = req.startAsync(); + // AsyncContext.setTimeout()的超时时间不准,所以只能自己控制 + asyncContext.setTimeout(0L); + + scheduler.execute( + new ClientLongPulling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag)); + } + + @Override + public List> interest() { + List> eventTypes = new ArrayList>(); + eventTypes.add(LocalDataChangeEvent.class); + return eventTypes; + } + + @Override + public void onEvent(Event event) { + if (isFixedPolling()) { + // ignore + } else { + if (event instanceof LocalDataChangeEvent) { + LocalDataChangeEvent evt = (LocalDataChangeEvent)event; + scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps)); + } + } + } + + static public boolean isSupportLongPulling(HttpServletRequest req) { + return null != req.getHeader(LONG_PULLING_HEADER); + } + + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public LongPollingService() { + allSubs = new ConcurrentLinkedQueue(); + + scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("com.alibaba.nacos.LongPulling"); + return t; + } + }); + scheduler.scheduleWithFixedDelay(new StatTask(), 0L, 10L, TimeUnit.SECONDS); + } + + // ================= + + static public final String LONG_PULLING_HEADER = "Long-Pulling-Timeout"; + static public final String LONG_PULLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup"; + + final ScheduledExecutorService scheduler; + + /** + * 长轮询订阅关系 + */ + final Queue allSubs; + + // ================= + + class DataChangeTask implements Runnable { + @Override + public void run() { + try { + ConfigService.getContentBetaMd5(groupKey); + for (Iterator iter = allSubs.iterator(); iter.hasNext(); ) { + ClientLongPulling clientSub = iter.next(); + if (clientSub.clientMd5Map.containsKey(groupKey)) { + // 如果beta发布且不在beta列表直接跳过 + if (isBeta && !betaIps.contains(clientSub.ip)) { + continue; + } + + // 如果tag发布且不在tag列表直接跳过 + if (StringUtils.isNotBlank(tag) && !tag.equals(clientSub.tag)) { + continue; + } + + getRetainIps().put(clientSub.ip, System.currentTimeMillis()); + iter.remove(); // 删除订阅关系 + LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", + (System.currentTimeMillis() - changeTime), + "in-advance", + RequestUtil.getRemoteIp((HttpServletRequest)clientSub.asyncContext.getRequest()), + "polling", + clientSub.clientMd5Map.size(), clientSub.probeRequestSize, groupKey); + clientSub.sendResponse(Arrays.asList(groupKey)); + } + } + } catch (Throwable t) { + LogUtil.defaultLog.error("data change error:" + t.getMessage(), t.getCause()); + } + } + + DataChangeTask(String groupKey) { + this(groupKey, false, null); + } + + DataChangeTask(String groupKey, boolean isBeta, List betaIps) { + this(groupKey, isBeta, betaIps, null); + } + + DataChangeTask(String groupKey, boolean isBeta, List betaIps, String tag) { + this.groupKey = groupKey; + this.isBeta = isBeta; + this.betaIps = betaIps; + this.tag = tag; + } + + final String groupKey; + final long changeTime = System.currentTimeMillis(); + final boolean isBeta; + final List betaIps; + final String tag; + } + + // ================= + + class StatTask implements Runnable { + @Override + public void run() { + memoryLog.info("[long-pulling] client count " + allSubs.size()); + } + } + + // ================= + + class ClientLongPulling implements Runnable { + + @Override + public void run() { + asyncTimeoutFuture = scheduler.schedule(new Runnable() { + public void run() { + try { + getRetainIps().put(ClientLongPulling.this.ip, System.currentTimeMillis()); + /** + * 删除订阅关系 + */ + allSubs.remove(ClientLongPulling.this); + + if (isFixedPolling()) { + LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}", + (System.currentTimeMillis() - createTime), + "fix", RequestUtil.getRemoteIp((HttpServletRequest)asyncContext.getRequest()), + "polling", + clientMd5Map.size(), probeRequestSize); + List changedGroups = MD5Util.compareMd5( + (HttpServletRequest)asyncContext.getRequest(), + (HttpServletResponse)asyncContext.getResponse(), clientMd5Map); + if (changedGroups.size() > 0) { + sendResponse(changedGroups); + } else { + sendResponse(null); + } + } else { + LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}", + (System.currentTimeMillis() - createTime), + "timeout", RequestUtil.getRemoteIp((HttpServletRequest)asyncContext.getRequest()), + "polling", + clientMd5Map.size(), probeRequestSize); + sendResponse(null); + } + } catch (Throwable t) { + LogUtil.defaultLog.error("long pulling error:" + t.getMessage(), t.getCause()); + } + + } + }, timeoutTime, TimeUnit.MILLISECONDS); + + allSubs.add(this); + } + + void sendResponse(List changedGroups) { + /** + * 取消超时任务 + */ + if (null != asyncTimeoutFuture) { + asyncTimeoutFuture.cancel(false); + } + generateResponse(changedGroups); + } + + void generateResponse(List changedGroups) { + if (null == changedGroups) { + /** + * 告诉容器发送HTTP响应 + */ + asyncContext.complete(); + return; + } + + HttpServletResponse response = (HttpServletResponse)asyncContext.getResponse(); + + try { + String respString = MD5Util.compareMd5ResultString(changedGroups); + + // 禁用缓存 + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + response.setHeader("Cache-Control", "no-cache,no-store"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println(respString); + asyncContext.complete(); + } catch (Exception se) { + pullLog.error(se.toString(), se); + asyncContext.complete(); + } + } + + ClientLongPulling(AsyncContext ac, Map clientMd5Map, String ip, int probeRequestSize, + long timeoutTime, String appName, String tag) { + this.asyncContext = ac; + this.clientMd5Map = clientMd5Map; + this.probeRequestSize = probeRequestSize; + this.createTime = System.currentTimeMillis(); + this.ip = ip; + this.timeoutTime = timeoutTime; + this.appName = appName; + this.tag = tag; + } + + // ================= + + final AsyncContext asyncContext; + final Map clientMd5Map; + final long createTime; + final String ip; + final String appName; + final String tag; + final int probeRequestSize; + final long timeoutTime; + + Future asyncTimeoutFuture; + } + + void generateResponse(HttpServletRequest request, HttpServletResponse response, List changedGroups) { + if (null == changedGroups) { + return; + } + + try { + String respString = MD5Util.compareMd5ResultString(changedGroups); + // 禁用缓存 + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + response.setHeader("Cache-Control", "no-cache,no-store"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println(respString); + } catch (Exception se) { + pullLog.error(se.toString(), se); + } + } + + public Map getRetainIps() { + return retainIps; + } + + public void setRetainIps(Map retainIps) { + this.retainIps = retainIps; + } + +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPullingService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/LongPullingService.java deleted file mode 100755 index 1609dce8e..000000000 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/LongPullingService.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.config.server.service; - -import static com.alibaba.nacos.config.server.utils.LogUtil.memoryLog; -import static com.alibaba.nacos.config.server.utils.LogUtil.pullLog; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - -import javax.servlet.AsyncContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; -import org.springframework.stereotype.Service; - -import com.alibaba.nacos.config.server.model.SampleResult; -import com.alibaba.nacos.config.server.utils.GroupKey; -import com.alibaba.nacos.config.server.utils.LogUtil; -import com.alibaba.nacos.config.server.utils.MD5Util; -import com.alibaba.nacos.config.server.utils.RequestUtil; -import com.alibaba.nacos.config.server.utils.event.EventDispatcher.AbstractEventListener; -import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event; - -/** - * 长轮询服务。负责处理 - * @author Nacos - */ -@Service -public class LongPullingService extends AbstractEventListener { - - private static final int FIXED_POLLING_INTERVAL_MS = 10000; - - private static final int SAMPLE_PERIOD = 100; - - private static final int SAMPLE_TIMES = 3; - - private static final String TRUE_STR = "true"; - - private Map retainIps = new ConcurrentHashMap(); - - private static boolean isFixedPolling() { - return SwitchService.getSwitchBoolean(SwitchService.FIXED_POLLING, false); - } - - private static int getFixedPollingInterval() { - return SwitchService.getSwitchInteger(SwitchService.FIXED_POLLING_INTERVAL, FIXED_POLLING_INTERVAL_MS); - } - - - public boolean isClientLongPolling(String clientIp) { - return getClientPollingRecord(clientIp) != null; - } - - public Map getClientSubConfigInfo(String clientIp) { - ClientLongPulling record = getClientPollingRecord(clientIp); - - if (record == null) { - return Collections.emptyMap(); - } - - return record.clientMd5Map; - } - - public SampleResult getSubscribleInfo(String dataId, String group, String tenant) { - String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); - SampleResult sampleResult = new SampleResult(); - Map lisentersGroupkeyStatus = new HashMap(50); - - for (ClientLongPulling clientLongPulling : allSubs) { - if (clientLongPulling.clientMd5Map.containsKey(groupKey)) { - lisentersGroupkeyStatus.put(clientLongPulling.ip, clientLongPulling.clientMd5Map.get(groupKey)); - } - } - sampleResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); - return sampleResult; - } - - public SampleResult getSubscribleInfoByIp(String clientIp) { - SampleResult sampleResult = new SampleResult(); - Map lisentersGroupkeyStatus = new HashMap(50); - - for (ClientLongPulling clientLongPulling : allSubs) { - if (clientLongPulling.ip.equals(clientIp)) { - // 一个ip可能有多个监听 - if (!lisentersGroupkeyStatus.equals(clientLongPulling.clientMd5Map)) { - lisentersGroupkeyStatus.putAll(clientLongPulling.clientMd5Map); - } - } - } - sampleResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); - return sampleResult; - } - - /** - * 聚合采样结果中的采样ip和监听配置的信息;合并策略用后面的覆盖前面的是没有问题的 - * @param sampleResults sample Results - * @return Results - */ - public SampleResult mergeSampleResult(List sampleResults) { - SampleResult mergeResult = new SampleResult(); - Map lisentersGroupkeyStatus = new HashMap(50); - for (SampleResult sampleResult : sampleResults) { - Map lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus(); - for (Map.Entry entry : lisentersGroupkeyStatusTmp.entrySet()) { - lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue()); - } - } - mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); - return mergeResult; - } - - public Map> collectApplicationSubscribeConfigInfos() { - if (allSubs == null || allSubs.isEmpty()) { - return null; - } - HashMap> app2Groupkeys = new HashMap>(50); - for (ClientLongPulling clientLongPulling : allSubs) { - if(StringUtils.isEmpty(clientLongPulling.appName) || "unknown".equalsIgnoreCase(clientLongPulling.appName)) { - continue; - } - Set appSubscribeConfigs = app2Groupkeys.get(clientLongPulling.appName); - Set clientSubscribeConfigs = clientLongPulling.clientMd5Map.keySet(); - if(appSubscribeConfigs==null) { - appSubscribeConfigs = new HashSet(clientSubscribeConfigs.size()); - } - appSubscribeConfigs.addAll(clientSubscribeConfigs); - app2Groupkeys.put(clientLongPulling.appName, appSubscribeConfigs); - } - - return app2Groupkeys; - } - - - public SampleResult getCollectSubscribleInfo(String dataId, String group, String tenant) { - List sampleResultLst = new ArrayList(50); - for (int i = 0; i < SAMPLE_TIMES; i++) { - SampleResult sampleTmp = getSubscribleInfo(dataId, group, tenant); - if (sampleTmp != null) { - sampleResultLst.add(sampleTmp); - } - if (i < SAMPLE_TIMES - 1) { - try { - Thread.sleep(SAMPLE_PERIOD); - } catch (InterruptedException e) { - LogUtil.clientLog.error("sleep wrong", e); - } - } - } - - SampleResult sampleResult = mergeSampleResult(sampleResultLst); - return sampleResult; - } - - public SampleResult getCollectSubscribleInfoByIp(String ip) { - SampleResult sampleResult = new SampleResult(); - sampleResult.setLisentersGroupkeyStatus(new HashMap(50)); - for (int i = 0; i < SAMPLE_TIMES; i++) { - SampleResult sampleTmp = getSubscribleInfoByIp(ip); - if (sampleTmp != null) { - if (sampleTmp.getLisentersGroupkeyStatus() != null - && !sampleResult.getLisentersGroupkeyStatus().equals(sampleTmp.getLisentersGroupkeyStatus())) { - sampleResult.getLisentersGroupkeyStatus().putAll(sampleTmp.getLisentersGroupkeyStatus()); - } - } - if (i < SAMPLE_TIMES - 1) { - try { - Thread.sleep(SAMPLE_PERIOD); - } catch (InterruptedException e) { - LogUtil.clientLog.error("sleep wrong", e); - } - } - } - return sampleResult; - } - - private ClientLongPulling getClientPollingRecord(String clientIp) { - if (allSubs == null) { - return null; - } - - for (ClientLongPulling clientLongPulling : allSubs) { - HttpServletRequest request = (HttpServletRequest) clientLongPulling.asyncContext.getRequest(); - - if (clientIp.equals(RequestUtil.getRemoteIp(request))) { - return clientLongPulling; - } - } - - return null; - } - - public void addLongPullingClient(HttpServletRequest req, HttpServletResponse rsp, Map clientMd5Map, int probeRequestSize) { - - String str = req.getHeader(LongPullingService.LONG_PULLING_HEADER); - String noHangUpFlag = req.getHeader(LongPullingService.LONG_PULLING_NO_HANG_UP_HEADER); - String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); - String tag = req.getHeader("Vipserver-Tag"); - int delayTime=SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); - /** - * 提前500ms返回响应,为避免客户端超时 @qiaoyi.dingqy 2013.10.22改动 add delay time for LoadBalance - */ - long timeout = Math.max(10000, Long.parseLong(str) - delayTime); - if (isFixedPolling()) { - timeout = Math.max(10000, getFixedPollingInterval()); - // do nothing but set fix polling timeout - } else { - long start = System.currentTimeMillis(); - List changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map); - if (changedGroups.size() > 0) { - generateResponse(req, rsp, changedGroups); - LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", - System.currentTimeMillis() - start, "instant", RequestUtil.getRemoteIp(req), "polling", - clientMd5Map.size(), probeRequestSize, changedGroups.size()); - return; - } else if(noHangUpFlag!=null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) { - LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "nohangup", - RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize, - changedGroups.size()); - return; - } - } - String ip = RequestUtil.getRemoteIp(req); - // 一定要由HTTP线程调用,否则离开后容器会立即发送响应 - final AsyncContext asyncContext = req.startAsync(); - // AsyncContext.setTimeout()的超时时间不准,所以只能自己控制 - asyncContext.setTimeout(0L); - - - scheduler.execute( - new ClientLongPulling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag)); - } - - @Override - public List> interest() { - List> eventTypes = new ArrayList>(); - eventTypes.add(LocalDataChangeEvent.class); - return eventTypes; - } - - @Override - public void onEvent(Event event) { - if (isFixedPolling()) { - // ignore - } else { - if (event instanceof LocalDataChangeEvent) { - LocalDataChangeEvent evt = (LocalDataChangeEvent) event; - scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps)); - } - } - } - - static public boolean isSupportLongPulling(HttpServletRequest req) { - return null != req.getHeader(LONG_PULLING_HEADER); - } - - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public LongPullingService() { - allSubs = new ConcurrentLinkedQueue(); - - scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("com.alibaba.nacos.LongPulling"); - return t; - } - }); - scheduler.scheduleWithFixedDelay(new StatTask(), 0L, 10L, TimeUnit.SECONDS); - } - - // ================= - - static public final String LONG_PULLING_HEADER = "Long-Pulling-Timeout"; - static public final String LONG_PULLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup"; - - final ScheduledExecutorService scheduler; - - /** - * 长轮询订阅关系 - */ - final Queue allSubs; - - // ================= - - class DataChangeTask implements Runnable { - @Override - public void run() { - try { - ConfigService.getContentBetaMd5(groupKey); - for (Iterator iter = allSubs.iterator(); iter.hasNext(); ) { - ClientLongPulling clientSub = iter.next(); - if (clientSub.clientMd5Map.containsKey(groupKey)) { - // 如果beta发布且不在beta列表直接跳过 - if (isBeta && !betaIps.contains(clientSub.ip)) { - continue; - } - - // 如果tag发布且不在tag列表直接跳过 - if (StringUtils.isNotBlank(tag) && !tag.equals(clientSub.tag)) { - continue; - } - - getRetainIps().put(clientSub.ip, System.currentTimeMillis()); - iter.remove(); // 删除订阅关系 - LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", - (System.currentTimeMillis() - changeTime), - "in-advance", RequestUtil.getRemoteIp((HttpServletRequest) clientSub.asyncContext.getRequest()), - "polling", - clientSub.clientMd5Map.size(), clientSub.probeRequestSize, groupKey); - clientSub.sendResponse(Arrays.asList(groupKey)); - } - } - } catch (Throwable t) { - LogUtil.defaultLog.error("data change error:" + t.getMessage(), t.getCause()); - } - } - - DataChangeTask(String groupKey) { - this(groupKey, false, null); - } - - DataChangeTask(String groupKey, boolean isBeta, List betaIps) { - this(groupKey, isBeta, betaIps, null); - } - - DataChangeTask(String groupKey, boolean isBeta, List betaIps, String tag) { - this.groupKey = groupKey; - this.isBeta = isBeta; - this.betaIps = betaIps; - this.tag = tag; - } - - final String groupKey; - final long changeTime = System.currentTimeMillis(); - final boolean isBeta; - final List betaIps; - final String tag; - } - - // ================= - - class StatTask implements Runnable { - @Override - public void run() { - memoryLog.info("[long-pulling] client count " + allSubs.size()); - } - } - - // ================= - - class ClientLongPulling implements Runnable { - - @Override - public void run() { - asyncTimeoutFuture = scheduler.schedule(new Runnable() { - public void run() { - try { - getRetainIps().put(ClientLongPulling.this.ip, System.currentTimeMillis()); - /** - * 删除订阅关系 - */ - allSubs.remove(ClientLongPulling.this); - - if(isFixedPolling()) { - LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}", - (System.currentTimeMillis() - createTime), - "fix", RequestUtil.getRemoteIp((HttpServletRequest) asyncContext.getRequest()), - "polling", - clientMd5Map.size(), probeRequestSize); - List changedGroups = MD5Util.compareMd5((HttpServletRequest) asyncContext.getRequest(), (HttpServletResponse) asyncContext.getResponse(), clientMd5Map); - if (changedGroups.size() > 0) { - sendResponse(changedGroups); - } else { - sendResponse(null); - } - } else { - LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}", - (System.currentTimeMillis() - createTime), - "timeout", RequestUtil.getRemoteIp((HttpServletRequest) asyncContext.getRequest()), - "polling", - clientMd5Map.size(), probeRequestSize); - sendResponse(null); - } - } catch (Throwable t) { - LogUtil.defaultLog.error("long pulling error:" + t.getMessage(), t.getCause()); - } - - } - }, timeoutTime, TimeUnit.MILLISECONDS); - - allSubs.add(this); - } - - void sendResponse(List changedGroups) { - /** - * 取消超时任务 - */ - if (null != asyncTimeoutFuture) { - asyncTimeoutFuture.cancel(false); - } - generateResponse(changedGroups); - } - - void generateResponse(List changedGroups) { - if (null == changedGroups) { - /** - * 告诉容器发送HTTP响应 - */ - asyncContext.complete(); - return; - } - - HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse(); - - try { - String respString = MD5Util.compareMd5ResultString(changedGroups); - - // 禁用缓存 - response.setHeader("Pragma", "no-cache"); - response.setDateHeader("Expires", 0); - response.setHeader("Cache-Control", "no-cache,no-store"); - response.setStatus(HttpServletResponse.SC_OK); - response.getWriter().println(respString); - asyncContext.complete(); - } catch (Exception se) { - pullLog.error(se.toString(), se); - asyncContext.complete(); - } - } - - ClientLongPulling(AsyncContext ac, Map clientMd5Map, String ip, int probeRequestSize, - long timeoutTime, String appName, String tag) { - this.asyncContext = ac; - this.clientMd5Map = clientMd5Map; - this.probeRequestSize = probeRequestSize; - this.createTime = System.currentTimeMillis(); - this.ip = ip; - this.timeoutTime = timeoutTime; - this.appName = appName; - this.tag = tag; - } - - // ================= - - final AsyncContext asyncContext; - final Map clientMd5Map; - final long createTime; - final String ip; - final String appName; - final String tag; - final int probeRequestSize; - final long timeoutTime; - - Future asyncTimeoutFuture; - } - - void generateResponse(HttpServletRequest request, HttpServletResponse response, List changedGroups) { - if (null == changedGroups) { - return; - } - - try { - String respString = MD5Util.compareMd5ResultString(changedGroups); - // 禁用缓存 - response.setHeader("Pragma", "no-cache"); - response.setDateHeader("Expires", 0); - response.setHeader("Cache-Control", "no-cache,no-store"); - response.setStatus(HttpServletResponse.SC_OK); - response.getWriter().println(respString); - } catch (Exception se) { - pullLog.error(se.toString(), se); - } - } - - public Map getRetainIps() { - return retainIps; - } - - public void setRetainIps(Map retainIps) { - this.retainIps = retainIps; - } - - -} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java index cb7230e8f..01a348e6b 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java @@ -53,7 +53,6 @@ import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.CollectionUtils; - import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; @@ -66,6 +65,7 @@ import com.alibaba.nacos.config.server.model.ConfigInfoChanged; import com.alibaba.nacos.config.server.model.ConfigKey; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.model.SubInfo; +import com.alibaba.nacos.config.server.model.TenantInfo; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5; import com.alibaba.nacos.config.server.utils.PaginationHelper; @@ -75,10 +75,8 @@ import com.google.common.collect.Lists; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** - * 数据库服务,提供ConfigInfo在数据库的存取
- * 3.0开始增加数据版本号, 并将物理删除改为逻辑删除
- * 3.0增加数据库切换功能 - * + * 数据库服务,提供ConfigInfo在数据库的存取
3.0开始增加数据版本号, 并将物理删除改为逻辑删除
3.0增加数据库切换功能 + * * @author boyan * @author leiwen.zh * @since 1.0 @@ -87,1757 +85,1801 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @Repository public class PersistService { - @Autowired - private DynamicDataSource dynamicDataSource; - - private DataSourceService dataSourceService; + @Autowired + private DynamicDataSource dynamicDataSource; - @PostConstruct - public void init() { - dataSourceService = dynamicDataSource.getDataSource(); + private DataSourceService dataSourceService; - jt = getJdbcTemplate(); - tjt = getTransactionTemplate(); - } + @PostConstruct + public void init() { + dataSourceService = dynamicDataSource.getDataSource(); - - public boolean checkMasterWritable() { - return dataSourceService.checkMasterWritable(); - } + jt = getJdbcTemplate(); + tjt = getTransactionTemplate(); + } - public void setBasicDataSourceService(DataSourceService dataSourceService) { - this.dataSourceService = dataSourceService; - } - - static final class ConfigInfoWrapperRowMapper implements - RowMapper { - public ConfigInfoWrapper mapRow(ResultSet rs, int rowNum) - throws SQLException { - ConfigInfoWrapper info = new ConfigInfoWrapper(); + public boolean checkMasterWritable() { + return dataSourceService.checkMasterWritable(); + } - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setAppName(rs.getString("app_name")); + public void setBasicDataSourceService(DataSourceService dataSourceService) { + this.dataSourceService = dataSourceService; + } + + static final class ConfigInfoWrapperRowMapper implements + RowMapper { + public ConfigInfoWrapper mapRow(ResultSet rs, int rowNum) + throws SQLException { + ConfigInfoWrapper info = new ConfigInfoWrapper(); + + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setAppName(rs.getString("app_name")); - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - try { - info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); - } catch (SQLException e) { - // ignore - } try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - } - return info; - } - } - - static final class ConfigInfoBetaWrapperRowMapper implements - RowMapper { - public ConfigInfoBetaWrapper mapRow(ResultSet rs, int rowNum) - throws SQLException { - ConfigInfoBetaWrapper info = new ConfigInfoBetaWrapper(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setAppName(rs.getString("app_name")); - info.setBetaIps(rs.getString("beta_ips")); - - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - try { - info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); - } catch (SQLException e) { - // ignore - } - try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - } - return info; - } - } - - static final class ConfigInfoTagWrapperRowMapper implements - RowMapper { - public ConfigInfoTagWrapper mapRow(ResultSet rs, int rowNum) - throws SQLException { - ConfigInfoTagWrapper info = new ConfigInfoTagWrapper(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setTag(rs.getString("tag_id")); - info.setAppName(rs.getString("app_name")); - - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - try { - info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); - } catch (SQLException e) { - // ignore - } - try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - } - return info; - } - } - - static final class ConfigInfoRowMapper implements - RowMapper { - public ConfigInfo mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigInfo info = new ConfigInfo(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setAppName(rs.getString("app_name")); - - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - return info; - } - } - - static final class ConfigKeyRowMapper implements - RowMapper { - public ConfigKey mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigKey info = new ConfigKey(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setAppName(rs.getString("app_name")); - - return info; - } - } - - static final class ConfigAdvanceInfoRowMapper implements RowMapper { - public ConfigAdvanceInfo mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigAdvanceInfo info = new ConfigAdvanceInfo(); - info.setCreateTime(rs.getTimestamp("gmt_modified").getTime()); - info.setModifyTime(rs.getTimestamp("gmt_modified").getTime()); - info.setCreateUser(rs.getString("src_user")); - info.setCreateIp(rs.getString("src_ip")); - info.setDesc(rs.getString("c_desc")); - info.setUse(rs.getString("c_use")); - info.setEffect(rs.getString("effect")); - info.setType(rs.getString("type")); - info.setSchema(rs.getString("c_schema")); - return info; - } - } - - static final class ConfigAllInfoRowMapper implements RowMapper { - public ConfigAllInfo mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigAllInfo info = new ConfigAllInfo(); - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setAppName(rs.getString("app_name")); - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - info.setCreateTime(rs.getTimestamp("gmt_modified").getTime()); - info.setModifyTime(rs.getTimestamp("gmt_modified").getTime()); - info.setCreateUser(rs.getString("src_user")); - info.setCreateIp(rs.getString("src_ip")); - info.setDesc(rs.getString("c_desc")); - info.setUse(rs.getString("c_use")); - info.setEffect(rs.getString("effect")); - info.setType(rs.getString("type")); - info.setSchema(rs.getString("c_schema")); - return info; - } - } - - static final class ConfigInfo4BetaRowMapper implements - RowMapper { - public ConfigInfo4Beta mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigInfo4Beta info = new ConfigInfo4Beta(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setAppName(rs.getString("app_name")); - info.setBetaIps(rs.getString("beta_ips")); - - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - } - return info; - } - } - - static final class ConfigInfo4TagRowMapper implements - RowMapper { - public ConfigInfo4Tag mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigInfo4Tag info = new ConfigInfo4Tag(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - info.setTag(rs.getString("tag_id")); - info.setAppName(rs.getString("app_name")); - - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - try { - info.setMd5(rs.getString("md5")); - } catch (SQLException e) { - } - return info; - } - } - - static final class ConfigInfoBaseRowMapper implements - RowMapper { - public ConfigInfoBase mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigInfoBase info = new ConfigInfoBase(); - - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - - try { - info.setContent(rs.getString("content")); - } catch (SQLException e) { - // ignore - } - try { - info.setId(rs.getLong("ID")); - } catch (SQLException e) { - // ignore - } - return info; - } - } + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + try { + info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + } + return info; + } + } - static final class ConfigInfoAggrRowMapper implements - RowMapper { - public ConfigInfoAggr mapRow(ResultSet rs, int rowNum) - throws SQLException { - ConfigInfoAggr info = new ConfigInfoAggr(); - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setDatumId(rs.getString("datum_id")); - info.setTenant(rs.getString("tenant_id")); - info.setAppName(rs.getString("app_name")); - info.setContent(rs.getString("content")); - return info; - } - } + static final class ConfigInfoBetaWrapperRowMapper implements + RowMapper { + public ConfigInfoBetaWrapper mapRow(ResultSet rs, int rowNum) + throws SQLException { + ConfigInfoBetaWrapper info = new ConfigInfoBetaWrapper(); - static final class ConfigInfoChangedRowMapper implements RowMapper { - public ConfigInfoChanged mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigInfoChanged info = new ConfigInfoChanged(); - info.setDataId(rs.getString("data_id")); - info.setGroup(rs.getString("group_id")); - info.setTenant(rs.getString("tenant_id")); - return info; - } - } + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setAppName(rs.getString("app_name")); + info.setBetaIps(rs.getString("beta_ips")); - static final class ConfigHistoryRowMapper implements RowMapper { - public ConfigHistoryInfo mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); - configHistoryInfo.setId(rs.getLong("nid")); - configHistoryInfo.setDataId(rs.getString("data_id")); - configHistoryInfo.setGroup(rs.getString("group_id")); - configHistoryInfo.setTenant(rs.getString("tenant_id")); - configHistoryInfo.setAppName(rs.getString("app_name")); - configHistoryInfo.setSrcIp(rs.getString("src_ip")); - configHistoryInfo.setOpType(rs.getString("op_type")); - configHistoryInfo.setCreatedTime(rs.getTimestamp("gmt_create")); - configHistoryInfo.setLastModifiedTime(rs.getTimestamp("gmt_modified")); - return configHistoryInfo; - } - } + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + try { + info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + } + return info; + } + } - static final class ConfigHistoryDetailRowMapper implements RowMapper { - public ConfigHistoryInfo mapRow(ResultSet rs, int rowNum) throws SQLException { - ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); - configHistoryInfo.setId(rs.getLong("nid")); - configHistoryInfo.setDataId(rs.getString("data_id")); - configHistoryInfo.setGroup(rs.getString("group_id")); - configHistoryInfo.setTenant(rs.getString("tenant_id")); - configHistoryInfo.setAppName(rs.getString("app_name")); - configHistoryInfo.setMd5(rs.getString("md5")); - configHistoryInfo.setContent(rs.getString("content")); - configHistoryInfo.setSrcUser(rs.getString("src_user")); - configHistoryInfo.setSrcIp(rs.getString("src_ip")); - configHistoryInfo.setOpType(rs.getString("op_type")); - configHistoryInfo.setCreatedTime(rs.getTimestamp("gmt_create")); - configHistoryInfo.setLastModifiedTime(rs.getTimestamp("gmt_modified")); - return configHistoryInfo; - } - }; - + static final class ConfigInfoTagWrapperRowMapper implements + RowMapper { + public ConfigInfoTagWrapper mapRow(ResultSet rs, int rowNum) + throws SQLException { + ConfigInfoTagWrapper info = new ConfigInfoTagWrapper(); - public synchronized void reload() throws IOException { - this.dataSourceService.reload(); - } - - /** - * 单元测试用 - */ - public JdbcTemplate getJdbcTemplate() { - return this.dataSourceService.getJdbcTemplate(); - } - - public TransactionTemplate getTransactionTemplate() { - return this.dataSourceService.getTransactionTemplate(); - } + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setTag(rs.getString("tag_id")); + info.setAppName(rs.getString("app_name")); - public String getCurrentDBUrl() { - return this.dataSourceService.getCurrentDBUrl(); - } + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + try { + info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + } + return info; + } + } - // ----------------------- config_info 表 insert update delete - /** - * 添加普通配置信息,发布数据变更事件 - */ - public void addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, final Timestamp time, final Map configAdvanceInfo, final boolean notify) { - tjt.execute(new TransactionCallback() { - @Override - public Boolean doInTransaction(TransactionStatus status) { - try { - long configId = addConfigInfoAtomic(srcIp, srcUser, configInfo, time, configAdvanceInfo); - String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - addConfiTagsRelationAtomic(configId, configTags, configInfo.getDataId(), configInfo.getGroup(), - configInfo.getTenant()); - insertConfigHistoryAtomic(0, configInfo, srcIp, srcUser, time, "I"); - if (notify) { - EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), time.getTime())); - } - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - return Boolean.TRUE; - } - }); - } - - /** - * 添加普通配置信息,发布数据变更事件 - */ - public void addConfigInfo4Beta(ConfigInfo configInfo, String betaIps, - String srcIp, String srcUser, Timestamp time, boolean notify) { - String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - try { - String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); - jt.update( - "insert into config_info_beta(data_id,group_id,tenant_id,app_name,content,md5,beta_ips,src_ip,src_user,gmt_create,gmt_modified) values(?,?,?,?,?,?,?,?,?,?,?)", - configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5, - betaIps, srcIp, srcUser, time, time); - if (notify) { - EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, configInfo.getDataId(), configInfo.getGroup(), - tenantTmp, time.getTime())); - } + static final class ConfigInfoRowMapper implements + RowMapper { + public ConfigInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigInfo info = new ConfigInfo(); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setAppName(rs.getString("app_name")); - /** - * 添加普通配置信息,发布数据变更事件 - */ - public void addConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser, Timestamp time, - boolean notify) { - String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); - try { - String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); - jt.update( - "insert into config_info_tag(data_id,group_id,tenant_id,tag_id,app_name,content,md5,src_ip,src_user,gmt_create,gmt_modified) values(?,?,?,?,?,?,?,?,?,?,?)", - configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, appNameTmp, configInfo.getContent(), md5, - srcIp, srcUser, time, time); - if (notify) { - EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), - configInfo.getGroup(), tenantTmp, tagTmp, time.getTime())); - } - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + return info; + } + } - /** - * 更新配置信息 - */ - public void updateConfigInfo(final ConfigInfo configInfo, final String srcIp, final String srcUser, - final Timestamp time, final Map configAdvanceInfo, final boolean notify) { - tjt.execute(new TransactionCallback() { - @Override - public Boolean doInTransaction(TransactionStatus status) { - try { - ConfigInfo oldConfigInfo = findConfigInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); - String appNameTmp = oldConfigInfo.getAppName(); - // 用户传过来的appName不为空,则用持久化用户的appName,否则用db的;清空appName的时候需要传空串 - if (configInfo.getAppName() == null) { - configInfo.setAppName(appNameTmp); - } - updateConfigInfoAtomic(configInfo, srcIp, srcUser, time, configAdvanceInfo); - String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); - if (configTags != null) { - // 删除所有tag,然后再重新创建 - removeTagByIdAtomic(oldConfigInfo.getId()); - addConfiTagsRelationAtomic(oldConfigInfo.getId(), configTags, configInfo.getDataId(), - configInfo.getGroup(), configInfo.getTenant()); - } - insertConfigHistoryAtomic(oldConfigInfo.getId(), oldConfigInfo, srcIp, srcUser, time, "U"); - if (notify) { - EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), - configInfo.getGroup(), configInfo.getTenant(), time.getTime())); - } - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - return Boolean.TRUE; - } - }); - } - - /** - * 更新配置信息 - */ - public void updateConfigInfo4Beta(ConfigInfo configInfo, String srcIp, String srcUser, Timestamp time, - boolean notify) { - String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - try { - String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); - jt.update( - "update config_info_beta set content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? where data_id=? and group_id=? and tenant_id=?", - configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), - configInfo.getGroup(), tenantTmp); - if (notify) { - EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, configInfo.getDataId(), configInfo.getGroup(), - tenantTmp, time.getTime())); - } + static final class ConfigKeyRowMapper implements + RowMapper { + public ConfigKey mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigKey info = new ConfigKey(); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 更新配置信息 - */ - public void updateConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser, Timestamp time, - boolean notify) { - String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); - try { - String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); - jt.update( - "update config_info_tag set content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? where data_id=? and group_id=? and tenant_id=? and tag_id=?", - configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), - configInfo.getGroup(), tenantTmp, tagTmp); - if (notify) { - EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, configInfo.getDataId(), configInfo.getGroup(), - tenantTmp, tagTmp, time.getTime())); - } + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setAppName(rs.getString("app_name")); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public void insertOrUpdateBeta(final ConfigInfo configInfo, final String betaIps, final String srcIp, - final String srcUser, final Timestamp time, final boolean notify) { - try { - addConfigInfo4Beta(configInfo, betaIps, srcIp, null, time, notify); - } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 - updateConfigInfo4Beta(configInfo, srcIp, null, time, notify); - } - } - - public void insertOrUpdateTag(final ConfigInfo configInfo, final String tag, final String srcIp, - final String srcUser, final Timestamp time, final boolean notify) { - try { - addConfigInfo4Tag(configInfo, tag, srcIp, null, time, notify); - } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 - updateConfigInfo4Tag(configInfo, tag, srcIp, null, time, notify); - } - } + return info; + } + } - /** - * 更新md5 - */ - public void updateMd5(String dataId, String group, String tenant, String md5, Timestamp lastTime) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - jt.update( - "update config_info set md5 = ? where data_id=? and group_id=? and tenant_id=? and gmt_modified=?", - md5, dataId, group, tenantTmp, lastTime); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + static final class ConfigAdvanceInfoRowMapper implements RowMapper { + public ConfigAdvanceInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigAdvanceInfo info = new ConfigAdvanceInfo(); + info.setCreateTime(rs.getTimestamp("gmt_modified").getTime()); + info.setModifyTime(rs.getTimestamp("gmt_modified").getTime()); + info.setCreateUser(rs.getString("src_user")); + info.setCreateIp(rs.getString("src_ip")); + info.setDesc(rs.getString("c_desc")); + info.setUse(rs.getString("c_use")); + info.setEffect(rs.getString("effect")); + info.setType(rs.getString("type")); + info.setSchema(rs.getString("c_schema")); + return info; + } + } - public void insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Timestamp time, Map configAdvanceInfo) { - insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, true); - } + static final class ConfigAllInfoRowMapper implements RowMapper { + public ConfigAllInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigAllInfo info = new ConfigAllInfo(); + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setAppName(rs.getString("app_name")); + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + info.setCreateTime(rs.getTimestamp("gmt_modified").getTime()); + info.setModifyTime(rs.getTimestamp("gmt_modified").getTime()); + info.setCreateUser(rs.getString("src_user")); + info.setCreateIp(rs.getString("src_ip")); + info.setDesc(rs.getString("c_desc")); + info.setUse(rs.getString("c_use")); + info.setEffect(rs.getString("effect")); + info.setType(rs.getString("type")); + info.setSchema(rs.getString("c_schema")); + return info; + } + } - /** - * 写入主表,插入或更新 - */ - public void insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Timestamp time, Map configAdvanceInfo, boolean notify) { - try { - addConfigInfo(srcIp, srcUser, configInfo, time, configAdvanceInfo, notify); - } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 - updateConfigInfo(configInfo, srcIp, srcUser, time, configAdvanceInfo, notify); - } - } - - /** - * 写入主表,插入或更新 - */ - public void insertOrUpdateSub(SubInfo subInfo) { - try { - addConfigSubAtomic(subInfo.getDataId(), subInfo.getGroup(), subInfo.getAppName(), subInfo.getDate()); - } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 - updateConfigSubAtomic(subInfo.getDataId(), subInfo.getGroup(), subInfo.getAppName(), subInfo.getDate()); - } - } + static final class ConfigInfo4BetaRowMapper implements + RowMapper { + public ConfigInfo4Beta mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigInfo4Beta info = new ConfigInfo4Beta(); - /** - * 删除配置信息, 物理删除 - */ - public void removeConfigInfo(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser) { tjt.execute(new TransactionCallback() { - final Timestamp time = new Timestamp(System.currentTimeMillis()); - @Override - public Boolean doInTransaction(TransactionStatus status) { - try { - ConfigInfo configInfo = findConfigInfo(dataId, group, tenant); - if (configInfo != null) { - removeConfigInfoAtomic(dataId, group, tenant, srcIp, srcUser); - removeTagByIdAtomic(configInfo.getId()); - insertConfigHistoryAtomic(configInfo.getId(), configInfo, srcIp, srcUser, time, "D"); - } - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - return Boolean.TRUE; - } - }); - } + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setAppName(rs.getString("app_name")); + info.setBetaIps(rs.getString("beta_ips")); - /** - * 删除beta配置信息, 物理删除 - */ - public void removeConfigInfo4Beta(final String dataId, final String group, final String tenant) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - tjt.execute(new TransactionCallback() { - @Override - public Boolean doInTransaction(TransactionStatus status) { - try { - ConfigInfo configInfo = findConfigInfo4Beta(dataId, group, tenant); - if (configInfo != null) { - jt.update("delete from config_info_beta where data_id=? and group_id=? and tenant_id=?", dataId, - group, tenantTmp); - } - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - return Boolean.TRUE; - } - }); - } + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + } + return info; + } + } - // ----------------------- config_aggr_info 表 insert update delete - /** - * 增加聚合前数据到数据库, select -> update or insert - */ - public boolean addAggrConfigInfo(final String dataId, final String group, String tenant, final String datumId, - String appName, final String content) { - String appNameTmp = StringUtils.isBlank(appName) ? StringUtils.EMPTY : appName; - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final Timestamp now = new Timestamp(System.currentTimeMillis()); - String select = "select content from config_info_aggr where data_id = ? and group_id = ? and tenant_id = ? and datum_id = ?"; - String insert = "insert into config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, content, gmt_modified) values(?,?,?,?,?,?,?) "; - String update = "update config_info_aggr set content = ? , gmt_modified = ? where data_id = ? and group_id = ? and tenant_id = ? and datum_id = ?"; + static final class ConfigInfo4TagRowMapper implements + RowMapper { + public ConfigInfo4Tag mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigInfo4Tag info = new ConfigInfo4Tag(); - try { - try { - String dbContent = jt.queryForObject(select, new Object[] { dataId, group, tenantTmp, datumId }, - String.class); + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + info.setTag(rs.getString("tag_id")); + info.setAppName(rs.getString("app_name")); - if (dbContent != null && dbContent.equals(content)) { - return true; - } else { - return jt.update(update, content, now, dataId, group, tenantTmp, datumId) > 0; - } - } catch (EmptyResultDataAccessException ex) { // no data, insert - return jt.update(insert, dataId, group, tenantTmp, datumId, appNameTmp, content, now) > 0; - } - } catch (DataAccessException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + try { + info.setMd5(rs.getString("md5")); + } catch (SQLException e) { + } + return info; + } + } - /** - * 删除单条聚合前数据 - */ - public void removeSingleAggrConfigInfo(final String dataId, - final String group, final String tenant, final String datumId) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "delete from config_info_aggr where data_id=? and group_id=? and tenant_id=? and datum_id=?"; + static final class ConfigInfoBaseRowMapper implements + RowMapper { + public ConfigInfoBase mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigInfoBase info = new ConfigInfoBase(); - try { - this.jt.update(sql, new PreparedStatementSetter() { - public void setValues(PreparedStatement ps) throws SQLException { - int index = 1; - ps.setString(index++, dataId); - ps.setString(index++, group); - ps.setString(index++, tenantTmp); - ps.setString(index++, datumId); - } - }); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); - /** - * 删除一个dataId下面所有的聚合前数据 - */ - public void removeAggrConfigInfo(final String dataId, final String group, final String tenant) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "delete from config_info_aggr where data_id=? and group_id=? and tenant_id=?"; + try { + info.setContent(rs.getString("content")); + } catch (SQLException e) { + // ignore + } + try { + info.setId(rs.getLong("ID")); + } catch (SQLException e) { + // ignore + } + return info; + } + } - try { - this.jt.update(sql, new PreparedStatementSetter() { - public void setValues(PreparedStatement ps) throws SQLException { - int index = 1; - ps.setString(index++, dataId); - ps.setString(index++, group); - ps.setString(index++, tenantTmp); - } - }); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + static final class ConfigInfoAggrRowMapper implements + RowMapper { + public ConfigInfoAggr mapRow(ResultSet rs, int rowNum) + throws SQLException { + ConfigInfoAggr info = new ConfigInfoAggr(); + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setDatumId(rs.getString("datum_id")); + info.setTenant(rs.getString("tenant_id")); + info.setAppName(rs.getString("app_name")); + info.setContent(rs.getString("content")); + return info; + } + } - /** - * 批量删除聚合数据,需要指定datum的列表 - * - * @param dataId - * @param group - * @param datumList - */ - public boolean batchRemoveAggr(final String dataId, final String group, final String tenant, - final List datumList) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final StringBuilder datumString = new StringBuilder(); - for (String datum : datumList) { - datumString.append("'").append(datum).append("',"); - } - datumString.deleteCharAt(datumString.length() - 1); - final String sql = "delete from config_info_aggr where data_id=? and group_id=? and tenant_id=? and datum_id in (" - + datumString.toString() + ")"; - try { - jt.update(sql, dataId, group, tenantTmp); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - return false; - } - return true; - } + static final class ConfigInfoChangedRowMapper implements RowMapper { + public ConfigInfoChanged mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigInfoChanged info = new ConfigInfoChanged(); + info.setDataId(rs.getString("data_id")); + info.setGroup(rs.getString("group_id")); + info.setTenant(rs.getString("tenant_id")); + return info; + } + } - /** - * 删除startTime前的数据 - */ - public void removeConfigHistory(final Timestamp startTime, final int limitSize) { - String sql = "delete from his_config_info where gmt_modified < ? limit ?"; - PaginationHelper helper = new PaginationHelper(); + static final class ConfigHistoryRowMapper implements RowMapper { + public ConfigHistoryInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setId(rs.getLong("nid")); + configHistoryInfo.setDataId(rs.getString("data_id")); + configHistoryInfo.setGroup(rs.getString("group_id")); + configHistoryInfo.setTenant(rs.getString("tenant_id")); + configHistoryInfo.setAppName(rs.getString("app_name")); + configHistoryInfo.setSrcIp(rs.getString("src_ip")); + configHistoryInfo.setOpType(rs.getString("op_type")); + configHistoryInfo.setCreatedTime(rs.getTimestamp("gmt_create")); + configHistoryInfo.setLastModifiedTime(rs.getTimestamp("gmt_modified")); + return configHistoryInfo; + } + } + + static final class ConfigHistoryDetailRowMapper implements RowMapper { + public ConfigHistoryInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); + configHistoryInfo.setId(rs.getLong("nid")); + configHistoryInfo.setDataId(rs.getString("data_id")); + configHistoryInfo.setGroup(rs.getString("group_id")); + configHistoryInfo.setTenant(rs.getString("tenant_id")); + configHistoryInfo.setAppName(rs.getString("app_name")); + configHistoryInfo.setMd5(rs.getString("md5")); + configHistoryInfo.setContent(rs.getString("content")); + configHistoryInfo.setSrcUser(rs.getString("src_user")); + configHistoryInfo.setSrcIp(rs.getString("src_ip")); + configHistoryInfo.setOpType(rs.getString("op_type")); + configHistoryInfo.setCreatedTime(rs.getTimestamp("gmt_create")); + configHistoryInfo.setLastModifiedTime(rs.getTimestamp("gmt_modified")); + return configHistoryInfo; + } + } + + ; + + static final class TenantInfoRowMapper implements RowMapper { + public TenantInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + TenantInfo info = new TenantInfo(); + info.setTenantId(rs.getString("tenant_id")); + info.setTenantName(rs.getString("tenant_name")); + info.setTenantDesc(rs.getString("tenant_desc")); + return info; + } + } + + public synchronized void reload() throws IOException { + this.dataSourceService.reload(); + } + + /** + * 单元测试用 + */ + public JdbcTemplate getJdbcTemplate() { + return this.dataSourceService.getJdbcTemplate(); + } + + public TransactionTemplate getTransactionTemplate() { + return this.dataSourceService.getTransactionTemplate(); + } + + public String getCurrentDBUrl() { + return this.dataSourceService.getCurrentDBUrl(); + } + + // ----------------------- config_info 表 insert update delete + + /** + * 添加普通配置信息,发布数据变更事件 + */ + public void addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, + final Timestamp time, final Map configAdvanceInfo, final boolean notify) { + tjt.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + try { + long configId = addConfigInfoAtomic(srcIp, srcUser, configInfo, time, configAdvanceInfo); + String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + addConfiTagsRelationAtomic(configId, configTags, configInfo.getDataId(), configInfo.getGroup(), + configInfo.getTenant()); + insertConfigHistoryAtomic(0, configInfo, srcIp, srcUser, time, "I"); + if (notify) { + EventDispatcher.fireEvent( + new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(), + configInfo.getTenant(), time.getTime())); + } + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + return Boolean.TRUE; + } + }); + } + + /** + * 添加普通配置信息,发布数据变更事件 + */ + public void addConfigInfo4Beta(ConfigInfo configInfo, String betaIps, + String srcIp, String srcUser, Timestamp time, boolean notify) { + String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); + String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); try { - helper.updateLimit(jt, sql, new Object[]{startTime, limitSize}); + String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); + jt.update( + "INSERT INTO config_info_beta(data_id,group_id,tenant_id,app_name,content,md5,beta_ips,src_ip," + + "src_user,gmt_create,gmt_modified) VALUES(?,?,?,?,?,?,?,?,?,?,?)", + configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5, + betaIps, srcIp, srcUser, time, time); + if (notify) { + EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, configInfo.getDataId(), configInfo.getGroup(), + tenantTmp, time.getTime())); + } + } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); throw e; } } - - /** - * 获取指定时间前配置条数 - */ - public int findConfigHistoryCountByTime(final Timestamp startTime) { - String sql = "select COUNT(*) from his_config_info where gmt_modified < ?"; - Integer result = jt.queryForObject(sql, Integer.class, new Object[] { startTime }); - if (result == null) { - throw new IllegalArgumentException("configInfoBetaCount error"); - } - return result.intValue(); - } - - /** - * 获取最大maxId - */ - public long findConfigMaxId() { - String sql = "select max(id) from config_info"; - try { - return jt.queryForObject(sql, Integer.class); - } catch (NullPointerException e) { - return 0; - } - } - /** - * 批量添加或者更新数据.事务过程中出现任何异常都会强制抛出TransactionSystemException - * - * @param dataId - * @param group - * @param datumMap - * @return - */ - public boolean batchPublishAggr(final String dataId, final String group, final String tenant, - final Map datumMap, final String appName) { - try { - Boolean isPublishOk = tjt.execute(new TransactionCallback() { - @Override - public Boolean doInTransaction(TransactionStatus status) { - for (Entry entry : datumMap.entrySet()) { - try { - if (!addAggrConfigInfo(dataId, group, tenant, entry.getKey(), appName, entry.getValue())) { - throw new TransactionSystemException( - "error in addAggrConfigInfo"); - } - } catch (Throwable e) { - throw new TransactionSystemException( - "error in addAggrConfigInfo"); - } - } - return Boolean.TRUE; - } - }); - if (isPublishOk == null) { - return false; - } - return isPublishOk.booleanValue(); - } catch (TransactionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - return false; - } - } + /** + * 添加普通配置信息,发布数据变更事件 + */ + public void addConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser, Timestamp time, + boolean notify) { + String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); + String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); + String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); + try { + String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); + jt.update( + "INSERT INTO config_info_tag(data_id,group_id,tenant_id,tag_id,app_name,content,md5,src_ip,src_user," + + "gmt_create,gmt_modified) VALUES(?,?,?,?,?,?,?,?,?,?,?)", + configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, appNameTmp, configInfo.getContent(), + md5, + srcIp, srcUser, time, time); + if (notify) { + EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), + configInfo.getGroup(), tenantTmp, tagTmp, time.getTime())); + } + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - /** - * 批量替换,先全部删除聚合表中指定DataID+Group的数据,再插入数据. - * 事务过程中出现任何异常都会强制抛出TransactionSystemException - * - * @param dataId - * @param group - * @param datumMap - * @return - */ - public boolean replaceAggr(final String dataId, final String group, final String tenant, - final Map datumMap, final String appName) { - try { - Boolean isReplaceOk = tjt.execute(new TransactionCallback() { - @Override - public Boolean doInTransaction(TransactionStatus status) { - try { - String appNameTmp = appName == null ? "" : appName; - removeAggrConfigInfo(dataId, group, tenant); - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "insert into config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, content, gmt_modified) values(?,?,?,?,?,?,?) "; - for (Entry datumEntry : datumMap.entrySet()) { - jt.update(sql, dataId, group, tenantTmp, datumEntry.getKey(), appNameTmp, - datumEntry.getValue(), new Timestamp(System.currentTimeMillis())); - } - } catch (Throwable e) { - throw new TransactionSystemException( - "error in addAggrConfigInfo"); - } - return Boolean.TRUE; - } - }); - if (isReplaceOk == null) { - return false; - } - return isReplaceOk.booleanValue(); - } catch (TransactionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - return false; - } + /** + * 更新配置信息 + */ + public void updateConfigInfo(final ConfigInfo configInfo, final String srcIp, final String srcUser, + final Timestamp time, final Map configAdvanceInfo, + final boolean notify) { + tjt.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + try { + ConfigInfo oldConfigInfo = findConfigInfo(configInfo.getDataId(), configInfo.getGroup(), + configInfo.getTenant()); + String appNameTmp = oldConfigInfo.getAppName(); + // 用户传过来的appName不为空,则用持久化用户的appName,否则用db的;清空appName的时候需要传空串 + if (configInfo.getAppName() == null) { + configInfo.setAppName(appNameTmp); + } + updateConfigInfoAtomic(configInfo, srcIp, srcUser, time, configAdvanceInfo); + String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + if (configTags != null) { + // 删除所有tag,然后再重新创建 + removeTagByIdAtomic(oldConfigInfo.getId()); + addConfiTagsRelationAtomic(oldConfigInfo.getId(), configTags, configInfo.getDataId(), + configInfo.getGroup(), configInfo.getTenant()); + } + insertConfigHistoryAtomic(oldConfigInfo.getId(), oldConfigInfo, srcIp, srcUser, time, "U"); + if (notify) { + EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), + configInfo.getGroup(), configInfo.getTenant(), time.getTime())); + } + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + return Boolean.TRUE; + } + }); + } - } + /** + * 更新配置信息 + */ + public void updateConfigInfo4Beta(ConfigInfo configInfo, String srcIp, String srcUser, Timestamp time, + boolean notify) { + String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); + String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); + try { + String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); + jt.update( + "UPDATE config_info_beta SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE " + + "data_id=? AND group_id=? AND tenant_id=?", + configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), + configInfo.getGroup(), tenantTmp); + if (notify) { + EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, configInfo.getDataId(), configInfo.getGroup(), + tenantTmp, time.getTime())); + } - /** - * 查找所有的dataId和group。保证不返回NULL。 - */ - @Deprecated - public List findAllDataIdAndGroup() { - String sql = "select distinct data_id, group_id from config_info"; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - try { - return jt.query(sql, new Object[] {}, CONFIG_INFO_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { - return Collections.emptyList(); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } catch (Exception e) { - fatalLog.error("[db-other-error]" + e.getMessage(), e); - throw new RuntimeException(e); - } - } - - /** - * 根据dataId和group查询配置信息 - */ - public ConfigInfo4Beta findConfigInfo4Beta(final String dataId, final String group, final String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - return this.jt.queryForObject( - "select ID,data_id,group_id,tenant_id,app_name,content,beta_ips from config_info_beta where data_id=? and group_id=? and tenant_id=?", - new Object[] { dataId, group, tenantTmp }, CONFIG_INFO4BETA_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据dataId和group查询配置信息 - */ - public ConfigInfo4Tag findConfigInfo4Tag(final String dataId, final String group, final String tenant, final String tag) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); - try { - return this.jt.queryForObject( - "select ID,data_id,group_id,tenant_id,tag_id,app_name,content from config_info_tag where data_id=? and group_id=? and tenant_id=? and tag_id=?", - new Object[] { dataId, group, tenantTmp, tagTmp }, CONFIG_INFO4TAG_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + /** + * 更新配置信息 + */ + public void updateConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser, Timestamp time, + boolean notify) { + String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); + String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); + String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); + try { + String md5 = MD5.getInstance().getMD5String(configInfo.getContent()); + jt.update( + "UPDATE config_info_tag SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=? WHERE " + + "data_id=? AND group_id=? AND tenant_id=? AND tag_id=?", + configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), + configInfo.getGroup(), tenantTmp, tagTmp); + if (notify) { + EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, configInfo.getDataId(), configInfo.getGroup(), + tenantTmp, tagTmp, time.getTime())); + } - - /** - * 根据dataId和group查询配置信息 - */ - public ConfigInfo findConfigInfoApp(final String dataId, final String group, final String tenant, - final String appName) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - return this.jt.queryForObject( - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and group_id=? and tenant_id=? and app_name=?", - new Object[] { dataId, group, tenantTmp, appName }, CONFIG_INFO_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据dataId和group查询配置信息 - */ - public ConfigInfo findConfigInfoAdvanceInfo(final String dataId, final String group, final String tenant, - final Map configAdvanceInfo) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); - final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - List paramList = new ArrayList(); - paramList.add(dataId); - paramList.add(group); - paramList.add(tenantTmp); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - StringBuilder sql = new StringBuilder("select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and group_id=? and tenant_id=? "); - if (StringUtils.isNotBlank(configTags)) { - sql = new StringBuilder("select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join config_tags_relation b on a.id=b.id where a.data_id=? and a.group_id=? and a.tenant_id=? "); - sql.append(" and b.tag_name in ("); - String [] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sql.append(", "); - } - sql.append("?"); - paramList.add(tagArr[i]); - } - sql.append(") "); + public void insertOrUpdateBeta(final ConfigInfo configInfo, final String betaIps, final String srcIp, + final String srcUser, final Timestamp time, final boolean notify) { + try { + addConfigInfo4Beta(configInfo, betaIps, srcIp, null, time, notify); + } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 + updateConfigInfo4Beta(configInfo, srcIp, null, time, notify); + } + } - if (StringUtils.isNotBlank(appName)) { - sql.append(" and a.app_name=? "); - paramList.add(appName); - } - } else { - if (StringUtils.isNotBlank(appName)) { - sql.append(" and app_name=? "); - paramList.add(appName); - } - } + public void insertOrUpdateTag(final ConfigInfo configInfo, final String tag, final String srcIp, + final String srcUser, final Timestamp time, final boolean notify) { + try { + addConfigInfo4Tag(configInfo, tag, srcIp, null, time, notify); + } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 + updateConfigInfo4Tag(configInfo, tag, srcIp, null, time, notify); + } + } - try { - return this.jt.queryForObject(sql.toString(), paramList.toArray(), CONFIG_INFO_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } + /** + * 更新md5 + */ + public void updateMd5(String dataId, String group, String tenant, String md5, Timestamp lastTime) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + jt.update( + "UPDATE config_info SET md5 = ? WHERE data_id=? AND group_id=? AND tenant_id=? AND gmt_modified=?", + md5, dataId, group, tenantTmp, lastTime); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - } - - /** - * 根据dataId和group查询配置信息 - */ - public ConfigInfoBase findConfigInfoBase(final String dataId, final String group) { - try { - return this.jt - .queryForObject( - "select ID,data_id,group_id,content from config_info where data_id=? and group_id=? and tenant_id=?", - new Object[] { dataId, group, StringUtils.EMPTY}, - CONFIG_INFO_BASE_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - + public void insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Timestamp time, + Map configAdvanceInfo) { + insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, true); + } - /** - * 根据数据库主键ID查询配置信息 - * - * @param id - * @return - */ - public ConfigInfo findConfigInfo(long id) { - try { - return this.jt - .queryForObject( - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where ID=?", - new Object[] { id }, CONFIG_INFO_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在 - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + /** + * 写入主表,插入或更新 + */ + public void insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Timestamp time, + Map configAdvanceInfo, boolean notify) { + try { + addConfigInfo(srcIp, srcUser, configInfo, time, configAdvanceInfo, notify); + } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 + updateConfigInfo(configInfo, srcIp, srcUser, time, configAdvanceInfo, notify); + } + } - /** - * 根据dataId查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * @param dataId - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoByDataId(final int pageNo, final int pageSize, final String dataId, - final String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPage(this.jt, "select count(*) from config_info where data_id=? and tenant_id=?", - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and tenant_id=?", - new Object[] { dataId, tenantTmp }, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据dataId查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * @param dataId - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoByDataIdAndApp(final int pageNo, final int pageSize, final String dataId, - final String tenant, final String appName) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPage(this.jt, - "select count(*) from config_info where data_id=? and tenant_id=? and app_name=?", - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and tenant_id=? and app_name=?", - new Object[] { dataId, tenantTmp, appName }, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public Page findConfigInfoByDataIdAndAdvance(final int pageNo, final int pageSize, final String dataId, - final String tenant, final Map configAdvanceInfo) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); - final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("select count(*) from config_info where data_id=? and tenant_id=? "); - StringBuilder sql = new StringBuilder("select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and tenant_id=? "); - List paramList = new ArrayList(); - paramList.add(dataId); - paramList.add(tenantTmp); - if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder("select count(*) from config_info a left join config_tags_relation b on a.id=b.id where a.data_id=? and a.tenant_id=? "); + /** + * 写入主表,插入或更新 + */ + public void insertOrUpdateSub(SubInfo subInfo) { + try { + addConfigSubAtomic(subInfo.getDataId(), subInfo.getGroup(), subInfo.getAppName(), subInfo.getDate()); + } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 + updateConfigSubAtomic(subInfo.getDataId(), subInfo.getGroup(), subInfo.getAppName(), subInfo.getDate()); + } + } - sql = new StringBuilder("select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join config_tags_relation b on a.id=b.id where a.data_id=? and a.tenant_id=? "); + /** + * 删除配置信息, 物理删除 + */ + public void removeConfigInfo(final String dataId, final String group, final String tenant, final String srcIp, + final String srcUser) { + tjt.execute(new TransactionCallback() { + final Timestamp time = new Timestamp(System.currentTimeMillis()); - sqlCount.append(" and b.tag_name in ("); - sql.append(" and b.tag_name in ("); - String [] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append("?"); - sql.append("?"); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); + @Override + public Boolean doInTransaction(TransactionStatus status) { + try { + ConfigInfo configInfo = findConfigInfo(dataId, group, tenant); + if (configInfo != null) { + removeConfigInfoAtomic(dataId, group, tenant, srcIp, srcUser); + removeTagByIdAtomic(configInfo.getId()); + insertConfigHistoryAtomic(configInfo.getId(), configInfo, srcIp, srcUser, time, "D"); + } + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + return Boolean.TRUE; + } + }); + } - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" and a.app_name=? "); - sql.append(" and a.app_name=? "); - paramList.add(appName); - } - } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" and app_name=? "); - sql.append(" and app_name=? "); - paramList.add(appName); - } - } - try { - return helper.fetchPage(this.jt, sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public Page findConfigInfo4Page(final int pageNo, final int pageSize, final String dataId, final String group, - final String tenant, final Map configAdvanceInfo) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); - final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - String sqlCount = "select count(*) from config_info"; - String sql = "select ID,data_id,group_id,tenant_id,app_name,content from config_info"; - StringBuilder where = new StringBuilder(" where "); - List paramList = new ArrayList(); - paramList.add(tenantTmp); - if (StringUtils.isNotBlank(configTags)) { - sqlCount = "select count(*) from config_info a left join config_tags_relation b on a.id=b.id"; - sql = "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join config_tags_relation b on a.id=b.id"; + /** + * 删除beta配置信息, 物理删除 + */ + public void removeConfigInfo4Beta(final String dataId, final String group, final String tenant) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + tjt.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + try { + ConfigInfo configInfo = findConfigInfo4Beta(dataId, group, tenant); + if (configInfo != null) { + jt.update("DELETE FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=?", dataId, + group, tenantTmp); + } + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + return Boolean.TRUE; + } + }); + } - where.append(" a.tenant_id=? "); - - if (StringUtils.isNotBlank(dataId)) { - where.append(" and a.data_id=? "); - paramList.add(dataId); - } - if (StringUtils.isNotBlank(group)) { - where.append(" and a.group_id=? "); - paramList.add(group); - } - if (StringUtils.isNotBlank(appName)) { - where.append(" and a.app_name=? "); - paramList.add(appName); - } + // ----------------------- config_aggr_info 表 insert update delete - where.append(" and b.tag_name in ("); - String [] tagArr= configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - where.append(", "); - } - where.append("?"); - paramList.add(tagArr[i]); - } - where.append(") "); - } else { - where.append(" tenant_id=? "); - if (StringUtils.isNotBlank(dataId)) { - where.append(" and data_id=? "); - paramList.add(dataId); - } - if (StringUtils.isNotBlank(group)) { - where.append(" and group_id=? "); - paramList.add(group); - } - if (StringUtils.isNotBlank(appName)) { - where.append(" and app_name=? "); - paramList.add(appName); - } - } - try { - return helper.fetchPage(this.jt, sqlCount + where, sql + where, paramList.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据dataId查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * @param dataId - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoBaseByDataId(final int pageNo, - final int pageSize, final String dataId) { - PaginationHelper helper = new PaginationHelper(); - try { - return helper - .fetchPage( - this.jt, - "select count(*) from config_info where data_id=? and tenant_id=?", - "select ID,data_id,group_id,content from config_info where data_id=? and tenant_id=?", - new Object[] { dataId, StringUtils.EMPTY }, pageNo, pageSize, - CONFIG_INFO_BASE_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + /** + * 增加聚合前数据到数据库, select -> update or insert + */ + public boolean addAggrConfigInfo(final String dataId, final String group, String tenant, final String datumId, + String appName, final String content) { + String appNameTmp = StringUtils.isBlank(appName) ? StringUtils.EMPTY : appName; + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + final Timestamp now = new Timestamp(System.currentTimeMillis()); + String select + = "SELECT content FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ? AND " + + "datum_id = ?"; + String insert + = "INSERT INTO config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, content, gmt_modified) " + + "VALUES(?,?,?,?,?,?,?) "; + String update + = "UPDATE config_info_aggr SET content = ? , gmt_modified = ? WHERE data_id = ? AND group_id = ? AND " + + "tenant_id = ? AND datum_id = ?"; - /** - * 根据group查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * - * @param group - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoByGroup(final int pageNo, final int pageSize, final String group, - final String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPage(this.jt, "select count(*) from config_info where group_id=? and tenant_id=?", - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where group_id=? and tenant_id=?", - new Object[] { group, tenantTmp }, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - /** - * 根据group查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * - * @param group - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoByGroupAndApp(final int pageNo, - final int pageSize, final String group, final String tenant, final String appName) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPage(this.jt, - "select count(*) from config_info where group_id=? and tenant_id=? and app_name =?", - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where group_id=? and tenant_id=? and app_name =?", - new Object[] { group, tenantTmp, appName }, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public Page findConfigInfoByGroupAndAdvance(final int pageNo, - final int pageSize, final String group, final String tenant, final Map configAdvanceInfo) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); + try { + try { + String dbContent = jt.queryForObject(select, new Object[] {dataId, group, tenantTmp, datumId}, + String.class); - final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); - final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("select count(*) from config_info where group_id=? and tenant_id=? "); - StringBuilder sql = new StringBuilder("select ID,data_id,group_id,tenant_id,app_name,content from config_info where group_id=? and tenant_id=? "); - List paramList = new ArrayList(); - paramList.add(group); - paramList.add(tenantTmp); - if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder("select count(*) from config_info a left join config_tags_relation b on a.id=b.id where a.group_id=? and a.tenant_id=? "); - sql = new StringBuilder("select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join config_tags_relation b on a.id=b.id where a.group_id=? and a.tenant_id=? "); + if (dbContent != null && dbContent.equals(content)) { + return true; + } else { + return jt.update(update, content, now, dataId, group, tenantTmp, datumId) > 0; + } + } catch (EmptyResultDataAccessException ex) { // no data, insert + return jt.update(insert, dataId, group, tenantTmp, datumId, appNameTmp, content, now) > 0; + } + } catch (DataAccessException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - sqlCount.append(" and b.tag_name in ("); - sql.append(" and b.tag_name in ("); - String [] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append("?"); - sql.append("?"); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); + /** + * 删除单条聚合前数据 + */ + public void removeSingleAggrConfigInfo(final String dataId, + final String group, final String tenant, final String datumId) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql = "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=? AND datum_id=?"; - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" and a.app_name=? "); - sql.append(" and a.app_name=? "); - paramList.add(appName); - } - } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" and app_name=? "); - sql.append(" and app_name=? "); - paramList.add(appName); - } - } + try { + this.jt.update(sql, new PreparedStatementSetter() { + public void setValues(PreparedStatement ps) throws SQLException { + int index = 1; + ps.setString(index++, dataId); + ps.setString(index++, group); + ps.setString(index++, tenantTmp); + ps.setString(index++, datumId); + } + }); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - try { - return helper.fetchPage(this.jt, sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据group查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * - * @param group - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoByApp(final int pageNo, - final int pageSize, final String tenant, final String appName) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPage(this.jt, "select count(*) from config_info where tenant_id like ? and app_name=?", - "select ID,data_id,group_id,tenant_id,app_name,content from config_info where tenant_id like ? and app_name=?", - new Object[] { generateLikeArgument(tenantTmp), appName }, pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public Page findConfigInfoByAdvance(final int pageNo, - final int pageSize, final String tenant, final Map configAdvanceInfo) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - PaginationHelper helper = new PaginationHelper(); - final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); - final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); - StringBuilder sqlCount = new StringBuilder("select count(*) from config_info where tenant_id like ? "); - StringBuilder sql = new StringBuilder("select ID,data_id,group_id,tenant_id,app_name,content from config_info where tenant_id like ? "); - List paramList = new ArrayList(); - paramList.add(tenantTmp); - if (StringUtils.isNotBlank(configTags)) { - sqlCount = new StringBuilder("select count(*) from config_info a left join config_tags_relation b on a.id=b.id where a.tenant_id=? "); - - sql = new StringBuilder("select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join config_tags_relation b on a.id=b.id where a.tenant_id=? "); - - sqlCount.append(" and b.tag_name in ("); - sql.append(" and b.tag_name in ("); - String [] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - sqlCount.append(", "); - sql.append(", "); - } - sqlCount.append("?"); - sql.append("?"); - paramList.add(tagArr[i]); - } - sqlCount.append(") "); - sql.append(") "); - - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" and a.app_name=? "); - sql.append(" and a.app_name=? "); - paramList.add(appName); - } - } else { - if (StringUtils.isNotBlank(appName)) { - sqlCount.append(" and app_name=? "); - sql.append(" and app_name=? "); - paramList.add(appName); - } - } - - try { - return helper.fetchPage(this.jt, sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据group查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * - * @param group - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoBaseByGroup(final int pageNo, - final int pageSize, final String group) { - PaginationHelper helper = new PaginationHelper(); - try { - return helper - .fetchPage( - this.jt, - "select count(*) from config_info where group_id=? and tenant_id=?", - "select ID,data_id,group_id,content from config_info where group_id=? and tenant_id=?", - new Object[] { group, StringUtils.EMPTY }, pageNo, pageSize, - CONFIG_INFO_BASE_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + /** + * 删除一个dataId下面所有的聚合前数据 + */ + public void removeAggrConfigInfo(final String dataId, final String group, final String tenant) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql = "DELETE FROM config_info_aggr WHERE data_id=? AND group_id=? AND tenant_id=?"; - /** - * 返回配置项个数 - */ - public int configInfoCount() { - String sql = " SELECT COUNT(ID) FROM config_info "; - Integer result = jt.queryForObject(sql, Integer.class); - if (result ==null) { - throw new IllegalArgumentException("configInfoCount error"); - } - return result.intValue(); - } - - /** - * 返回配置项个数 - */ - public int configInfoCount(String tenant) { - String sql = " SELECT COUNT(ID) FROM config_info where tenant_id like '" + tenant + "'"; - Integer result = jt.queryForObject(sql,Integer.class); - if (result ==null) { - throw new IllegalArgumentException("configInfoCount error"); - } - return result.intValue(); - } - - /** - * 返回beta配置项个数 - */ - public int configInfoBetaCount() { - String sql = " SELECT COUNT(ID) FROM config_info_beta "; - Integer result = jt.queryForObject(sql,Integer.class); - if (result == null) { - throw new IllegalArgumentException("configInfoBetaCount error"); - } - return result.intValue(); - } - - /** - * 返回beta配置项个数 - */ - public int configInfoTagCount() { - String sql = " SELECT COUNT(ID) FROM config_info_tag "; - Integer result = jt.queryForObject(sql,Integer.class); - if (result == null) { - throw new IllegalArgumentException("configInfoBetaCount error"); - } - return result.intValue(); - } + try { + this.jt.update(sql, new PreparedStatementSetter() { + public void setValues(PreparedStatement ps) throws SQLException { + int index = 1; + ps.setString(index++, dataId); + ps.setString(index++, group); + ps.setString(index++, tenantTmp); + } + }); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - public List getTenantIdList(int page, int pageSize) { - String sql = "select tenant_id from config_info where tenant_id != '' group by tenant_id limit ?, ?"; - int from = (page - 1) * pageSize; - return jt.queryForList(sql, String.class, from, pageSize); - } + /** + * 批量删除聚合数据,需要指定datum的列表 + * + * @param dataId + * @param group + * @param datumList + */ + public boolean batchRemoveAggr(final String dataId, final String group, final String tenant, + final List datumList) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + final StringBuilder datumString = new StringBuilder(); + for (String datum : datumList) { + datumString.append("'").append(datum).append("',"); + } + datumString.deleteCharAt(datumString.length() - 1); + final String sql = + "delete from config_info_aggr where data_id=? and group_id=? and tenant_id=? and datum_id in (" + + datumString.toString() + ")"; + try { + jt.update(sql, dataId, group, tenantTmp); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + return false; + } + return true; + } - public List getGroupIdList(int page, int pageSize) { - String sql = "select group_id from config_info where tenant_id ='' group by group_id limit ?, ?"; - int from = (page - 1) * pageSize; - return jt.queryForList(sql, String.class, from, pageSize); - } + /** + * 删除startTime前的数据 + */ + public void removeConfigHistory(final Timestamp startTime, final int limitSize) { + String sql = "delete from his_config_info where gmt_modified < ? limit ?"; + PaginationHelper helper = new PaginationHelper(); + try { + helper.updateLimit(jt, sql, new Object[] {startTime, limitSize}); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - public int aggrConfigInfoCount(String dataId, String group, String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = " SELECT COUNT(ID) FROM config_info_aggr WHERE data_id = ? and group_id = ? and tenant_id = ?"; - Integer result = jt.queryForObject(sql, Integer.class, new Object[] { dataId, group, tenantTmp }); - if (result == null) { - throw new IllegalArgumentException("aggrConfigInfoCount error"); - } - return result.intValue(); - } + /** + * 获取指定时间前配置条数 + */ + public int findConfigHistoryCountByTime(final Timestamp startTime) { + String sql = "SELECT COUNT(*) FROM his_config_info WHERE gmt_modified < ?"; + Integer result = jt.queryForObject(sql, Integer.class, new Object[] {startTime}); + if (result == null) { + throw new IllegalArgumentException("configInfoBetaCount error"); + } + return result.intValue(); + } - public int aggrConfigInfoCountIn(String dataId, String group, String tenant, List datumIds) { - return aggrConfigInfoCount(dataId, group, tenant, datumIds, true); - } + /** + * 获取最大maxId + */ + public long findConfigMaxId() { + String sql = "SELECT max(id) FROM config_info"; + try { + return jt.queryForObject(sql, Integer.class); + } catch (NullPointerException e) { + return 0; + } + } - public int aggrConfigInfoCountNotIn(String dataId, String group, String tenant, List datumIds) { - return aggrConfigInfoCount(dataId, group, tenant, datumIds, false); - } + /** + * 批量添加或者更新数据.事务过程中出现任何异常都会强制抛出TransactionSystemException + * + * @param dataId + * @param group + * @param datumMap + * @return + */ + public boolean batchPublishAggr(final String dataId, final String group, final String tenant, + final Map datumMap, final String appName) { + try { + Boolean isPublishOk = tjt.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + for (Entry entry : datumMap.entrySet()) { + try { + if (!addAggrConfigInfo(dataId, group, tenant, entry.getKey(), appName, entry.getValue())) { + throw new TransactionSystemException( + "error in addAggrConfigInfo"); + } + } catch (Throwable e) { + throw new TransactionSystemException( + "error in addAggrConfigInfo"); + } + } + return Boolean.TRUE; + } + }); + if (isPublishOk == null) { + return false; + } + return isPublishOk.booleanValue(); + } catch (TransactionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + return false; + } + } - private int aggrConfigInfoCount(String dataId, String group, String tenant, List datumIds, - boolean isIn) { - if (datumIds == null || datumIds.isEmpty()) { - return 0; - } - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - StringBuilder sql = new StringBuilder( - " SELECT COUNT(*) FROM config_info_aggr WHERE data_id = ? and group_id = ? and tenant_id = ? and " - + "datum_id"); - if (isIn) { - sql.append(" in ("); - } else { - sql.append(" not in ("); - } - for (int i = 0, size = datumIds.size(); i < size; i++) { - if (i > 0) { - sql.append(", "); - } - sql.append("?"); - } - sql.append(")"); + /** + * 批量替换,先全部删除聚合表中指定DataID+Group的数据,再插入数据. 事务过程中出现任何异常都会强制抛出TransactionSystemException + * + * @param dataId + * @param group + * @param datumMap + * @return + */ + public boolean replaceAggr(final String dataId, final String group, final String tenant, + final Map datumMap, final String appName) { + try { + Boolean isReplaceOk = tjt.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + try { + String appNameTmp = appName == null ? "" : appName; + removeAggrConfigInfo(dataId, group, tenant); + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql + = "INSERT INTO config_info_aggr(data_id, group_id, tenant_id, datum_id, app_name, " + + "content, gmt_modified) VALUES(?,?,?,?,?,?,?) "; + for (Entry datumEntry : datumMap.entrySet()) { + jt.update(sql, dataId, group, tenantTmp, datumEntry.getKey(), appNameTmp, + datumEntry.getValue(), new Timestamp(System.currentTimeMillis())); + } + } catch (Throwable e) { + throw new TransactionSystemException( + "error in addAggrConfigInfo"); + } + return Boolean.TRUE; + } + }); + if (isReplaceOk == null) { + return false; + } + return isReplaceOk.booleanValue(); + } catch (TransactionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + return false; + } - List objectList = Lists.newArrayList(dataId, group, tenantTmp); - objectList.addAll(datumIds); - Integer result = jt.queryForObject(sql.toString(), Integer.class, objectList.toArray()); - if (result == null) { - throw new IllegalArgumentException("aggrConfigInfoCount error"); - } - return result.intValue(); - } + } - /** - * 分页查询所有的配置信息 - * - * @param pageNo - * 页码(从1开始) - * @param pageSize - * 每页大小(必须大于0) - * - * @return ConfigInfo对象的集合 - */ - public Page findAllConfigInfo(final int pageNo, final int pageSize, final String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT COUNT(*) FROM config_info"; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " - + " FROM ( " - + " SELECT id FROM config_info " - + " WHERE tenant_id like ? " - + " ORDER BY id LIMIT ?,? " - + " ) g, config_info t " - + " WHERE g.id = t.id "; + /** + * 查找所有的dataId和group。保证不返回NULL。 + */ + @Deprecated + public List findAllDataIdAndGroup() { + String sql = "SELECT DISTINCT data_id, group_id FROM config_info"; - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, new Object[] { generateLikeArgument(tenantTmp), (pageNo - 1) * pageSize, pageSize }, - pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + try { + return jt.query(sql, new Object[] {}, CONFIG_INFO_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { + return Collections.emptyList(); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } catch (Exception e) { + fatalLog.error("[db-other-error]" + e.getMessage(), e); + throw new RuntimeException(e); + } + } - /** - * 分页查询所有的配置信息 - * - * @param pageNo - * 页码(从1开始) - * @param pageSize - * 每页大小(必须大于0) - * - * @return ConfigInfo对象的集合 - */ - public Page findAllConfigKey(final int pageNo, final int pageSize, final String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String select = " SELECT data_id,group_id,app_name " - + " FROM ( " - + " SELECT id FROM config_info " - + " WHERE tenant_id like ? " - + " ORDER BY id LIMIT ?, ? " - + " ) g, config_info t " - + " WHERE g.id = t.id "; + /** + * 根据dataId和group查询配置信息 + */ + public ConfigInfo4Beta findConfigInfo4Beta(final String dataId, final String group, final String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + return this.jt.queryForObject( + "SELECT ID,data_id,group_id,tenant_id,app_name,content,beta_ips FROM config_info_beta WHERE data_id=?" + + " AND group_id=? AND tenant_id=?", + new Object[] {dataId, group, tenantTmp}, CONFIG_INFO4BETA_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - final int totalCount = configInfoCount(tenant); - int pageCount = totalCount / pageSize; - if (totalCount > pageSize * pageCount) { - pageCount++; - } + /** + * 根据dataId和group查询配置信息 + */ + public ConfigInfo4Tag findConfigInfo4Tag(final String dataId, final String group, final String tenant, + final String tag) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); + try { + return this.jt.queryForObject( + "SELECT ID,data_id,group_id,tenant_id,tag_id,app_name,content FROM config_info_tag WHERE data_id=? " + + "AND group_id=? AND tenant_id=? AND tag_id=?", + new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO4TAG_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - if (pageNo > pageCount) { - return null; - } + /** + * 根据dataId和group查询配置信息 + */ + public ConfigInfo findConfigInfoApp(final String dataId, final String group, final String tenant, + final String appName) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + return this.jt.queryForObject( + "SELECT ID,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE data_id=? AND " + + "group_id=? AND tenant_id=? AND app_name=?", + new Object[] {dataId, group, tenantTmp, appName}, CONFIG_INFO_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - final Page page = new Page(); - page.setPageNumber(pageNo); - page.setPagesAvailable(pageCount); - page.setTotalCount(totalCount); + /** + * 根据dataId和group查询配置信息 + */ + public ConfigInfo findConfigInfoAdvanceInfo(final String dataId, final String group, final String tenant, + final Map configAdvanceInfo) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); + final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + List paramList = new ArrayList(); + paramList.add(dataId); + paramList.add(group); + paramList.add(tenantTmp); - try { - List result = jt.query(select, new Object[] { generateLikeArgument(tenantTmp), (pageNo - 1) * pageSize, pageSize }, - // new Object[0], - CONFIG_KEY_ROW_MAPPER); + StringBuilder sql = new StringBuilder( + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and group_id=? " + + "and tenant_id=? "); + if (StringUtils.isNotBlank(configTags)) { + sql = new StringBuilder( + "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join " + + "config_tags_relation b on a.id=b.id where a.data_id=? and a.group_id=? and a.tenant_id=? "); + sql.append(" and b.tag_name in ("); + String[] tagArr = configTags.split(","); + for (int i = 0; i < tagArr.length; i++) { + if (i != 0) { + sql.append(", "); + } + sql.append("?"); + paramList.add(tagArr[i]); + } + sql.append(") "); - for (ConfigKey item : result) { - page.getPageItems().add(item); - } - return page; - } catch (EmptyResultDataAccessException e) { - return page; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 分页查询所有的配置信息 - * - * @param pageNo - * 页码(从1开始) - * @param pageSize - * 每页大小(必须大于0) - * - * @return ConfigInfo对象的集合 - */ + if (StringUtils.isNotBlank(appName)) { + sql.append(" and a.app_name=? "); + paramList.add(appName); + } + } else { + if (StringUtils.isNotBlank(appName)) { + sql.append(" and app_name=? "); + paramList.add(appName); + } + } + + try { + return this.jt.queryForObject(sql.toString(), paramList.toArray(), CONFIG_INFO_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + + } + + /** + * 根据dataId和group查询配置信息 + */ + public ConfigInfoBase findConfigInfoBase(final String dataId, final String group) { + try { + return this.jt + .queryForObject( + "SELECT ID,data_id,group_id,content FROM config_info WHERE data_id=? AND group_id=? AND " + + "tenant_id=?", + new Object[] {dataId, group, StringUtils.EMPTY}, + CONFIG_INFO_BASE_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据数据库主键ID查询配置信息 + * + * @param id + * @return + */ + public ConfigInfo findConfigInfo(long id) { + try { + return this.jt + .queryForObject( + "SELECT ID,data_id,group_id,tenant_id,app_name,content FROM config_info WHERE ID=?", + new Object[] {id}, CONFIG_INFO_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在 + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据dataId查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param dataId + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoByDataId(final int pageNo, final int pageSize, final String dataId, + final String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPage(this.jt, "select count(*) from config_info where data_id=? and tenant_id=?", + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and " + + "tenant_id=?", + new Object[] {dataId, tenantTmp}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据dataId查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param dataId + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoByDataIdAndApp(final int pageNo, final int pageSize, final String dataId, + final String tenant, final String appName) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPage(this.jt, + "select count(*) from config_info where data_id=? and tenant_id=? and app_name=?", + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and " + + "tenant_id=? and app_name=?", + new Object[] {dataId, tenantTmp, appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public Page findConfigInfoByDataIdAndAdvance(final int pageNo, final int pageSize, final String dataId, + final String tenant, + final Map configAdvanceInfo) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); + final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + StringBuilder sqlCount = new StringBuilder("select count(*) from config_info where data_id=? and tenant_id=? "); + StringBuilder sql = new StringBuilder( + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where data_id=? and tenant_id=? "); + List paramList = new ArrayList(); + paramList.add(dataId); + paramList.add(tenantTmp); + if (StringUtils.isNotBlank(configTags)) { + sqlCount = new StringBuilder( + "select count(*) from config_info a left join config_tags_relation b on a.id=b.id where a.data_id=? " + + "and a.tenant_id=? "); + + sql = new StringBuilder( + "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join " + + "config_tags_relation b on a.id=b.id where a.data_id=? and a.tenant_id=? "); + + sqlCount.append(" and b.tag_name in ("); + sql.append(" and b.tag_name in ("); + String[] tagArr = configTags.split(","); + for (int i = 0; i < tagArr.length; i++) { + if (i != 0) { + sqlCount.append(", "); + sql.append(", "); + } + sqlCount.append("?"); + sql.append("?"); + paramList.add(tagArr[i]); + } + sqlCount.append(") "); + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" and a.app_name=? "); + sql.append(" and a.app_name=? "); + paramList.add(appName); + } + } else { + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" and app_name=? "); + sql.append(" and app_name=? "); + paramList.add(appName); + } + } + try { + return helper.fetchPage(this.jt, sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public Page findConfigInfo4Page(final int pageNo, final int pageSize, final String dataId, + final String group, + final String tenant, final Map configAdvanceInfo) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); + final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + String sqlCount = "select count(*) from config_info"; + String sql = "select ID,data_id,group_id,tenant_id,app_name,content from config_info"; + StringBuilder where = new StringBuilder(" where "); + List paramList = new ArrayList(); + paramList.add(tenantTmp); + if (StringUtils.isNotBlank(configTags)) { + sqlCount = "select count(*) from config_info a left join config_tags_relation b on a.id=b.id"; + sql + = "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join " + + "config_tags_relation b on a.id=b.id"; + + where.append(" a.tenant_id=? "); + + if (StringUtils.isNotBlank(dataId)) { + where.append(" and a.data_id=? "); + paramList.add(dataId); + } + if (StringUtils.isNotBlank(group)) { + where.append(" and a.group_id=? "); + paramList.add(group); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" and a.app_name=? "); + paramList.add(appName); + } + + where.append(" and b.tag_name in ("); + String[] tagArr = configTags.split(","); + for (int i = 0; i < tagArr.length; i++) { + if (i != 0) { + where.append(", "); + } + where.append("?"); + paramList.add(tagArr[i]); + } + where.append(") "); + } else { + where.append(" tenant_id=? "); + if (StringUtils.isNotBlank(dataId)) { + where.append(" and data_id=? "); + paramList.add(dataId); + } + if (StringUtils.isNotBlank(group)) { + where.append(" and group_id=? "); + paramList.add(group); + } + if (StringUtils.isNotBlank(appName)) { + where.append(" and app_name=? "); + paramList.add(appName); + } + } + try { + return helper.fetchPage(this.jt, sqlCount + where, sql + where, paramList.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据dataId查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param dataId + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoBaseByDataId(final int pageNo, + final int pageSize, final String dataId) { + PaginationHelper helper = new PaginationHelper(); + try { + return helper + .fetchPage( + this.jt, + "select count(*) from config_info where data_id=? and tenant_id=?", + "select ID,data_id,group_id,content from config_info where data_id=? and tenant_id=?", + new Object[] {dataId, StringUtils.EMPTY}, pageNo, pageSize, + CONFIG_INFO_BASE_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据group查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param group + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoByGroup(final int pageNo, final int pageSize, final String group, + final String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPage(this.jt, "select count(*) from config_info where group_id=? and tenant_id=?", + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where group_id=? and " + + "tenant_id=?", + new Object[] {group, tenantTmp}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据group查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param group + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoByGroupAndApp(final int pageNo, + final int pageSize, final String group, final String tenant, + final String appName) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPage(this.jt, + "select count(*) from config_info where group_id=? and tenant_id=? and app_name =?", + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where group_id=? and " + + "tenant_id=? and app_name =?", + new Object[] {group, tenantTmp, appName}, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public Page findConfigInfoByGroupAndAdvance(final int pageNo, + final int pageSize, final String group, final String tenant, + final Map configAdvanceInfo) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + + final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); + final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + StringBuilder sqlCount = new StringBuilder( + "select count(*) from config_info where group_id=? and tenant_id=? "); + StringBuilder sql = new StringBuilder( + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where group_id=? and tenant_id=? "); + List paramList = new ArrayList(); + paramList.add(group); + paramList.add(tenantTmp); + if (StringUtils.isNotBlank(configTags)) { + sqlCount = new StringBuilder( + "select count(*) from config_info a left join config_tags_relation b on a.id=b.id where a.group_id=?" + + " and a.tenant_id=? "); + sql = new StringBuilder( + "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join " + + "config_tags_relation b on a.id=b.id where a.group_id=? and a.tenant_id=? "); + + sqlCount.append(" and b.tag_name in ("); + sql.append(" and b.tag_name in ("); + String[] tagArr = configTags.split(","); + for (int i = 0; i < tagArr.length; i++) { + if (i != 0) { + sqlCount.append(", "); + sql.append(", "); + } + sqlCount.append("?"); + sql.append("?"); + paramList.add(tagArr[i]); + } + sqlCount.append(") "); + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" and a.app_name=? "); + sql.append(" and a.app_name=? "); + paramList.add(appName); + } + } else { + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" and app_name=? "); + sql.append(" and app_name=? "); + paramList.add(appName); + } + } + + try { + return helper.fetchPage(this.jt, sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据group查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param group + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoByApp(final int pageNo, + final int pageSize, final String tenant, final String appName) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPage(this.jt, "select count(*) from config_info where tenant_id like ? and app_name=?", + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where tenant_id like ? and " + + "app_name=?", + new Object[] {generateLikeArgument(tenantTmp), appName}, pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public Page findConfigInfoByAdvance(final int pageNo, + final int pageSize, final String tenant, + final Map configAdvanceInfo) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + PaginationHelper helper = new PaginationHelper(); + final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); + final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + StringBuilder sqlCount = new StringBuilder("select count(*) from config_info where tenant_id like ? "); + StringBuilder sql = new StringBuilder( + "select ID,data_id,group_id,tenant_id,app_name,content from config_info where tenant_id like ? "); + List paramList = new ArrayList(); + paramList.add(tenantTmp); + if (StringUtils.isNotBlank(configTags)) { + sqlCount = new StringBuilder( + "select count(*) from config_info a left join config_tags_relation b on a.id=b.id where a.tenant_id=?" + + " "); + + sql = new StringBuilder( + "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join " + + "config_tags_relation b on a.id=b.id where a.tenant_id=? "); + + sqlCount.append(" and b.tag_name in ("); + sql.append(" and b.tag_name in ("); + String[] tagArr = configTags.split(","); + for (int i = 0; i < tagArr.length; i++) { + if (i != 0) { + sqlCount.append(", "); + sql.append(", "); + } + sqlCount.append("?"); + sql.append("?"); + paramList.add(tagArr[i]); + } + sqlCount.append(") "); + sql.append(") "); + + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" and a.app_name=? "); + sql.append(" and a.app_name=? "); + paramList.add(appName); + } + } else { + if (StringUtils.isNotBlank(appName)) { + sqlCount.append(" and app_name=? "); + sql.append(" and app_name=? "); + paramList.add(appName); + } + } + + try { + return helper.fetchPage(this.jt, sqlCount.toString(), sql.toString(), paramList.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据group查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param group + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoBaseByGroup(final int pageNo, + final int pageSize, final String group) { + PaginationHelper helper = new PaginationHelper(); + try { + return helper + .fetchPage( + this.jt, + "select count(*) from config_info where group_id=? and tenant_id=?", + "select ID,data_id,group_id,content from config_info where group_id=? and tenant_id=?", + new Object[] {group, StringUtils.EMPTY}, pageNo, pageSize, + CONFIG_INFO_BASE_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 返回配置项个数 + */ + public int configInfoCount() { + String sql = " SELECT COUNT(ID) FROM config_info "; + Integer result = jt.queryForObject(sql, Integer.class); + if (result == null) { + throw new IllegalArgumentException("configInfoCount error"); + } + return result.intValue(); + } + + /** + * 返回配置项个数 + */ + public int configInfoCount(String tenant) { + String sql = " SELECT COUNT(ID) FROM config_info where tenant_id like '" + tenant + "'"; + Integer result = jt.queryForObject(sql, Integer.class); + if (result == null) { + throw new IllegalArgumentException("configInfoCount error"); + } + return result.intValue(); + } + + /** + * 返回beta配置项个数 + */ + public int configInfoBetaCount() { + String sql = " SELECT COUNT(ID) FROM config_info_beta "; + Integer result = jt.queryForObject(sql, Integer.class); + if (result == null) { + throw new IllegalArgumentException("configInfoBetaCount error"); + } + return result.intValue(); + } + + /** + * 返回beta配置项个数 + */ + public int configInfoTagCount() { + String sql = " SELECT COUNT(ID) FROM config_info_tag "; + Integer result = jt.queryForObject(sql, Integer.class); + if (result == null) { + throw new IllegalArgumentException("configInfoBetaCount error"); + } + return result.intValue(); + } + + public List getTenantIdList(int page, int pageSize) { + String sql = "SELECT tenant_id FROM config_info WHERE tenant_id != '' GROUP BY tenant_id LIMIT ?, ?"; + int from = (page - 1) * pageSize; + return jt.queryForList(sql, String.class, from, pageSize); + } + + public List getGroupIdList(int page, int pageSize) { + String sql = "SELECT group_id FROM config_info WHERE tenant_id ='' GROUP BY group_id LIMIT ?, ?"; + int from = (page - 1) * pageSize; + return jt.queryForList(sql, String.class, from, pageSize); + } + + public int aggrConfigInfoCount(String dataId, String group, String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql = " SELECT COUNT(ID) FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND tenant_id = ?"; + Integer result = jt.queryForObject(sql, Integer.class, new Object[] {dataId, group, tenantTmp}); + if (result == null) { + throw new IllegalArgumentException("aggrConfigInfoCount error"); + } + return result.intValue(); + } + + public int aggrConfigInfoCountIn(String dataId, String group, String tenant, List datumIds) { + return aggrConfigInfoCount(dataId, group, tenant, datumIds, true); + } + + public int aggrConfigInfoCountNotIn(String dataId, String group, String tenant, List datumIds) { + return aggrConfigInfoCount(dataId, group, tenant, datumIds, false); + } + + private int aggrConfigInfoCount(String dataId, String group, String tenant, List datumIds, + boolean isIn) { + if (datumIds == null || datumIds.isEmpty()) { + return 0; + } + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + StringBuilder sql = new StringBuilder( + " SELECT COUNT(*) FROM config_info_aggr WHERE data_id = ? and group_id = ? and tenant_id = ? and " + + "datum_id"); + if (isIn) { + sql.append(" in ("); + } else { + sql.append(" not in ("); + } + for (int i = 0, size = datumIds.size(); i < size; i++) { + if (i > 0) { + sql.append(", "); + } + sql.append("?"); + } + sql.append(")"); + + List objectList = Lists.newArrayList(dataId, group, tenantTmp); + objectList.addAll(datumIds); + Integer result = jt.queryForObject(sql.toString(), Integer.class, objectList.toArray()); + if (result == null) { + throw new IllegalArgumentException("aggrConfigInfoCount error"); + } + return result.intValue(); + } + + /** + * 分页查询所有的配置信息 + * + * @param pageNo 页码(从1开始) + * @param pageSize 每页大小(必须大于0) + * @return ConfigInfo对象的集合 + */ + public Page findAllConfigInfo(final int pageNo, final int pageSize, final String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sqlCountRows = "SELECT COUNT(*) FROM config_info"; + String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5 " + + " FROM ( " + + " SELECT id FROM config_info " + + " WHERE tenant_id like ? " + + " ORDER BY id LIMIT ?,? " + + " ) g, config_info t " + + " WHERE g.id = t.id "; + + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, + new Object[] {generateLikeArgument(tenantTmp), (pageNo - 1) * pageSize, pageSize}, + pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 分页查询所有的配置信息 + * + * @param pageNo 页码(从1开始) + * @param pageSize 每页大小(必须大于0) + * @return ConfigInfo对象的集合 + */ + public Page findAllConfigKey(final int pageNo, final int pageSize, final String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String select = " SELECT data_id,group_id,app_name " + + " FROM ( " + + " SELECT id FROM config_info " + + " WHERE tenant_id LIKE ? " + + " ORDER BY id LIMIT ?, ? " + + " ) g, config_info t " + + " WHERE g.id = t.id "; + + final int totalCount = configInfoCount(tenant); + int pageCount = totalCount / pageSize; + if (totalCount > pageSize * pageCount) { + pageCount++; + } + + if (pageNo > pageCount) { + return null; + } + + final Page page = new Page(); + page.setPageNumber(pageNo); + page.setPagesAvailable(pageCount); + page.setTotalCount(totalCount); + + try { + List result = jt.query(select, + new Object[] {generateLikeArgument(tenantTmp), (pageNo - 1) * pageSize, pageSize}, + // new Object[0], + CONFIG_KEY_ROW_MAPPER); + + for (ConfigKey item : result) { + page.getPageItems().add(item); + } + return page; + } catch (EmptyResultDataAccessException e) { + return page; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 分页查询所有的配置信息 + * + * @param pageNo 页码(从1开始) + * @param pageSize 每页大小(必须大于0) + * @return ConfigInfo对象的集合 + */ @Deprecated public Page findAllConfigInfoBase(final int pageNo, final int pageSize) { String sqlCountRows = "SELECT COUNT(*) FROM config_info"; String sqlFetchRows = " SELECT t.id,data_id,group_id,content,md5 " - + " FROM ( " - + " SELECT id FROM config_info " - + " ORDER BY id LIMIT ?,? " - + " ) g, config_info t " - + " WHERE g.id = t.id "; + + " FROM ( " + + " SELECT id FROM config_info " + + " ORDER BY id LIMIT ?,? " + + " ) g, config_info t " + + " WHERE g.id = t.id "; PaginationHelper helper = new PaginationHelper(); try { return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, new Object[] { - (pageNo - 1) * pageSize, pageSize }, pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); + (pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, CONFIG_INFO_BASE_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); throw e; } } - public static class ConfigInfoWrapper extends ConfigInfo { - private static final long serialVersionUID = 4511997359365712505L; - - private long lastModified; + public static class ConfigInfoWrapper extends ConfigInfo { + private static final long serialVersionUID = 4511997359365712505L; - public ConfigInfoWrapper() { - } + private long lastModified; - public long getLastModified() { - return lastModified; - } + public ConfigInfoWrapper() { + } - public void setLastModified(long lastModified) { - this.lastModified = lastModified; - } - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - } - public static class ConfigInfoBetaWrapper extends ConfigInfo4Beta { - private static final long serialVersionUID = 4511997359365712505L; - - private long lastModified; - - public ConfigInfoBetaWrapper() { - } - - public long getLastModified() { - return lastModified; - } - - public void setLastModified(long lastModified) { - this.lastModified = lastModified; - } - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - } - - public static class ConfigInfoTagWrapper extends ConfigInfo4Tag { - private static final long serialVersionUID = 4511997359365712505L; - - private long lastModified; - - public ConfigInfoTagWrapper() { - } - - public long getLastModified() { - return lastModified; - } - - public void setLastModified(long lastModified) { - this.lastModified = lastModified; - } - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - } + public long getLastModified() { + return lastModified; + } + + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + } + + public static class ConfigInfoBetaWrapper extends ConfigInfo4Beta { + private static final long serialVersionUID = 4511997359365712505L; + + private long lastModified; + + public ConfigInfoBetaWrapper() { + } + + public long getLastModified() { + return lastModified; + } + + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + } + + public static class ConfigInfoTagWrapper extends ConfigInfo4Tag { + private static final long serialVersionUID = 4511997359365712505L; + + private long lastModified; + + public ConfigInfoTagWrapper() { + } + + public long getLastModified() { + return lastModified; + } + + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + } public Page findAllConfigInfoForDumpAll( - final int pageNo, final int pageSize) { + final int pageNo, final int pageSize) { String sqlCountRows = "select count(*) from config_info"; String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified " - + " FROM ( " - + " SELECT id FROM config_info " - + " ORDER BY id LIMIT ?,? " - + " ) g, config_info t " - + " WHERE g.id = t.id "; + + " FROM ( " + + " SELECT id FROM config_info " + + " ORDER BY id LIMIT ?,? " + + " ) g, config_info t " + + " WHERE g.id = t.id "; PaginationHelper helper = new PaginationHelper(); List params = new ArrayList(); try { - return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); + return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, params.toArray(), pageNo, pageSize, + CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); throw e; @@ -1845,10 +1887,13 @@ public class PersistService { } public Page findAllConfigInfoFragment(final long lastMaxId, final int pageSize) { - String select = "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified from config_info where id > ? order by id asc limit ?,?"; + String select + = "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified from config_info where id > ? " + + "order by id asc limit ?,?"; PaginationHelper helper = new PaginationHelper(); try { - return helper.fetchPageLimit(jt, select, new Object[] {lastMaxId, 0, pageSize }, 1, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); + return helper.fetchPageLimit(jt, select, new Object[] {lastMaxId, 0, pageSize}, 1, pageSize, + CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); throw e; @@ -1856,18 +1901,18 @@ public class PersistService { } public Page findAllConfigInfoBetaForDumpAll( - final int pageNo, final int pageSize) { + final int pageNo, final int pageSize) { String sqlCountRows = "SELECT COUNT(*) FROM config_info_beta"; String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,beta_ips " - + " FROM ( " - + " SELECT id FROM config_info_beta " - + " ORDER BY id LIMIT ?,? " - + " ) g, config_info_beta t " - + " WHERE g.id = t.id "; + + " FROM ( " + + " SELECT id FROM config_info_beta " + + " ORDER BY id LIMIT ?,? " + + " ) g, config_info_beta t " + + " WHERE g.id = t.id "; PaginationHelper helper = new PaginationHelper(); try { return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, new Object[] { - (pageNo - 1) * pageSize, pageSize }, pageNo, pageSize, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); + (pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); @@ -1876,18 +1921,18 @@ public class PersistService { } public Page findAllConfigInfoTagForDumpAll( - final int pageNo, final int pageSize) { + final int pageNo, final int pageSize) { String sqlCountRows = "SELECT COUNT(*) FROM config_info_tag"; String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,tag_id,app_name,content,md5,gmt_modified " - + " FROM ( " - + " SELECT id FROM config_info_tag " - + " ORDER BY id LIMIT ?,? " - + " ) g, config_info_tag t " - + " WHERE g.id = t.id "; + + " FROM ( " + + " SELECT id FROM config_info_tag " + + " ORDER BY id LIMIT ?,? " + + " ) g, config_info_tag t " + + " WHERE g.id = t.id "; PaginationHelper helper = new PaginationHelper(); try { return helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, new Object[] { - (pageNo - 1) * pageSize, pageSize }, pageNo, pageSize, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); + (pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); @@ -1895,1145 +1940,1239 @@ public class PersistService { } } - /** - * 通过select in方式实现db记录的批量查询; subQueryLimit指定in中条件的个数,上限20 - */ - public List findConfigInfoByBatch(final List dataIds, - final String group, final String tenant, int subQueryLimit) { - // assert dataids group not null - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - // if dataids empty return empty list - if (CollectionUtils.isEmpty(dataIds)) { - return Collections.emptyList(); - } + /** + * 通过select in方式实现db记录的批量查询; subQueryLimit指定in中条件的个数,上限20 + */ + public List findConfigInfoByBatch(final List dataIds, + final String group, final String tenant, int subQueryLimit) { + // assert dataids group not null + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + // if dataids empty return empty list + if (CollectionUtils.isEmpty(dataIds)) { + return Collections.emptyList(); + } - // 批量查询上限 - // in 个数控制在100内, sql语句长度越短越好 - if (subQueryLimit > QUERY_LIMIT_SIZE) { - subQueryLimit = 50; - } - List result = new ArrayList(dataIds.size()); + // 批量查询上限 + // in 个数控制在100内, sql语句长度越短越好 + if (subQueryLimit > QUERY_LIMIT_SIZE) { + subQueryLimit = 50; + } + List result = new ArrayList(dataIds.size()); - String sqlStart = "select data_id, group_id, tenant_id, app_name, content from config_info where group_id = ? and tenant_id = ? and data_id in ("; - String sqlEnd = ")"; - StringBuilder subQuerySql = new StringBuilder(); + String sqlStart + = "select data_id, group_id, tenant_id, app_name, content from config_info where group_id = ? and " + + "tenant_id = ? and data_id in ("; + String sqlEnd = ")"; + StringBuilder subQuerySql = new StringBuilder(); - for (int i = 0; i < dataIds.size(); i += subQueryLimit) { - // dataids - List params = new ArrayList(dataIds.subList(i, i - + subQueryLimit < dataIds.size() ? i + subQueryLimit - : dataIds.size())); + for (int i = 0; i < dataIds.size(); i += subQueryLimit) { + // dataids + List params = new ArrayList(dataIds.subList(i, i + + subQueryLimit < dataIds.size() ? i + subQueryLimit + : dataIds.size())); - for (int j = 0; j < params.size(); j++) { - subQuerySql.append("?"); - if (j != params.size() - 1) { - subQuerySql.append(","); - } - } + for (int j = 0; j < params.size(); j++) { + subQuerySql.append("?"); + if (j != params.size() - 1) { + subQuerySql.append(","); + } + } - // group - params.add(0, group); - params.add(1, tenantTmp); + // group + params.add(0, group); + params.add(1, tenantTmp); - List r = this.jt.query( - sqlStart + subQuerySql.toString() + sqlEnd, - params.toArray(), CONFIG_INFO_ROW_MAPPER); + List r = this.jt.query( + sqlStart + subQuerySql.toString() + sqlEnd, + params.toArray(), CONFIG_INFO_ROW_MAPPER); - // assert not null - if (r != null && r.size() > 0) { - result.addAll(r); - } - } - return result; - } + // assert not null + if (r != null && r.size() > 0) { + result.addAll(r); + } + } + return result; + } - /** - * 根据dataId和group模糊查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * @param dataId - * 支持模糊查询 - * @param group - * 支持模糊查询 - * @param tenant - * 支持模糊查询 - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoLike(final int pageNo, final int pageSize, final String dataId, - final String group, final String tenant, final String appName, final String content) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - if (StringUtils.isBlank(dataId) && StringUtils.isBlank(group)) { - if (StringUtils.isBlank(appName)) { - return this.findAllConfigInfo(pageNo, pageSize, tenantTmp); - } else { - return this.findConfigInfoByApp(pageNo, pageSize, tenantTmp, appName); - } - } + /** + * 根据dataId和group模糊查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param dataId 支持模糊查询 + * @param group 支持模糊查询 + * @param tenant 支持模糊查询 + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoLike(final int pageNo, final int pageSize, final String dataId, + final String group, final String tenant, final String appName, + final String content) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + if (StringUtils.isBlank(dataId) && StringUtils.isBlank(group)) { + if (StringUtils.isBlank(appName)) { + return this.findAllConfigInfo(pageNo, pageSize, tenantTmp); + } else { + return this.findConfigInfoByApp(pageNo, pageSize, tenantTmp, appName); + } + } - PaginationHelper helper = new PaginationHelper(); + PaginationHelper helper = new PaginationHelper(); - String sqlCountRows = "select count(*) from config_info where "; - String sqlFetchRows = "select ID,data_id,group_id,tenant_id,app_name,content from config_info where "; - String where = " 1=1 "; - List params = new ArrayList(); + String sqlCountRows = "select count(*) from config_info where "; + String sqlFetchRows = "select ID,data_id,group_id,tenant_id,app_name,content from config_info where "; + String where = " 1=1 "; + List params = new ArrayList(); - if (!StringUtils.isBlank(dataId)) { - where += " and data_id like ? "; - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where += " and group_id like ? "; - params.add(generateLikeArgument(group)); - } - - where += " and tenant_id like ? "; - params.add(generateLikeArgument(tenantTmp)); + if (!StringUtils.isBlank(dataId)) { + where += " and data_id like ? "; + params.add(generateLikeArgument(dataId)); + } + if (!StringUtils.isBlank(group)) { + where += " and group_id like ? "; + params.add(generateLikeArgument(group)); + } - if (!StringUtils.isBlank(appName)) { - where += " and app_name = ? "; - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where += " and content like ? "; - params.add(generateLikeArgument(content)); - } + where += " and tenant_id like ? "; + params.add(generateLikeArgument(tenantTmp)); - try { - return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows - + where, params.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public Page findConfigInfoLike4Page(final int pageNo, final int pageSize, final String dataId, - final String group, final String tenant, final Map configAdvanceInfo) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); - final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); - final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); - PaginationHelper helper = new PaginationHelper(); - String sqlCountRows = "select count(*) from config_info"; - String sqlFetchRows = "select ID,data_id,group_id,tenant_id,app_name,content from config_info"; - StringBuilder where = new StringBuilder(" where "); - List params = new ArrayList(); - params.add(generateLikeArgument(tenantTmp)); - if (StringUtils.isNotBlank(configTags)) { - sqlCountRows = "select count(*) from config_info a left join config_tags_relation b on a.id=b.id "; - sqlFetchRows = "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join config_tags_relation b on a.id=b.id "; - - where.append(" a.tenant_id like ? "); - if (!StringUtils.isBlank(dataId)) { - where.append(" and a.data_id like ? "); - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where.append(" and a.group_id like ? "); - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(appName)) { - where.append(" and a.app_name = ? "); - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where.append(" and a.content like ? "); - params.add(generateLikeArgument(content)); - } + if (!StringUtils.isBlank(appName)) { + where += " and app_name = ? "; + params.add(appName); + } + if (!StringUtils.isBlank(content)) { + where += " and content like ? "; + params.add(generateLikeArgument(content)); + } - where.append(" and b.tag_name in ("); - String[] tagArr = configTags.split(","); - for (int i = 0; i < tagArr.length; i++) { - if (i != 0) { - where.append(", "); - } - where.append("?"); - params.add(tagArr[i]); - } - where.append(") "); - } else { - where.append(" tenant_id like ? "); - if (!StringUtils.isBlank(dataId)) { - where.append(" and data_id like ? "); - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where.append(" and group_id like ? "); - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(appName)) { - where.append(" and app_name = ? "); - params.add(appName); - } - if (!StringUtils.isBlank(content)) { - where.append(" and content like ? "); - params.add(generateLikeArgument(content)); - } - } - - try { - return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows - + where, params.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据dataId和group模糊查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * @param configKeys - * 查询配置列表 - * @param blacklist - * 是否黑名单 - * - * @return ConfigInfo对象的集合 - */ - public Page findConfigInfoLike(final int pageNo, - final int pageSize, final ConfigKey[] configKeys, final boolean blacklist) { - String sqlCountRows = "select count(*) from config_info where "; - String sqlFetchRows = "select ID,data_id,group_id,tenant_id,app_name,content from config_info where "; - String where = " 1=1 "; - // 白名单,请同步条件为空,则没有符合条件的配置 - if (configKeys.length == 0 && blacklist == false) { - Page page = new Page(); - page.setTotalCount(0); - return page; - } - PaginationHelper helper = new PaginationHelper(); - List params = new ArrayList(); - boolean isFirst = true; - for (ConfigKey configInfo : configKeys) { - String dataId = configInfo.getDataId(); - String group = configInfo.getGroup(); - String appName = configInfo.getAppName(); - - if (StringUtils.isBlank(dataId) - && StringUtils.isBlank(group) - && StringUtils.isBlank(appName)) { - break; - } - - if (blacklist) { - if (isFirst) { - isFirst = false; - where += " and "; - } else { - where += " and "; - } - - where += "("; - boolean isFirstSub = true; - if (!StringUtils.isBlank(dataId)) { - where += " data_id not like ? "; - params.add(generateLikeArgument(dataId)); - isFirstSub = false; - } - if (!StringUtils.isBlank(group)) { - if (!isFirstSub) { - where += " or "; - } - where += " group_id not like ? "; - params.add(generateLikeArgument(group)); - isFirstSub = false; - } - if (!StringUtils.isBlank(appName)) { - if (!isFirstSub) { - where += " or "; - } - where += " app_name != ? "; - params.add(appName); - isFirstSub = false; - } - where += ") "; - } else { - if (isFirst) { - isFirst = false; - where += " and "; - } else { - where += " or "; - } - where += "("; - boolean isFirstSub = true; - if (!StringUtils.isBlank(dataId)) { - where += " data_id like ? "; - params.add(generateLikeArgument(dataId)); - isFirstSub = false; - } - if (!StringUtils.isBlank(group)) { - if (!isFirstSub) { - where += " and "; - } - where += " group_id like ? "; - params.add(generateLikeArgument(group)); - isFirstSub = false; - } - if (!StringUtils.isBlank(appName)) { - if (!isFirstSub) { - where += " and "; - } - where += " app_name = ? "; - params.add(appName); - isFirstSub = false; - } - where += ") "; - } - } - - try { - return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows - + where, params.toArray(), pageNo, pageSize, - CONFIG_INFO_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - /** - * 根据dataId和group模糊查询配置信息 - * - * @param pageNo - * 页码(必须大于0) - * @param pageSize - * 每页大小(必须大于0) - * @param dataId - * @param group - * - * @return ConfigInfo对象的集合 - * @throws IOException - */ - public Page findConfigInfoBaseLike(final int pageNo, - final int pageSize, final String dataId, final String group, - final String content) throws IOException { - if (StringUtils.isBlank(dataId) && StringUtils.isBlank(group)) { - throw new IOException("invalid param"); - } - - PaginationHelper helper = new PaginationHelper(); - - String sqlCountRows = "select count(*) from config_info where "; - String sqlFetchRows = "select ID,data_id,group_id,tenant_id,content from config_info where "; - String where = " 1=1 and tenant_id='' "; - List params = new ArrayList(); - - if (!StringUtils.isBlank(dataId)) { - where += " and data_id like ? "; - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where += " and group_id like ? "; - params.add(generateLikeArgument(group)); - } - if (!StringUtils.isBlank(content)) { - where += " and content like ? "; - params.add(generateLikeArgument(content)); - } - - try { - return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows - + where, params.toArray(), pageNo, pageSize, - CONFIG_INFO_BASE_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + try { + return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows + + where, params.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - /** - * 查找聚合前的单条数据 - * - * @param dataId - * @param group - * @param datumId - * @return - */ - public ConfigInfoAggr findSingleConfigInfoAggr(String dataId, String group, String tenant, String datumId) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "select id,data_id,group_id,tenant_id,datum_id,app_name,content from config_info_aggr where data_id=? and group_id=? and tenant_id=? and datum_id=?"; + public Page findConfigInfoLike4Page(final int pageNo, final int pageSize, final String dataId, + final String group, final String tenant, + final Map configAdvanceInfo) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + final String appName = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("appName"); + final String content = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("content"); + final String configTags = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("config_tags"); + PaginationHelper helper = new PaginationHelper(); + String sqlCountRows = "select count(*) from config_info"; + String sqlFetchRows = "select ID,data_id,group_id,tenant_id,app_name,content from config_info"; + StringBuilder where = new StringBuilder(" where "); + List params = new ArrayList(); + params.add(generateLikeArgument(tenantTmp)); + if (StringUtils.isNotBlank(configTags)) { + sqlCountRows = "select count(*) from config_info a left join config_tags_relation b on a.id=b.id "; + sqlFetchRows + = "select a.ID,a.data_id,a.group_id,a.tenant_id,a.app_name,a.content from config_info a left join " + + "config_tags_relation b on a.id=b.id "; - try { - return this.jt.queryForObject(sql, new Object[] { dataId, group, tenantTmp, datumId }, - CONFIG_INFO_AGGR_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { - // 是EmptyResultDataAccessException, 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } catch (Exception e) { - fatalLog.error("[db-other-error]" + e.getMessage(), e); - throw new RuntimeException(e); - } - } + where.append(" a.tenant_id like ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" and a.data_id like ? "); + params.add(generateLikeArgument(dataId)); + } + if (!StringUtils.isBlank(group)) { + where.append(" and a.group_id like ? "); + params.add(generateLikeArgument(group)); + } + if (!StringUtils.isBlank(appName)) { + where.append(" and a.app_name = ? "); + params.add(appName); + } + if (!StringUtils.isBlank(content)) { + where.append(" and a.content like ? "); + params.add(generateLikeArgument(content)); + } - /** - * 查找一个dataId下面的所有聚合前的数据. 保证不返回NULL. - */ - public List findConfigInfoAggr(String dataId, String group, String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sql = "select data_id,group_id,tenant_id,datum_id,app_name,content from config_info_aggr where data_id=? and group_id=? and tenant_id=? order by datum_id"; + where.append(" and b.tag_name in ("); + String[] tagArr = configTags.split(","); + for (int i = 0; i < tagArr.length; i++) { + if (i != 0) { + where.append(", "); + } + where.append("?"); + params.add(tagArr[i]); + } + where.append(") "); + } else { + where.append(" tenant_id like ? "); + if (!StringUtils.isBlank(dataId)) { + where.append(" and data_id like ? "); + params.add(generateLikeArgument(dataId)); + } + if (!StringUtils.isBlank(group)) { + where.append(" and group_id like ? "); + params.add(generateLikeArgument(group)); + } + if (!StringUtils.isBlank(appName)) { + where.append(" and app_name = ? "); + params.add(appName); + } + if (!StringUtils.isBlank(content)) { + where.append(" and content like ? "); + params.add(generateLikeArgument(content)); + } + } - try { - return this.jt.query(sql, new Object[] { dataId, group, tenantTmp }, - CONFIG_INFO_AGGR_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } catch (EmptyResultDataAccessException e) { - return Collections.emptyList(); - } catch (Exception e) { - fatalLog.error("[db-other-error]" + e.getMessage(), e); - throw new RuntimeException(e); - } - } + try { + return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows + + where, params.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据dataId和group模糊查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param configKeys 查询配置列表 + * @param blacklist 是否黑名单 + * @return ConfigInfo对象的集合 + */ + public Page findConfigInfoLike(final int pageNo, + final int pageSize, final ConfigKey[] configKeys, + final boolean blacklist) { + String sqlCountRows = "select count(*) from config_info where "; + String sqlFetchRows = "select ID,data_id,group_id,tenant_id,app_name,content from config_info where "; + String where = " 1=1 "; + // 白名单,请同步条件为空,则没有符合条件的配置 + if (configKeys.length == 0 && blacklist == false) { + Page page = new Page(); + page.setTotalCount(0); + return page; + } + PaginationHelper helper = new PaginationHelper(); + List params = new ArrayList(); + boolean isFirst = true; + for (ConfigKey configInfo : configKeys) { + String dataId = configInfo.getDataId(); + String group = configInfo.getGroup(); + String appName = configInfo.getAppName(); + + if (StringUtils.isBlank(dataId) + && StringUtils.isBlank(group) + && StringUtils.isBlank(appName)) { + break; + } + + if (blacklist) { + if (isFirst) { + isFirst = false; + where += " and "; + } else { + where += " and "; + } + + where += "("; + boolean isFirstSub = true; + if (!StringUtils.isBlank(dataId)) { + where += " data_id not like ? "; + params.add(generateLikeArgument(dataId)); + isFirstSub = false; + } + if (!StringUtils.isBlank(group)) { + if (!isFirstSub) { + where += " or "; + } + where += " group_id not like ? "; + params.add(generateLikeArgument(group)); + isFirstSub = false; + } + if (!StringUtils.isBlank(appName)) { + if (!isFirstSub) { + where += " or "; + } + where += " app_name != ? "; + params.add(appName); + isFirstSub = false; + } + where += ") "; + } else { + if (isFirst) { + isFirst = false; + where += " and "; + } else { + where += " or "; + } + where += "("; + boolean isFirstSub = true; + if (!StringUtils.isBlank(dataId)) { + where += " data_id like ? "; + params.add(generateLikeArgument(dataId)); + isFirstSub = false; + } + if (!StringUtils.isBlank(group)) { + if (!isFirstSub) { + where += " and "; + } + where += " group_id like ? "; + params.add(generateLikeArgument(group)); + isFirstSub = false; + } + if (!StringUtils.isBlank(appName)) { + if (!isFirstSub) { + where += " and "; + } + where += " app_name = ? "; + params.add(appName); + isFirstSub = false; + } + where += ") "; + } + } + + try { + return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows + + where, params.toArray(), pageNo, pageSize, + CONFIG_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 根据dataId和group模糊查询配置信息 + * + * @param pageNo 页码(必须大于0) + * @param pageSize 每页大小(必须大于0) + * @param dataId + * @param group + * @return ConfigInfo对象的集合 + * @throws IOException + */ + public Page findConfigInfoBaseLike(final int pageNo, + final int pageSize, final String dataId, final String group, + final String content) throws IOException { + if (StringUtils.isBlank(dataId) && StringUtils.isBlank(group)) { + throw new IOException("invalid param"); + } + + PaginationHelper helper = new PaginationHelper(); + + String sqlCountRows = "select count(*) from config_info where "; + String sqlFetchRows = "select ID,data_id,group_id,tenant_id,content from config_info where "; + String where = " 1=1 and tenant_id='' "; + List params = new ArrayList(); + + if (!StringUtils.isBlank(dataId)) { + where += " and data_id like ? "; + params.add(generateLikeArgument(dataId)); + } + if (!StringUtils.isBlank(group)) { + where += " and group_id like ? "; + params.add(generateLikeArgument(group)); + } + if (!StringUtils.isBlank(content)) { + where += " and content like ? "; + params.add(generateLikeArgument(content)); + } + + try { + return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows + + where, params.toArray(), pageNo, pageSize, + CONFIG_INFO_BASE_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 查找聚合前的单条数据 + * + * @param dataId + * @param group + * @param datumId + * @return + */ + public ConfigInfoAggr findSingleConfigInfoAggr(String dataId, String group, String tenant, String datumId) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql + = "SELECT id,data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? " + + "AND group_id=? AND tenant_id=? AND datum_id=?"; + + try { + return this.jt.queryForObject(sql, new Object[] {dataId, group, tenantTmp, datumId}, + CONFIG_INFO_AGGR_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { + // 是EmptyResultDataAccessException, 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } catch (Exception e) { + fatalLog.error("[db-other-error]" + e.getMessage(), e); + throw new RuntimeException(e); + } + } + + /** + * 查找一个dataId下面的所有聚合前的数据. 保证不返回NULL. + */ + public List findConfigInfoAggr(String dataId, String group, String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sql + = "SELECT data_id,group_id,tenant_id,datum_id,app_name,content FROM config_info_aggr WHERE data_id=? AND " + + "group_id=? AND tenant_id=? ORDER BY datum_id"; + + try { + return this.jt.query(sql, new Object[] {dataId, group, tenantTmp}, + CONFIG_INFO_AGGR_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } catch (EmptyResultDataAccessException e) { + return Collections.emptyList(); + } catch (Exception e) { + fatalLog.error("[db-other-error]" + e.getMessage(), e); + throw new RuntimeException(e); + } + } public Page findConfigInfoAggrByPage(String dataId, String group, String tenant, final int pageNo, final int pageSize) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "SELECT COUNT(*) FROM config_info_aggr WHERE data_id = ? and group_id = ? and tenant_id = ?"; - String sqlFetchRows = "select data_id,group_id,tenant_id,datum_id,app_name,content from config_info_aggr where data_id=? and group_id=? and tenant_id=? order by datum_id limit ?,?"; + String sqlCountRows + = "SELECT COUNT(*) FROM config_info_aggr WHERE data_id = ? and group_id = ? and tenant_id = ?"; + String sqlFetchRows + = "select data_id,group_id,tenant_id,datum_id,app_name,content from config_info_aggr where data_id=? and " + + "group_id=? and tenant_id=? order by datum_id limit ?,?"; PaginationHelper helper = new PaginationHelper(); try { - return helper.fetchPageLimit(jt, sqlCountRows, new Object[] {dataId, group, tenantTmp}, sqlFetchRows, new Object[] {dataId, group, tenantTmp, (pageNo - 1) * pageSize, pageSize }, - pageNo, pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); + return helper.fetchPageLimit(jt, sqlCountRows, new Object[] {dataId, group, tenantTmp}, sqlFetchRows, + new Object[] {dataId, group, tenantTmp, (pageNo - 1) * pageSize, pageSize}, + pageNo, pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { fatalLog.error("[db-error] " + e.toString(), e); throw e; } } - - /** - * 查询符合条件的聚合数据 - * - * @param pageNo - * pageNo - * @param pageSize - * pageSize - * @param configKeys - * 聚合数据条件 - * @param blacklist - * 黑名单 - * @return - */ - public Page findConfigInfoAggrLike(final int pageNo, final int pageSize, ConfigKey[] configKeys, boolean blacklist) { - - String sqlCountRows = "select count(*) from config_info_aggr where "; - String sqlFetchRows = "select data_id,group_id,tenant_id,datum_id,app_name,content from config_info_aggr where "; - String where = " 1=1 "; - // 白名单,请同步条件为空,则没有符合条件的配置 - if (configKeys.length == 0 && blacklist == false) { - Page page = new Page(); - page.setTotalCount(0); - return page; - } - PaginationHelper helper = new PaginationHelper(); - List params = new ArrayList(); - boolean isFirst = true; - - for (ConfigKey configInfoAggr : configKeys) { - String dataId = configInfoAggr.getDataId(); - String group = configInfoAggr.getGroup(); - String appName = configInfoAggr.getAppName(); - if (StringUtils.isBlank(dataId) - && StringUtils.isBlank(group) - && StringUtils.isBlank(appName)) { - break; - } - if (blacklist) { - if (isFirst) { - isFirst = false; - where += " and "; - } else { - where += " and "; - } - - where += "("; - boolean isFirstSub = true; - if (!StringUtils.isBlank(dataId)) { - where += " data_id not like ? "; - params.add(generateLikeArgument(dataId)); - isFirstSub = false; - } - if (!StringUtils.isBlank(group)) { - if (!isFirstSub) { - where += " or "; - } - where += " group_id not like ? "; - params.add(generateLikeArgument(group)); - isFirstSub = false; - } - if (!StringUtils.isBlank(appName)) { - if (!isFirstSub) { - where += " or "; - } - where += " app_name != ? "; - params.add(appName); - isFirstSub = false; - } - where += ") "; - } else { - if (isFirst) { - isFirst = false; - where += " and "; - } else { - where += " or "; - } - where += "("; - boolean isFirstSub = true; - if (!StringUtils.isBlank(dataId)) { - where += " data_id like ? "; - params.add(generateLikeArgument(dataId)); - isFirstSub = false; - } - if (!StringUtils.isBlank(group)) { - if (!isFirstSub) { - where += " and "; - } - where += " group_id like ? "; - params.add(generateLikeArgument(group)); - isFirstSub = false; - } - if (!StringUtils.isBlank(appName)) { - if (!isFirstSub) { - where += " and "; - } - where += " app_name = ? "; - params.add(appName); - isFirstSub = false; - } - where += ") "; - } - } - - try { - Page result = helper.fetchPage(jt, sqlCountRows - + where, sqlFetchRows + where, params.toArray(), pageNo, - pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); - return result; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - /** - * 找到所有聚合数据组。 - */ - public List findAllAggrGroup() { - String sql = "select distinct data_id, group_id, tenant_id from config_info_aggr"; + /** + * 查询符合条件的聚合数据 + * + * @param pageNo pageNo + * @param pageSize pageSize + * @param configKeys 聚合数据条件 + * @param blacklist 黑名单 + * @return + */ + public Page findConfigInfoAggrLike(final int pageNo, final int pageSize, ConfigKey[] configKeys, + boolean blacklist) { - try { - return this.jt.query(sql, new Object[] {}, - CONFIG_INFO_CHANGED_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } catch (EmptyResultDataAccessException e) { - return null; - } catch (Exception e) { - fatalLog.error("[db-other-error]" + e.getMessage(), e); - throw new RuntimeException(e); - } - } + String sqlCountRows = "select count(*) from config_info_aggr where "; + String sqlFetchRows + = "select data_id,group_id,tenant_id,datum_id,app_name,content from config_info_aggr where "; + String where = " 1=1 "; + // 白名单,请同步条件为空,则没有符合条件的配置 + if (configKeys.length == 0 && blacklist == false) { + Page page = new Page(); + page.setTotalCount(0); + return page; + } + PaginationHelper helper = new PaginationHelper(); + List params = new ArrayList(); + boolean isFirst = true; - /** - * 由datum内容查找datumId - * @param dataId data id - * @param groupId group - * @param content content - * @return datum keys - */ - public List findDatumIdByContent(String dataId, String groupId, - String content) { - String sql = "select datum_id from config_info_aggr where data_id = ? and group_id = ? and content = ? "; + for (ConfigKey configInfoAggr : configKeys) { + String dataId = configInfoAggr.getDataId(); + String group = configInfoAggr.getGroup(); + String appName = configInfoAggr.getAppName(); + if (StringUtils.isBlank(dataId) + && StringUtils.isBlank(group) + && StringUtils.isBlank(appName)) { + break; + } + if (blacklist) { + if (isFirst) { + isFirst = false; + where += " and "; + } else { + where += " and "; + } - try { - return this.jt.queryForList(sql, new Object[] { dataId, groupId, - content }, String.class); - } catch (EmptyResultDataAccessException e) { - return null; - } catch (IncorrectResultSizeDataAccessException e) { - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public List findChangeConfig(final Timestamp startTime, - final Timestamp endTime) { - try { - List> list = jt - .queryForList( - "SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified FROM config_info where gmt_modified >=? and gmt_modified <= ?", - new Object[] { startTime, endTime }); - return convertChangeConfig(list); - } catch (DataAccessException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 根据时间段和配置条件查询符合条件的配置 - * - * @param dataId - * dataId 支持模糊 - * @param group - * dataId 支持模糊 - * @param appName - * 产品名 - * @param startTime - * 起始时间 - * @param endTime - * 截止时间 - * @param pageNo - * pageNo - * @param pageSize - * pageSize - * @return - */ - public Page findChangeConfig(final String dataId, final String group, final String tenant, - final String appName, final Timestamp startTime, final Timestamp endTime, final int pageNo, - final int pageSize, final long lastMaxId) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "select count(*) from config_info where "; - String sqlFetchRows = "select id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified from config_info where "; - String where = " 1=1 "; - List params = new ArrayList(); + where += "("; + boolean isFirstSub = true; + if (!StringUtils.isBlank(dataId)) { + where += " data_id not like ? "; + params.add(generateLikeArgument(dataId)); + isFirstSub = false; + } + if (!StringUtils.isBlank(group)) { + if (!isFirstSub) { + where += " or "; + } + where += " group_id not like ? "; + params.add(generateLikeArgument(group)); + isFirstSub = false; + } + if (!StringUtils.isBlank(appName)) { + if (!isFirstSub) { + where += " or "; + } + where += " app_name != ? "; + params.add(appName); + isFirstSub = false; + } + where += ") "; + } else { + if (isFirst) { + isFirst = false; + where += " and "; + } else { + where += " or "; + } + where += "("; + boolean isFirstSub = true; + if (!StringUtils.isBlank(dataId)) { + where += " data_id like ? "; + params.add(generateLikeArgument(dataId)); + isFirstSub = false; + } + if (!StringUtils.isBlank(group)) { + if (!isFirstSub) { + where += " and "; + } + where += " group_id like ? "; + params.add(generateLikeArgument(group)); + isFirstSub = false; + } + if (!StringUtils.isBlank(appName)) { + if (!isFirstSub) { + where += " and "; + } + where += " app_name = ? "; + params.add(appName); + isFirstSub = false; + } + where += ") "; + } + } - if (!StringUtils.isBlank(dataId)) { - where += " and data_id like ? "; - params.add(generateLikeArgument(dataId)); - } - if (!StringUtils.isBlank(group)) { - where += " and group_id like ? "; - params.add(generateLikeArgument(group)); - } - - if (!StringUtils.isBlank(tenantTmp)) { - where += " and tenant_id = ? "; - params.add(tenantTmp); - } + try { + Page result = helper.fetchPage(jt, sqlCountRows + + where, sqlFetchRows + where, params.toArray(), pageNo, + pageSize, CONFIG_INFO_AGGR_ROW_MAPPER); + return result; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - if (!StringUtils.isBlank(appName)) { - where += " and app_name = ? "; - params.add(appName); - } - if (startTime != null) { - where += " and gmt_modified >=? "; - params.add(startTime); - } - if (endTime != null) { - where += " and gmt_modified <=? "; - params.add(endTime); - } - - PaginationHelper helper = new PaginationHelper(); - try { - return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, - lastMaxId, CONFIG_INFO_WRAPPER_ROW_MAPPER); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + /** + * 找到所有聚合数据组。 + */ + public List findAllAggrGroup() { + String sql = "SELECT DISTINCT data_id, group_id, tenant_id FROM config_info_aggr"; - public List findDeletedConfig(final Timestamp startTime, - final Timestamp endTime) { - try { - List> list = jt - .queryForList( - "SELECT distinct data_id, group_id, tenant_id FROM his_config_info where op_type = 'D' and gmt_modified >=? and gmt_modified <= ?", - new Object[] { startTime, endTime }); - return convertDeletedConfig(list); - } catch (DataAccessException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + try { + return this.jt.query(sql, new Object[] {}, + CONFIG_INFO_CHANGED_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } catch (EmptyResultDataAccessException e) { + return null; + } catch (Exception e) { + fatalLog.error("[db-other-error]" + e.getMessage(), e); + throw new RuntimeException(e); + } + } - /** - * 增加配置;数据库原子操作,最小sql动作,无业务封装 - * @param srcIp ip - * @param srcUser user - * @param configInfo info - * @param time time - * @param configAdvanceInfo advance info - * @return excute sql result - */ - private long addConfigInfoAtomic(final String srcIp, final String srcUser, final ConfigInfo configInfo, final Timestamp time, - Map configAdvanceInfo) { - final String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - final String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - - final String desc = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("desc"); - final String use = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("use"); - final String effect = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("effect"); - final String type = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("type"); - final String schema = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("schema"); - - final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent()); - - KeyHolder keyHolder = new GeneratedKeyHolder(); - - final String sql = "insert into config_info(data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_create,gmt_modified,c_desc,c_use,effect,type,c_schema) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + /** + * 由datum内容查找datumId + * + * @param dataId data id + * @param groupId group + * @param content content + * @return datum keys + */ + public List findDatumIdByContent(String dataId, String groupId, + String content) { + String sql = "SELECT datum_id FROM config_info_aggr WHERE data_id = ? AND group_id = ? AND content = ? "; - try { - jt.update(new PreparedStatementCreator() { - @SuppressFBWarnings(value = { "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", - "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" }, justification = "findbugs does not trust jdbctemplate, sql is constant in practice") - public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { - PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - ps.setString(1, configInfo.getDataId()); - ps.setString(2, configInfo.getGroup()); - ps.setString(3, tenantTmp); - ps.setString(4, appNameTmp); - ps.setString(5, configInfo.getContent()); - ps.setString(6, md5Tmp); - ps.setString(7, srcIp); - ps.setString(8, srcUser); - ps.setTimestamp(9, time); - ps.setTimestamp(10, time); - ps.setString(11, desc); - ps.setString(12, use); - ps.setString(13, effect); - ps.setString(14, type); - ps.setString(15, schema); - return ps; - } - }, keyHolder); - Number nu = keyHolder.getKey(); - if (nu == null) { - throw new IllegalArgumentException("insert config_info fail"); - } - return nu.longValue(); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - + try { + return this.jt.queryForList(sql, new Object[] {dataId, groupId, + content}, String.class); + } catch (EmptyResultDataAccessException e) { + return null; + } catch (IncorrectResultSizeDataAccessException e) { + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - /** - * 增加配置;数据库原子操作,最小sql动作,无业务封装 - * @param configId id - * @param tagName tag - * @param dataId data id - * @param group group - * @param tenant tenant - */ - public void addConfiTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant) { - try { - jt.update( - "insert into config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) values(?,?,?,?,?,?)", - configId, tagName, null, dataId, group, tenant); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + public List findChangeConfig(final Timestamp startTime, + final Timestamp endTime) { + try { + List> list = jt + .queryForList( + "SELECT data_id, group_id, tenant_id, app_name, content, gmt_modified FROM config_info WHERE " + + "gmt_modified >=? AND gmt_modified <= ?", + new Object[] {startTime, endTime}); + return convertChangeConfig(list); + } catch (DataAccessException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - /** - * 增加配置;数据库原子操作,最小sql动作,无业务封装 - * @param configId config id - * @param configTags tags - * @param dataId dataId - * @param group group - * @param tenant tenant - */ - public void addConfiTagsRelationAtomic(long configId, String configTags, String dataId, String group, String tenant) { - if (StringUtils.isNotBlank(configTags)) { - String [] tagArr = configTags.split(","); - for (String tag : tagArr) { - addConfiTagRelationAtomic(configId, tag, dataId, group, tenant); - } - } - } - - public void removeTagByIdAtomic(long id) { - try { - jt.update("delete from config_tags_relation where id=?", id); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + /** + * 根据时间段和配置条件查询符合条件的配置 + * + * @param dataId dataId 支持模糊 + * @param group dataId 支持模糊 + * @param appName 产品名 + * @param startTime 起始时间 + * @param endTime 截止时间 + * @param pageNo pageNo + * @param pageSize pageSize + * @return + */ + public Page findChangeConfig(final String dataId, final String group, final String tenant, + final String appName, final Timestamp startTime, + final Timestamp endTime, final int pageNo, + final int pageSize, final long lastMaxId) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sqlCountRows = "select count(*) from config_info where "; + String sqlFetchRows + = "select id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified from config_info where "; + String where = " 1=1 "; + List params = new ArrayList(); - public List getConfigTagsByTenant(String tenant) { - String sql = "select tag_name from config_tags_relation where tenant_id = ? "; - try { - return jt.queryForList(sql, new Object[] { tenant }, String.class); - } catch (EmptyResultDataAccessException e) { - return null; - } catch (IncorrectResultSizeDataAccessException e) { - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public List selectTagByConfig(String dataId, String group, String tenant) { - String sql = "select tag_name from config_tags_relation where data_id=? and group_id=? and tenant_id = ? "; - try { - return jt.queryForList(sql, new Object[] { dataId, group, tenant }, String.class); - } catch (EmptyResultDataAccessException e) { - return null; - } catch (IncorrectResultSizeDataAccessException e) { - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + if (!StringUtils.isBlank(dataId)) { + where += " and data_id like ? "; + params.add(generateLikeArgument(dataId)); + } + if (!StringUtils.isBlank(group)) { + where += " and group_id like ? "; + params.add(generateLikeArgument(group)); + } - /** - * 删除配置;数据库原子操作,最小sql动作,无业务封装 - * @param dataId dataId - * @param group group - * @param tenant tenant - * @param srcIp ip - * @param srcUser user - */ - private void removeConfigInfoAtomic(final String dataId, final String group, final String tenant, final String srcIp, - final String srcUser) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - jt.update("delete from config_info where data_id=? and group_id=? and tenant_id=?", dataId, group, - tenantTmp); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 删除配置;数据库原子操作,最小sql动作,无业务封装 - * @param dataId dataId - * @param group group - * @param tenant tenant - * @param tag tag - * @param srcIp ip - * @param srcUser user - */ - public void removeConfigInfoTag(final String dataId, final String group, final String tenant, final String tag, final String srcIp, - final String srcUser) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag; - try { - jt.update("delete from config_info_tag where data_id=? and group_id=? and tenant_id=? and tag_id=?", dataId, group, - tenantTmp,tagTmp); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + if (!StringUtils.isBlank(tenantTmp)) { + where += " and tenant_id = ? "; + params.add(tenantTmp); + } - /** - * 更新配置;数据库原子操作,最小sql动作,无业务封装 - * @param configInfo config info - * @param srcIp ip - * @param srcUser user - * @param time time - * @param configAdvanceInfo advance info - */ - private void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, - final Timestamp time, Map configAdvanceInfo) { - String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent()); - String desc = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("desc"); - String use = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("use"); - String effect = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("effect"); - String type = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("type"); - String schema = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("schema"); - - try { - jt.update( - "update config_info set content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=?,c_desc=?,c_use=?,effect=?,type=?,c_schema=? where data_id=? and group_id=? and tenant_id=?", - configInfo.getContent(), md5Tmp, srcIp, srcUser, time, appNameTmp, desc, use, effect, type, schema, - configInfo.getDataId(), configInfo.getGroup(), tenantTmp); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 查询配置信息;数据库原子操作,最小sql动作,无业务封装 - * @param dataId dataId - * @param group group - * @param tenant tenant - * @return config info - */ - public ConfigInfo findConfigInfo(final String dataId, final String group, final String tenant) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - return this.jt.queryForObject( - "select ID,data_id,group_id,tenant_id,app_name,content,md5 from config_info where data_id=? and group_id=? and tenant_id=?", - new Object[] { dataId, group, tenantTmp }, CONFIG_INFO_ROW_MAPPER); - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 查询配置信息;数据库原子操作,最小sql动作,无业务封装 - * @param dataId dataId - * @param group group - * @param tenant tenant - * @return advance info - */ - public ConfigAdvanceInfo findConfigAdvanceInfo(final String dataId, final String group, final String tenant) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - List configTagList = this.selectTagByConfig(dataId, group, tenant); - ConfigAdvanceInfo configAdvance = this.jt.queryForObject( - "select gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema from config_info where data_id=? and group_id=? and tenant_id=?", - new Object[] { dataId, group, tenantTmp }, CONFIG_ADVANCE_INFO_ROW_MAPPER); - if (configTagList != null && !configTagList.isEmpty()) { - StringBuilder configTagsTmp = new StringBuilder(); - for (String configTag : configTagList) { - if (configTagsTmp.length() == 0) { - configTagsTmp.append(configTag); - } else { - configTagsTmp.append(",").append(configTag); - } - } - configAdvance.setConfigTags(configTagsTmp.toString()); - } - return configAdvance; - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - /** - * 查询配置信息;数据库原子操作,最小sql动作,无业务封装 - * @param dataId dataId - * @param group group - * @param tenant tenant - * @return advance info - */ - public ConfigAllInfo findConfigAllInfo(final String dataId, final String group, final String tenant) { - final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - try { - List configTagList = this.selectTagByConfig(dataId, group, tenant); - ConfigAllInfo configAdvance = this.jt.queryForObject( - "select ID,data_id,group_id,tenant_id,app_name,content,md5,gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema from config_info where data_id=? and group_id=? and tenant_id=?", - new Object[] { dataId, group, tenantTmp }, CONFIG_ALL_INFO_ROW_MAPPER); - if (configTagList != null && !configTagList.isEmpty()) { - StringBuilder configTagsTmp = new StringBuilder(); - for (String configTag : configTagList) { - if (configTagsTmp.length() == 0) { - configTagsTmp.append(configTag); - } else { - configTagsTmp.append(",").append(configTag); - } - } - configAdvance.setConfigTags(configTagsTmp.toString()); - } - return configAdvance; - } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null - return null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + if (!StringUtils.isBlank(appName)) { + where += " and app_name = ? "; + params.add(appName); + } + if (startTime != null) { + where += " and gmt_modified >=? "; + params.add(startTime); + } + if (endTime != null) { + where += " and gmt_modified <=? "; + params.add(endTime); + } - /** - * 更新变更记录;数据库原子操作,最小sql动作,无业务封装 - * @param id id - * @param configInfo config info - * @param srcIp ip - * @param srcUser user - * @param time time - * @param ops ops type - */ - private void insertConfigHistoryAtomic(long id, ConfigInfo configInfo, String srcIp, String srcUser, - final Timestamp time, String ops) { - String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); - String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); - final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent()); - try { - jt.update( - "insert into his_config_info (id,data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_modified,op_type) values(?,?,?,?,?,?,?,?,?,?,?)", - id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), - md5Tmp, srcIp, srcUser, time, ops); - } catch (DataAccessException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + PaginationHelper helper = new PaginationHelper(); + try { + return helper.fetchPage(jt, sqlCountRows + where, sqlFetchRows + where, params.toArray(), pageNo, pageSize, + lastMaxId, CONFIG_INFO_WRAPPER_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - /** - * list配置的历史变更记录 - * - * @param dataId - * data Id - * @param group - * group - * @param tenant - * tenant - * @param pageNo - * no - * @param pageSize - * size - * @return history info - */ - public Page findConfigHistory(String dataId, String group, String tenant, int pageNo, int pageSize) { - PaginationHelper helper = new PaginationHelper(); - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; - String sqlCountRows = "select count(*) from his_config_info where data_id = ? and group_id = ? and tenant_id = ?"; - String sqlFetchRows = "select nid,data_id,group_id,tenant_id,app_name,src_ip,op_type,gmt_create,gmt_modified from his_config_info where data_id = ? and group_id = ? and tenant_id = ? order by nid desc"; + public List findDeletedConfig(final Timestamp startTime, + final Timestamp endTime) { + try { + List> list = jt + .queryForList( + "SELECT DISTINCT data_id, group_id, tenant_id FROM his_config_info WHERE op_type = 'D' AND " + + "gmt_modified >=? AND gmt_modified <= ?", + new Object[] {startTime, endTime}); + return convertDeletedConfig(list); + } catch (DataAccessException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } - Page page = null; - try { - page = helper.fetchPage(this.jt, sqlCountRows, sqlFetchRows, new Object[] { dataId, group, tenantTmp }, pageNo, - pageSize, HISTORY_LIST_ROW_MAPPER); - } catch (DataAccessException e) { - fatalLog.error("[list-config-history] error, dataId:{}, group:{}", new Object[] { dataId, group }, e); - throw e; - } - return page; - } + /** + * 增加配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param srcIp ip + * @param srcUser user + * @param configInfo info + * @param time time + * @param configAdvanceInfo advance info + * @return excute sql result + */ + private long addConfigInfoAtomic(final String srcIp, final String srcUser, final ConfigInfo configInfo, + final Timestamp time, + Map configAdvanceInfo) { + final String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY + : configInfo.getAppName(); + final String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY + : configInfo.getTenant(); - /** - * 增加配置;数据库原子操作,最小sql动作,无业务封装 - * @param dataId dataId - * @param group group - * @param appName appName - * @param date date - */ - private void addConfigSubAtomic(final String dataId, final String group, final String appName, - final Timestamp date) { - final String appNameTmp = appName == null ? "" : appName; - try { - jt.update( - "insert into app_configdata_relation_subs(data_id,group_id,app_name,gmt_modified) values(?,?,?,?)", - dataId, group, appNameTmp, date); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } + final String desc = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("desc"); + final String use = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("use"); + final String effect = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("effect"); + final String type = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("type"); + final String schema = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("schema"); - /** - * 更新配置;数据库原子操作,最小sql动作,无业务封装 - * - * @param dataId - * data Id - * @param group - * group - * @param appName - * app name - * @param time - * time - */ - private void updateConfigSubAtomic(final String dataId, final String group, final String appName, - final Timestamp time) { - final String appNameTmp = appName == null ? "" : appName; - try { - jt.update( - "update app_configdata_relation_subs set gmt_modified=? where data_id=? and group_id=? and app_name=?", - time, dataId, group, appNameTmp); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error] " + e.toString(), e); - throw e; - } - } - - public ConfigHistoryInfo detailConfigHistory(Long nid) { - String sqlFetchRows = "select nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified from his_config_info where nid = ?"; - try { - ConfigHistoryInfo historyInfo = jt.queryForObject(sqlFetchRows, new Object[] { nid }, - HISTORY_DETAIL_ROW_MAPPER); - return historyInfo; - } catch (DataAccessException e) { - fatalLog.error("[list-config-history] error, nid:{}", new Object[] { nid }, e); - throw e; - } - } + final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent()); - private List convertDeletedConfig(List> list) { - List configs = new ArrayList(); - for (Map map : list) { - String dataId = (String) map.get("data_id"); - String group = (String) map.get("group_id"); - String tenant = (String) map.get("tenant_id"); - ConfigInfo config = new ConfigInfo(); - config.setDataId(dataId); - config.setGroup(group); - config.setTenant(tenant); - configs.add(config); - } - return configs; - } - - private List convertChangeConfig( - List> list) { - List configs = new ArrayList(); - for (Map map : list) { - String dataId = (String) map.get("data_id"); - String group = (String) map.get("group_id"); - String tenant = (String) map.get("tenant_id"); - String content = (String) map.get("content"); - long mTime = ((Timestamp)map.get("gmt_modified")).getTime(); - ConfigInfoWrapper config = new ConfigInfoWrapper(); - config.setDataId(dataId); - config.setGroup(group); - config.setTenant(tenant); - config.setContent(content); - config.setLastModified(mTime); - configs.add(config); - } - return configs; - } + KeyHolder keyHolder = new GeneratedKeyHolder(); - /** - * 获取所有的配置的Md5值,通过分页方式获取。 - * - * @return - */ + final String sql + = "INSERT INTO config_info(data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_create," + + "gmt_modified,c_desc,c_use,effect,type,c_schema) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + + try { + jt.update(new PreparedStatementCreator() { + @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", + "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"}, + justification = "findbugs does not trust jdbctemplate, sql is constant in practice") + public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + ps.setString(1, configInfo.getDataId()); + ps.setString(2, configInfo.getGroup()); + ps.setString(3, tenantTmp); + ps.setString(4, appNameTmp); + ps.setString(5, configInfo.getContent()); + ps.setString(6, md5Tmp); + ps.setString(7, srcIp); + ps.setString(8, srcUser); + ps.setTimestamp(9, time); + ps.setTimestamp(10, time); + ps.setString(11, desc); + ps.setString(12, use); + ps.setString(13, effect); + ps.setString(14, type); + ps.setString(15, schema); + return ps; + } + }, keyHolder); + Number nu = keyHolder.getKey(); + if (nu == null) { + throw new IllegalArgumentException("insert config_info fail"); + } + return nu.longValue(); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 增加配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param configId id + * @param tagName tag + * @param dataId data id + * @param group group + * @param tenant tenant + */ + public void addConfiTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant) { + try { + jt.update( + "INSERT INTO config_tags_relation(id,tag_name,tag_type,data_id,group_id,tenant_id) VALUES(?,?,?,?,?,?)", + configId, tagName, null, dataId, group, tenant); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 增加配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param configId config id + * @param configTags tags + * @param dataId dataId + * @param group group + * @param tenant tenant + */ + public void addConfiTagsRelationAtomic(long configId, String configTags, String dataId, String group, + String tenant) { + if (StringUtils.isNotBlank(configTags)) { + String[] tagArr = configTags.split(","); + for (String tag : tagArr) { + addConfiTagRelationAtomic(configId, tag, dataId, group, tenant); + } + } + } + + public void removeTagByIdAtomic(long id) { + try { + jt.update("DELETE FROM config_tags_relation WHERE id=?", id); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public List getConfigTagsByTenant(String tenant) { + String sql = "SELECT tag_name FROM config_tags_relation WHERE tenant_id = ? "; + try { + return jt.queryForList(sql, new Object[] {tenant}, String.class); + } catch (EmptyResultDataAccessException e) { + return null; + } catch (IncorrectResultSizeDataAccessException e) { + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public List selectTagByConfig(String dataId, String group, String tenant) { + String sql = "SELECT tag_name FROM config_tags_relation WHERE data_id=? AND group_id=? AND tenant_id = ? "; + try { + return jt.queryForList(sql, new Object[] {dataId, group, tenant}, String.class); + } catch (EmptyResultDataAccessException e) { + return null; + } catch (IncorrectResultSizeDataAccessException e) { + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 删除配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId dataId + * @param group group + * @param tenant tenant + * @param srcIp ip + * @param srcUser user + */ + private void removeConfigInfoAtomic(final String dataId, final String group, final String tenant, + final String srcIp, + final String srcUser) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + jt.update("DELETE FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", dataId, group, + tenantTmp); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 删除配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId dataId + * @param group group + * @param tenant tenant + * @param tag tag + * @param srcIp ip + * @param srcUser user + */ + public void removeConfigInfoTag(final String dataId, final String group, final String tenant, final String tag, + final String srcIp, + final String srcUser) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag; + try { + jt.update("DELETE FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?", dataId, + group, + tenantTmp, tagTmp); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 更新配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param configInfo config info + * @param srcIp ip + * @param srcUser user + * @param time time + * @param configAdvanceInfo advance info + */ + private void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, + final Timestamp time, Map configAdvanceInfo) { + String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); + String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); + final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent()); + String desc = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("desc"); + String use = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("use"); + String effect = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("effect"); + String type = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("type"); + String schema = configAdvanceInfo == null ? null : (String)configAdvanceInfo.get("schema"); + + try { + jt.update( + "UPDATE config_info SET content=?, md5 = ?, src_ip=?,src_user=?,gmt_modified=?,app_name=?,c_desc=?,c_use=?,effect=?,type=?,c_schema=? WHERE data_id=? AND group_id=? AND tenant_id=?", + configInfo.getContent(), md5Tmp, srcIp, srcUser, time, appNameTmp, desc, use, effect, type, schema, + configInfo.getDataId(), configInfo.getGroup(), tenantTmp); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 查询配置信息;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId dataId + * @param group group + * @param tenant tenant + * @return config info + */ + public ConfigInfo findConfigInfo(final String dataId, final String group, final String tenant) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + return this.jt.queryForObject( + "SELECT ID,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_ROW_MAPPER); + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 查询配置信息;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId dataId + * @param group group + * @param tenant tenant + * @return advance info + */ + public ConfigAdvanceInfo findConfigAdvanceInfo(final String dataId, final String group, final String tenant) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + List configTagList = this.selectTagByConfig(dataId, group, tenant); + ConfigAdvanceInfo configAdvance = this.jt.queryForObject( + "SELECT gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + new Object[] {dataId, group, tenantTmp}, CONFIG_ADVANCE_INFO_ROW_MAPPER); + if (configTagList != null && !configTagList.isEmpty()) { + StringBuilder configTagsTmp = new StringBuilder(); + for (String configTag : configTagList) { + if (configTagsTmp.length() == 0) { + configTagsTmp.append(configTag); + } else { + configTagsTmp.append(",").append(configTag); + } + } + configAdvance.setConfigTags(configTagsTmp.toString()); + } + return configAdvance; + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 查询配置信息;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId dataId + * @param group group + * @param tenant tenant + * @return advance info + */ + public ConfigAllInfo findConfigAllInfo(final String dataId, final String group, final String tenant) { + final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + try { + List configTagList = this.selectTagByConfig(dataId, group, tenant); + ConfigAllInfo configAdvance = this.jt.queryForObject( + "SELECT ID,data_id,group_id,tenant_id,app_name,content,md5,gmt_create,gmt_modified,src_user,src_ip,c_desc,c_use,effect,type,c_schema FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + new Object[] {dataId, group, tenantTmp}, CONFIG_ALL_INFO_ROW_MAPPER); + if (configTagList != null && !configTagList.isEmpty()) { + StringBuilder configTagsTmp = new StringBuilder(); + for (String configTag : configTagList) { + if (configTagsTmp.length() == 0) { + configTagsTmp.append(configTag); + } else { + configTagsTmp.append(",").append(configTag); + } + } + configAdvance.setConfigTags(configTagsTmp.toString()); + } + return configAdvance; + } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null + return null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 更新变更记录;数据库原子操作,最小sql动作,无业务封装 + * + * @param id id + * @param configInfo config info + * @param srcIp ip + * @param srcUser user + * @param time time + * @param ops ops type + */ + private void insertConfigHistoryAtomic(long id, ConfigInfo configInfo, String srcIp, String srcUser, + final Timestamp time, String ops) { + String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName(); + String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); + final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent()); + try { + jt.update( + "INSERT INTO his_config_info (id,data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_modified,op_type) VALUES(?,?,?,?,?,?,?,?,?,?,?)", + id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), + md5Tmp, srcIp, srcUser, time, ops); + } catch (DataAccessException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * list配置的历史变更记录 + * + * @param dataId data Id + * @param group group + * @param tenant tenant + * @param pageNo no + * @param pageSize size + * @return history info + */ + public Page findConfigHistory(String dataId, String group, String tenant, int pageNo, + int pageSize) { + PaginationHelper helper = new PaginationHelper(); + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + String sqlCountRows + = "select count(*) from his_config_info where data_id = ? and group_id = ? and tenant_id = ?"; + String sqlFetchRows + = "select nid,data_id,group_id,tenant_id,app_name,src_ip,op_type,gmt_create,gmt_modified from his_config_info where data_id = ? and group_id = ? and tenant_id = ? order by nid desc"; + + Page page = null; + try { + page = helper.fetchPage(this.jt, sqlCountRows, sqlFetchRows, new Object[] {dataId, group, tenantTmp}, + pageNo, + pageSize, HISTORY_LIST_ROW_MAPPER); + } catch (DataAccessException e) { + fatalLog.error("[list-config-history] error, dataId:{}, group:{}", new Object[] {dataId, group}, e); + throw e; + } + return page; + } + + /** + * 增加配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId dataId + * @param group group + * @param appName appName + * @param date date + */ + private void addConfigSubAtomic(final String dataId, final String group, final String appName, + final Timestamp date) { + final String appNameTmp = appName == null ? "" : appName; + try { + jt.update( + "INSERT INTO app_configdata_relation_subs(data_id,group_id,app_name,gmt_modified) VALUES(?,?,?,?)", + dataId, group, appNameTmp, date); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * 更新配置;数据库原子操作,最小sql动作,无业务封装 + * + * @param dataId data Id + * @param group group + * @param appName app name + * @param time time + */ + private void updateConfigSubAtomic(final String dataId, final String group, final String appName, + final Timestamp time) { + final String appNameTmp = appName == null ? "" : appName; + try { + jt.update( + "UPDATE app_configdata_relation_subs SET gmt_modified=? WHERE data_id=? AND group_id=? AND app_name=?", + time, dataId, group, appNameTmp); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public ConfigHistoryInfo detailConfigHistory(Long nid) { + String sqlFetchRows + = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified FROM his_config_info WHERE nid = ?"; + try { + ConfigHistoryInfo historyInfo = jt.queryForObject(sqlFetchRows, new Object[] {nid}, + HISTORY_DETAIL_ROW_MAPPER); + return historyInfo; + } catch (DataAccessException e) { + fatalLog.error("[list-config-history] error, nid:{}", new Object[] {nid}, e); + throw e; + } + } + + /** + * insert tenant info + * + * @param kp kp + * @param tenantId tenant Id + * @param tenantName tenant name + * @param tenantDesc tenant description + * @param time time + */ + public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc, + String createResoure, final long time) { + try { + jt.update( + "INSERT INTO tenant_info(kp,tenant_id,tenant_name,tenant_desc,create_source,gmt_create,gmt_modified) VALUES(?,?,?,?,?,?,?)", + kp, tenantId, tenantName, tenantDesc, createResoure, time, time); + } catch (DataAccessException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + /** + * Update tenantInfo showname + * + * @param kp kp + * @param tenantId tenant Id + * @param tenantName tenant name + * @param tenantDesc tenant description + */ + public void updateTenantNameAtomic(String kp, String tenantId, String tenantName, String tenantDesc) { + try { + jt.update( + "UPDATE tenant_info SET tenant_name = ?, tenant_desc = ?, gmt_modified= ? WHERE kp=? AND tenant_id=?", + tenantName, tenantDesc, System.currentTimeMillis(), kp, tenantId); + } catch (DataAccessException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + public List findTenantByKp(String kp) { + String sql = "SELECT tenant_id,tenant_name,tenant_desc FROM tenant_info WHERE kp=?"; + try { + return this.jt.query(sql, new Object[] {kp}, TENANT_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } catch (EmptyResultDataAccessException e) { + return Collections.emptyList(); + } catch (Exception e) { + fatalLog.error("[db-other-error]" + e.getMessage(), e); + throw new RuntimeException(e); + } + } + + public TenantInfo findTenantByKp(String kp, String tenantId) { + String sql = "SELECT tenant_id,tenant_name,tenant_desc FROM tenant_info WHERE kp=? AND tenant_id=?"; + try { + return this.jt.queryForObject(sql, new Object[] {kp, tenantId}, TENANT_INFO_ROW_MAPPER); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } catch (EmptyResultDataAccessException e) { + return null; + } catch (Exception e) { + fatalLog.error("[db-other-error]" + e.getMessage(), e); + throw new RuntimeException(e); + } + } + + public void removeTenantInfoAtomic(final String kp, final String tenantId) { + try { + jt.update("DELETE FROM tenant_info WHERE kp=? AND tenant_id=?", kp, tenantId); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error] " + e.toString(), e); + throw e; + } + } + + private List convertDeletedConfig(List> list) { + List configs = new ArrayList(); + for (Map map : list) { + String dataId = (String)map.get("data_id"); + String group = (String)map.get("group_id"); + String tenant = (String)map.get("tenant_id"); + ConfigInfo config = new ConfigInfo(); + config.setDataId(dataId); + config.setGroup(group); + config.setTenant(tenant); + configs.add(config); + } + return configs; + } + + private List convertChangeConfig( + List> list) { + List configs = new ArrayList(); + for (Map map : list) { + String dataId = (String)map.get("data_id"); + String group = (String)map.get("group_id"); + String tenant = (String)map.get("tenant_id"); + String content = (String)map.get("content"); + long mTime = ((Timestamp)map.get("gmt_modified")).getTime(); + ConfigInfoWrapper config = new ConfigInfoWrapper(); + config.setDataId(dataId); + config.setGroup(group); + config.setTenant(tenant); + config.setContent(content); + config.setLastModified(mTime); + configs.add(config); + } + return configs; + } + + /** + * 获取所有的配置的Md5值,通过分页方式获取。 + * + * @return + */ public List listAllGroupKeyMd5() { - final int pageSize = 10000; - int totalCount = configInfoCount(); - int pageCount = (int) Math.ceil(totalCount * 1.0 / pageSize); - List allConfigInfo = new ArrayList(); - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - List configInfoList = listGroupKeyMd5ByPage(pageNo, pageSize); - allConfigInfo.addAll(configInfoList); - } - return allConfigInfo; + final int pageSize = 10000; + int totalCount = configInfoCount(); + int pageCount = (int)Math.ceil(totalCount * 1.0 / pageSize); + List allConfigInfo = new ArrayList(); + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + List configInfoList = listGroupKeyMd5ByPage(pageNo, pageSize); + allConfigInfo.addAll(configInfoList); + } + return allConfigInfo; } private List listGroupKeyMd5ByPage(int pageNo, int pageSize) { String sqlCountRows = " SELECT COUNT(*) FROM config_info "; - String sqlFetchRows = " SELECT t.id,data_id,group_id,tenant_id,app_name,md5,gmt_modified FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t WHERE g.id = t.id"; + String sqlFetchRows + = " SELECT t.id,data_id,group_id,tenant_id,app_name,md5,gmt_modified FROM ( SELECT id FROM config_info ORDER BY id LIMIT ?,? ) g, config_info t WHERE g.id = t.id"; PaginationHelper helper = new PaginationHelper(); try { Page page = helper.fetchPageLimit(jt, sqlCountRows, sqlFetchRows, new Object[] { - (pageNo - 1) * pageSize, pageSize }, pageNo, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); + (pageNo - 1) * pageSize, pageSize}, pageNo, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); return page.getPageItems(); } catch (CannotGetJdbcConnectionException e) { @@ -3041,23 +3180,22 @@ public class PersistService { throw e; } } - - - private String generateLikeArgument(String s) { - if (s.indexOf(PATTERN_STR) >= 0) - return s.replaceAll("\\*", "%"); - else { - return s; - } - } - - public ConfigInfoWrapper queryConfigInfo(final String dataId, final String group, final String tenant) { - String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; + + private String generateLikeArgument(String s) { + String fuzzySearchSign = "\\*"; + String sqlLikePercentSign = "%"; + if (s.contains(PATTERN_STR)) { return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); } else { + return s; + } + } + + public ConfigInfoWrapper queryConfigInfo(final String dataId, final String group, final String tenant) { + String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { return this.jt - .queryForObject( - "select ID,data_id,group_id,tenant_id,app_name,content,gmt_modified,md5 from config_info where data_id=? and group_id=? and tenant_id=?", - new Object[] { dataId, group, tenantTmp }, CONFIG_INFO_WRAPPER_ROW_MAPPER); + .queryForObject( + "SELECT ID,data_id,group_id,tenant_id,app_name,content,gmt_modified,md5 FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", + new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { return null; } catch (CannotGetJdbcConnectionException e) { @@ -3065,98 +3203,100 @@ public class PersistService { throw e; } } - - public boolean isExistTable(String tableName) - { - String sql = "SELECT COUNT(*) FROM " +tableName; - try { - jt.queryForObject(sql, Integer.class); - return true; - } catch (Throwable e) { - return false; - } - } - - public Boolean completeMd5() { - defaultLog.info("[start completeMd5]"); - int perPageSize = 1000; - int rowCount = configInfoCount(); - int pageCount = (int) Math.ceil(rowCount * 1.0 / perPageSize); - int actualRowCount = 0; - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - Page page = findAllConfigInfoForDumpAll( - pageNo, perPageSize); - if (page != null) { - for (PersistService.ConfigInfoWrapper cf : page.getPageItems()) { - String md5InDb = cf.getMd5(); - final String content = cf.getContent(); - final String tenant = cf.getTenant(); - final String md5 = MD5.getInstance().getMD5String( - content); - if (StringUtils.isBlank(md5InDb)) { - try { - updateMd5(cf.getDataId(), cf.getGroup(), tenant, md5, new Timestamp(cf.getLastModified())); - } catch (Exception e) { - LogUtil.defaultLog - .error("[completeMd5-error] datId:{} group:{} lastModified:{}", - new Object[] { - cf.getDataId(), - cf.getGroup(), - new Timestamp(cf - .getLastModified()) }); - } - } else - { - if (!md5InDb.equals(md5)) { - try { - updateMd5(cf.getDataId(), cf.getGroup(), tenant, md5, new Timestamp(cf.getLastModified())); - } catch (Exception e) { - LogUtil.defaultLog.error("[completeMd5-error] datId:{} group:{} lastModified:{}", - new Object[] { cf.getDataId(), cf.getGroup(), - new Timestamp(cf.getLastModified()) }); - } - } - } - } - actualRowCount += page.getPageItems().size(); - defaultLog.info("[completeMd5] {} / {}", actualRowCount, rowCount); - } - } - return true; - } - - static final ConfigInfoWrapperRowMapper CONFIG_INFO_WRAPPER_ROW_MAPPER = new ConfigInfoWrapperRowMapper(); + public boolean isExistTable(String tableName) { + String sql = "SELECT COUNT(*) FROM " + tableName; + try { + jt.queryForObject(sql, Integer.class); + return true; + } catch (Throwable e) { + return false; + } + } - static final ConfigKeyRowMapper CONFIG_KEY_ROW_MAPPER = new ConfigKeyRowMapper(); + public Boolean completeMd5() { + defaultLog.info("[start completeMd5]"); + int perPageSize = 1000; + int rowCount = configInfoCount(); + int pageCount = (int)Math.ceil(rowCount * 1.0 / perPageSize); + int actualRowCount = 0; + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + Page page = findAllConfigInfoForDumpAll( + pageNo, perPageSize); + if (page != null) { + for (PersistService.ConfigInfoWrapper cf : page.getPageItems()) { + String md5InDb = cf.getMd5(); + final String content = cf.getContent(); + final String tenant = cf.getTenant(); + final String md5 = MD5.getInstance().getMD5String( + content); + if (StringUtils.isBlank(md5InDb)) { + try { + updateMd5(cf.getDataId(), cf.getGroup(), tenant, md5, new Timestamp(cf.getLastModified())); + } catch (Exception e) { + LogUtil.defaultLog + .error("[completeMd5-error] datId:{} group:{} lastModified:{}", + new Object[] { + cf.getDataId(), + cf.getGroup(), + new Timestamp(cf + .getLastModified())}); + } + } else { + if (!md5InDb.equals(md5)) { + try { + updateMd5(cf.getDataId(), cf.getGroup(), tenant, md5, + new Timestamp(cf.getLastModified())); + } catch (Exception e) { + LogUtil.defaultLog.error("[completeMd5-error] datId:{} group:{} lastModified:{}", + new Object[] {cf.getDataId(), cf.getGroup(), + new Timestamp(cf.getLastModified())}); + } + } + } + } - static final ConfigInfoBetaWrapperRowMapper CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER = new ConfigInfoBetaWrapperRowMapper(); - - static final ConfigInfoTagWrapperRowMapper CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER = new ConfigInfoTagWrapperRowMapper(); + actualRowCount += page.getPageItems().size(); + defaultLog.info("[completeMd5] {} / {}", actualRowCount, rowCount); + } + } + return true; + } - static final ConfigInfoRowMapper CONFIG_INFO_ROW_MAPPER = new ConfigInfoRowMapper(); - - static final ConfigAdvanceInfoRowMapper CONFIG_ADVANCE_INFO_ROW_MAPPER = new ConfigAdvanceInfoRowMapper(); - - static final ConfigAllInfoRowMapper CONFIG_ALL_INFO_ROW_MAPPER = new ConfigAllInfoRowMapper(); - - static final ConfigInfo4BetaRowMapper CONFIG_INFO4BETA_ROW_MAPPER = new ConfigInfo4BetaRowMapper(); - - static final ConfigInfo4TagRowMapper CONFIG_INFO4TAG_ROW_MAPPER = new ConfigInfo4TagRowMapper(); - - static final ConfigInfoBaseRowMapper CONFIG_INFO_BASE_ROW_MAPPER = new ConfigInfoBaseRowMapper(); + static final TenantInfoRowMapper TENANT_INFO_ROW_MAPPER = new TenantInfoRowMapper(); - static final ConfigInfoAggrRowMapper CONFIG_INFO_AGGR_ROW_MAPPER = new ConfigInfoAggrRowMapper(); + static final ConfigInfoWrapperRowMapper CONFIG_INFO_WRAPPER_ROW_MAPPER = new ConfigInfoWrapperRowMapper(); - static final ConfigInfoChangedRowMapper CONFIG_INFO_CHANGED_ROW_MAPPER = new ConfigInfoChangedRowMapper(); + static final ConfigKeyRowMapper CONFIG_KEY_ROW_MAPPER = new ConfigKeyRowMapper(); - static final ConfigHistoryRowMapper HISTORY_LIST_ROW_MAPPER = new ConfigHistoryRowMapper(); - - static final ConfigHistoryDetailRowMapper HISTORY_DETAIL_ROW_MAPPER = new ConfigHistoryDetailRowMapper(); + static final ConfigInfoBetaWrapperRowMapper CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER + = new ConfigInfoBetaWrapperRowMapper(); - private static String PATTERN_STR = "*"; - private final static int QUERY_LIMIT_SIZE = 50; - private JdbcTemplate jt; - private TransactionTemplate tjt; + static final ConfigInfoTagWrapperRowMapper CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER = new ConfigInfoTagWrapperRowMapper(); + + static final ConfigInfoRowMapper CONFIG_INFO_ROW_MAPPER = new ConfigInfoRowMapper(); + + static final ConfigAdvanceInfoRowMapper CONFIG_ADVANCE_INFO_ROW_MAPPER = new ConfigAdvanceInfoRowMapper(); + + static final ConfigAllInfoRowMapper CONFIG_ALL_INFO_ROW_MAPPER = new ConfigAllInfoRowMapper(); + + static final ConfigInfo4BetaRowMapper CONFIG_INFO4BETA_ROW_MAPPER = new ConfigInfo4BetaRowMapper(); + + static final ConfigInfo4TagRowMapper CONFIG_INFO4TAG_ROW_MAPPER = new ConfigInfo4TagRowMapper(); + + static final ConfigInfoBaseRowMapper CONFIG_INFO_BASE_ROW_MAPPER = new ConfigInfoBaseRowMapper(); + + static final ConfigInfoAggrRowMapper CONFIG_INFO_AGGR_ROW_MAPPER = new ConfigInfoAggrRowMapper(); + + static final ConfigInfoChangedRowMapper CONFIG_INFO_CHANGED_ROW_MAPPER = new ConfigInfoChangedRowMapper(); + + static final ConfigHistoryRowMapper HISTORY_LIST_ROW_MAPPER = new ConfigHistoryRowMapper(); + + static final ConfigHistoryDetailRowMapper HISTORY_DETAIL_ROW_MAPPER = new ConfigHistoryDetailRowMapper(); + + private static String PATTERN_STR = "*"; + private final static int QUERY_LIMIT_SIZE = 50; + private JdbcTemplate jt; + private TransactionTemplate tjt; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java index 5e849657e..1cd9f4bf9 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ServerListService.java @@ -15,25 +15,15 @@ */ package com.alibaba.nacos.config.server.service; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; - -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - -import javax.annotation.PostConstruct; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletResponse; - +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.service.notify.NotifyService; +import com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult; +import com.alibaba.nacos.config.server.utils.LogUtil; +import com.alibaba.nacos.config.server.utils.PropertyUtil; +import com.alibaba.nacos.config.server.utils.RunningConfigUtils; +import com.alibaba.nacos.config.server.utils.event.EventDispatcher; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; @@ -42,437 +32,447 @@ import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.service.notify.NotifyService; -import com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult; -import com.alibaba.nacos.config.server.utils.LogUtil; -import com.alibaba.nacos.config.server.utils.PropertyUtil; -import com.alibaba.nacos.config.server.utils.RunningConfigUtils; -import com.alibaba.nacos.config.server.utils.SystemConfig; -import com.alibaba.nacos.config.server.utils.event.EventDispatcher; +import javax.annotation.PostConstruct; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.StringReader; +import java.util.*; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.common.util.SystemUtils.readClusterConf; +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; /** * Serverlist service - * @author Nacos * + * @author Nacos */ @Service public class ServerListService implements ApplicationListener { - @Autowired + @Autowired private Environment env; - - @Autowired + + @Autowired private ServletContext servletContext; - - @Value("${server.port}") + private int port; - - - @PostConstruct - public void init() { - serverPort = System.getProperty("nacos.server.port", "8848"); - String envDomainName = System.getenv("address_server_domain"); - if (StringUtils.isBlank(envDomainName)) { - domainName = System.getProperty("address.server.domain", "jmenv.tbsite.net"); - } else { - domainName = envDomainName; - } - String envAddressPort = System.getenv("address_server_port"); - if (StringUtils.isBlank(envAddressPort)) { - addressPort = System.getProperty("address.server.port", "8080"); - } else { - addressPort = envAddressPort; - } - addressUrl = System.getProperty("address.server.url", - servletContext.getContextPath() + "/" + RunningConfigUtils.getClusterName()); - addressServerUrl = "http://" + domainName + ":" + addressPort + addressUrl; - envIdUrl = "http://" + domainName + ":" + addressPort + "/env"; - defaultLog.info("ServerListService address-server port:" + serverPort); - defaultLog.info("ADDRESS_SERVER_URL:" + addressServerUrl); - isHealthCheck = PropertyUtil.isHealthCheck(); - maxFailCount = PropertyUtil.getMaxHealthCheckFailCount(); - - try { - String val = null; - val = env.getProperty("useAddressServer"); - if (val != null && FALSE_STR.equals(val)) { - isUseAddressServer = false; - } - fatalLog.warn("useAddressServer:{}", isUseAddressServer); - } catch (Exception e) { - fatalLog.error("read application.properties wrong", e); - } - GetServerListTask task = new GetServerListTask(); - task.run(); - if (null == serverList || serverList.isEmpty()) { - fatalLog.error("########## cannot get serverlist, so exit."); - throw new RuntimeException("cannot get serverlist, so exit."); - } else { - TimerTaskService.scheduleWithFixedDelay(task, 0L, 5L, TimeUnit.SECONDS); - } - httpclient.start(); - CheckServerHealthTask checkServerHealthTask = new CheckServerHealthTask(); - TimerTaskService.scheduleWithFixedDelay(checkServerHealthTask, 0L, 5L, TimeUnit.SECONDS); - } + @PostConstruct + public void init() { + serverPort = System.getProperty("nacos.server.port", "8848"); + String envDomainName = System.getenv("address_server_domain"); + if (StringUtils.isBlank(envDomainName)) { + domainName = System.getProperty("address.server.domain", "jmenv.tbsite.net"); + } else { + domainName = envDomainName; + } + String envAddressPort = System.getenv("address_server_port"); + if (StringUtils.isBlank(envAddressPort)) { + addressPort = System.getProperty("address.server.port", "8080"); + } else { + addressPort = envAddressPort; + } + addressUrl = System.getProperty("address.server.url", + servletContext.getContextPath() + "/" + RunningConfigUtils.getClusterName()); + addressServerUrl = "http://" + domainName + ":" + addressPort + addressUrl; + envIdUrl = "http://" + domainName + ":" + addressPort + "/env"; - public String getEnvId() { - String envId = ""; - int i = 0; - do { - envId = getEnvIdHttp(); - if (StringUtils.isBlank(envId)) { - i++; - try { - Thread.sleep(500); - } catch (InterruptedException e) { - LogUtil.defaultLog.error("sleep interrupt"); - } - } - } while (StringUtils.isBlank(envId) && i < 5); + defaultLog.info("ServerListService address-server port:" + serverPort); + defaultLog.info("ADDRESS_SERVER_URL:" + addressServerUrl); + isHealthCheck = PropertyUtil.isHealthCheck(); + maxFailCount = PropertyUtil.getMaxHealthCheckFailCount(); - if (!StringUtils.isBlank(envId)) { - } else { - LogUtil.defaultLog.error("envId is blank"); - } - return envId; - } + try { + String val = null; + val = env.getProperty("useAddressServer"); + if (val != null && FALSE_STR.equals(val)) { + isUseAddressServer = false; + } + fatalLog.warn("useAddressServer:{}", isUseAddressServer); + } catch (Exception e) { + fatalLog.error("read application.properties wrong", e); + } + GetServerListTask task = new GetServerListTask(); + task.run(); + if (null == serverList || serverList.isEmpty()) { + fatalLog.error("########## cannot get serverlist, so exit."); + throw new RuntimeException("cannot get serverlist, so exit."); + } else { + TimerTaskService.scheduleWithFixedDelay(task, 0L, 5L, TimeUnit.SECONDS); + } + httpclient.start(); + CheckServerHealthTask checkServerHealthTask = new CheckServerHealthTask(); + TimerTaskService.scheduleWithFixedDelay(checkServerHealthTask, 0L, 5L, TimeUnit.SECONDS); + } - public List getServerList() { - return new ArrayList(serverList); - } + public String getEnvId() { + String envId = ""; + int i = 0; + do { + envId = getEnvIdHttp(); + if (StringUtils.isBlank(envId)) { + i++; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + LogUtil.defaultLog.error("sleep interrupt"); + } + } + } while (StringUtils.isBlank(envId) && i < 5); - public static void setServerList(List serverList) { - ServerListService.serverList = serverList; - } + if (!StringUtils.isBlank(envId)) { + } else { + LogUtil.defaultLog.error("envId is blank"); + } + return envId; + } - public static List getServerListUnhealth() { - return new ArrayList(serverListUnhealth); - } + public List getServerList() { + return new ArrayList(serverList); + } - public static Boolean isFirstIp() { - return serverList.get(0).contains(SystemConfig.LOCAL_IP); - } + public static void setServerList(List serverList) { + ServerListService.serverList = serverList; + } - public boolean isHealthCheck() { - return isHealthCheck; - } + public static List getServerListUnhealth() { + return new ArrayList(serverListUnhealth); + } - /** serverList has changed */ - static public class ServerlistChangeEvent implements EventDispatcher.Event {} - private void updateIfChanged(List newList) { - if (newList.isEmpty()) { - return; - } + public static Boolean isFirstIp() { + return serverList.get(0).contains(LOCAL_IP); + } - boolean isContainSelfIp = false; - for (String ipPortTmp : newList) { - if (ipPortTmp.contains(SystemConfig.LOCAL_IP)) { - isContainSelfIp = true; - break; - } - } + public boolean isHealthCheck() { + return isHealthCheck; + } - if (isContainSelfIp) { - isInIpList = true; - } else { - isInIpList = false; - String selfAddr = getFormatServerAddr(SystemConfig.LOCAL_IP); - newList.add(selfAddr); - fatalLog.error("########## [serverlist] self ip {} not in serverlist {}", selfAddr, newList); - } + /** + * serverList has changed + */ + static public class ServerlistChangeEvent implements EventDispatcher.Event {} - if (newList.equals(serverList)) { - return; - } + private void updateIfChanged(List newList) { + if (newList.isEmpty()) { + return; + } - serverList = new ArrayList(newList); + boolean isContainSelfIp = false; + for (String ipPortTmp : newList) { + if (ipPortTmp.contains(LOCAL_IP)) { + isContainSelfIp = true; + break; + } + } - List unhealthRemoved = new ArrayList(); - for (String unhealthIp : serverListUnhealth) { - if (!newList.contains(unhealthIp)) { - unhealthRemoved.add(unhealthIp); - } - } + if (isContainSelfIp) { + isInIpList = true; + } else { + isInIpList = false; + String selfAddr = getFormatServerAddr(LOCAL_IP); + newList.add(selfAddr); + fatalLog.error("########## [serverlist] self ip {} not in serverlist {}", selfAddr, newList); + } - serverListUnhealth.removeAll(unhealthRemoved); + if (newList.equals(serverList)) { + return; + } - List unhealthCountRemoved = new ArrayList(); - for (Map.Entry ip2UnhealthCountTmp : serverIp2unhealthCount.entrySet()) { - if (!newList.contains(ip2UnhealthCountTmp.getKey())) { - unhealthCountRemoved.add(ip2UnhealthCountTmp.getKey()); - } - } + serverList = new ArrayList(newList); - for (String unhealthCountTmp : unhealthCountRemoved) { - serverIp2unhealthCount.remove(unhealthCountTmp); - } + List unhealthRemoved = new ArrayList(); + for (String unhealthIp : serverListUnhealth) { + if (!newList.contains(unhealthIp)) { + unhealthRemoved.add(unhealthIp); + } + } - defaultLog.warn("[serverlist] updated to {}", serverList); + serverListUnhealth.removeAll(unhealthRemoved); - /** - * 非并发fireEvent - */ - EventDispatcher.fireEvent(new ServerlistChangeEvent()); - } + List unhealthCountRemoved = new ArrayList(); + for (Map.Entry ip2UnhealthCountTmp : serverIp2unhealthCount.entrySet()) { + if (!newList.contains(ip2UnhealthCountTmp.getKey())) { + unhealthCountRemoved.add(ip2UnhealthCountTmp.getKey()); + } + } - /** - * 保证不返回NULL - * - * @return serverlist - */ - private List getApacheServerList() { - // 优先从文件读取服务列表 - try { - List serverIps = new ArrayList(); - String serverIpsStr = DiskUtil.getServerList(); - if (!StringUtils.isBlank(serverIpsStr)) { - String split = System.getProperty("line.separator"); - String[] serverAddrArr = serverIpsStr.split(split); - for (String serverAddr : serverAddrArr) { - if (StringUtils.isNotBlank(serverAddr.trim())) { - serverIps.add(getFormatServerAddr(serverAddr)); - } - } - } - if (serverIps.size() > 0) { - return serverIps; - } - } catch (Exception e) { - defaultLog.error("nacos-XXXX", "[serverlist] failed to get serverlist from disk!", e); - } + for (String unhealthCountTmp : unhealthCountRemoved) { + serverIp2unhealthCount.remove(unhealthCountTmp); + } - if (isUseAddressServer() && !PropertyUtil.isStandaloneMode()) { - try { - HttpResult result = NotifyService.invokeURL(addressServerUrl, null, null); + defaultLog.warn("[serverlist] updated to {}", serverList); - if (HttpServletResponse.SC_OK == result.code) { - isAddressServerHealth = true; - addressServerFailCcount = 0; - List lines = IOUtils.readLines(new StringReader(result.content)); - List ips = new ArrayList(lines.size()); - for (String serverAddr : lines) { - if (null == serverAddr || serverAddr.trim().isEmpty()) { - continue; - } else { - ips.add(getFormatServerAddr(serverAddr)); - } - } - return ips; - } else { - addressServerFailCcount++; - if (addressServerFailCcount >= maxFailCount) { - isAddressServerHealth = false; - } - defaultLog.error("[serverlist] failed to get serverlist, error code {}", result.code); - return Collections.emptyList(); - } - } catch (IOException e) { - addressServerFailCcount++; - if (addressServerFailCcount >= maxFailCount) { - isAddressServerHealth = false; - } - defaultLog.error("[serverlist] exception, " + e.toString(), e); - return Collections.emptyList(); - } + /** + * 非并发fireEvent + */ + EventDispatcher.fireEvent(new ServerlistChangeEvent()); + } - } else { - List serverIps = new ArrayList(); - serverIps.add(getFormatServerAddr(SystemConfig.LOCAL_IP)); - return serverIps; - } - } + /** + * 保证不返回NULL + * + * @return serverlist + */ + private List getApacheServerList() { + if (STANDALONE_MODE) { + List serverIps = new ArrayList(); + serverIps.add(getFormatServerAddr(LOCAL_IP)); + return serverIps; + } - private String getFormatServerAddr(String serverAddr) { - if (StringUtils.isBlank(serverAddr)) { - throw new IllegalArgumentException("invalid serverlist"); - } - String[] ipPort = serverAddr.trim().split(":"); - String ip = ipPort[0].trim(); - if (ipPort.length == 1 && port != 0) { - return (ip + ":" + port); - } else { - return serverAddr; - } - } - - private String getEnvIdHttp() { - try { - // "http://jmenv.tbsite.net:8080/env"; - HttpResult result = NotifyService.invokeURL(envIdUrl, null, null); + // 优先从文件读取服务列表 + try { + List serverIps = new ArrayList(); + List serverAddrLines = readClusterConf(); + if (!CollectionUtils.isEmpty(serverAddrLines)) { + for (String serverAddr : serverAddrLines) { + if (StringUtils.isNotBlank(serverAddr.trim())) { + serverIps.add(getFormatServerAddr(serverAddr)); + } + } + } + if (serverIps.size() > 0) { + return serverIps; + } + } catch (Exception e) { + defaultLog.error("nacos-XXXX", "[serverlist] failed to get serverlist from disk!", e); + } - if (HttpServletResponse.SC_OK == result.code) { - return result.content.trim(); - } else { - defaultLog.error("[envId] failed to get envId, error code {}", result.code); - return ""; - } - } catch (IOException e) { - defaultLog.error("[envId] exception, " + e.toString(), e); - return ""; - } + if (isUseAddressServer()) { + try { + HttpResult result = NotifyService.invokeURL(addressServerUrl, null, null); - } + if (HttpServletResponse.SC_OK == result.code) { + isAddressServerHealth = true; + addressServerFailCcount = 0; + List lines = IOUtils.readLines(new StringReader(result.content)); + List ips = new ArrayList(lines.size()); + for (String serverAddr : lines) { + if (null == serverAddr || serverAddr.trim().isEmpty()) { + continue; + } else { + ips.add(getFormatServerAddr(serverAddr)); + } + } + return ips; + } else { + addressServerFailCcount++; + if (addressServerFailCcount >= maxFailCount) { + isAddressServerHealth = false; + } + defaultLog.error("[serverlist] failed to get serverlist, error code {}", result.code); + return Collections.emptyList(); + } + } catch (IOException e) { + addressServerFailCcount++; + if (addressServerFailCcount >= maxFailCount) { + isAddressServerHealth = false; + } + defaultLog.error("[serverlist] exception, " + e.toString(), e); + return Collections.emptyList(); + } - class GetServerListTask implements Runnable { - @Override - public void run() { - try { - updateIfChanged(getApacheServerList()); - } catch (Exception e) { - defaultLog.error("[serverlist] failed to get serverlist, " + e.toString(), e); - } - } - } + } else { + List serverIps = new ArrayList(); + serverIps.add(getFormatServerAddr(LOCAL_IP)); + return serverIps; + } + } - private void checkServerHealth() { - long startCheckTime = System.currentTimeMillis(); - for (String serverIp : serverList) { - // Compatible with old codes,use status.taobao - String url = "http://" + serverIp + servletContext.getContextPath() + Constants.HEALTH_CONTROLLER_PATH; - // "/nacos/health"; - HttpGet request = new HttpGet(url); - httpclient.execute(request, new AyscCheckServerHealthCallBack(serverIp)); - } - long endCheckTime = System.currentTimeMillis(); - long cost = endCheckTime - startCheckTime; - defaultLog.debug("checkServerHealth cost: {}", cost); - } + private String getFormatServerAddr(String serverAddr) { + if (StringUtils.isBlank(serverAddr)) { + throw new IllegalArgumentException("invalid serverlist"); + } + String[] ipPort = serverAddr.trim().split(":"); + String ip = ipPort[0].trim(); + if (ipPort.length == 1 && port != 0) { + return (ip + ":" + port); + } else { + return serverAddr; + } + } - class AyscCheckServerHealthCallBack implements FutureCallback { + private String getEnvIdHttp() { + try { + // "http://jmenv.tbsite.net:8080/env"; + HttpResult result = NotifyService.invokeURL(envIdUrl, null, null); - private String serverIp; + if (HttpServletResponse.SC_OK == result.code) { + return result.content.trim(); + } else { + defaultLog.error("[envId] failed to get envId, error code {}", result.code); + return ""; + } + } catch (IOException e) { + defaultLog.error("[envId] exception, " + e.toString(), e); + return ""; + } - public AyscCheckServerHealthCallBack(String serverIp) { - this.serverIp = serverIp; - } + } - @Override - public void completed(HttpResponse response) { - if (response.getStatusLine().getStatusCode() == HttpServletResponse.SC_OK) { - serverIp2unhealthCount.put(serverIp, 0); - if (serverListUnhealth.contains(serverIp)) { - serverListUnhealth.remove(serverIp); - } - HttpClientUtils.closeQuietly(response); - } - } + class GetServerListTask implements Runnable { + @Override + public void run() { + try { + updateIfChanged(getApacheServerList()); + } catch (Exception e) { + defaultLog.error("[serverlist] failed to get serverlist, " + e.toString(), e); + } + } + } - @Override - public void failed(Exception ex) { - Integer failCount = serverIp2unhealthCount.get(serverIp); - failCount = failCount == null ? Integer.valueOf(0) : failCount; - failCount++; - serverIp2unhealthCount.put(serverIp, failCount); - if (failCount > maxFailCount) { - if (!serverListUnhealth.contains(serverIp)) { - serverListUnhealth.add(serverIp); - } - defaultLog.error("unhealthIp:{}, unhealthCount:{}", serverIp, failCount); - } - } + private void checkServerHealth() { + long startCheckTime = System.currentTimeMillis(); + for (String serverIp : serverList) { + // Compatible with old codes,use status.taobao + String url = "http://" + serverIp + servletContext.getContextPath() + Constants.HEALTH_CONTROLLER_PATH; + // "/nacos/health"; + HttpGet request = new HttpGet(url); + httpclient.execute(request, new AyscCheckServerHealthCallBack(serverIp)); + } + long endCheckTime = System.currentTimeMillis(); + long cost = endCheckTime - startCheckTime; + defaultLog.debug("checkServerHealth cost: {}", cost); + } - @Override - public void cancelled() { - Integer failCount = serverIp2unhealthCount.get(serverIp); - failCount = failCount == null ? Integer.valueOf(0) : failCount; - failCount++; - serverIp2unhealthCount.put(serverIp, failCount); - if (failCount > maxFailCount) { - if (!serverListUnhealth.contains(serverIp)) { - serverListUnhealth.add(serverIp); - } - defaultLog.error("unhealthIp:{}, unhealthCount:{}", serverIp, failCount); - } - } - } + class AyscCheckServerHealthCallBack implements FutureCallback { - class CheckServerHealthTask implements Runnable{ + private String serverIp; - @Override - public void run() { - checkServerHealth(); - } + public AyscCheckServerHealthCallBack(String serverIp) { + this.serverIp = serverIp; + } - } + @Override + public void completed(HttpResponse response) { + if (response.getStatusLine().getStatusCode() == HttpServletResponse.SC_OK) { + serverIp2unhealthCount.put(serverIp, 0); + if (serverListUnhealth.contains(serverIp)) { + serverListUnhealth.remove(serverIp); + } + HttpClientUtils.closeQuietly(response); + } + } - private Boolean isUseAddressServer() { - return isUseAddressServer; - } + @Override + public void failed(Exception ex) { + Integer failCount = serverIp2unhealthCount.get(serverIp); + failCount = failCount == null ? Integer.valueOf(0) : failCount; + failCount++; + serverIp2unhealthCount.put(serverIp, failCount); + if (failCount > maxFailCount) { + if (!serverListUnhealth.contains(serverIp)) { + serverListUnhealth.add(serverIp); + } + defaultLog.error("unhealthIp:{}, unhealthCount:{}", serverIp, failCount); + } + } - static class CheckServerThreadFactory implements ThreadFactory { + @Override + public void cancelled() { + Integer failCount = serverIp2unhealthCount.get(serverIp); + failCount = failCount == null ? Integer.valueOf(0) : failCount; + failCount++; + serverIp2unhealthCount.put(serverIp, failCount); + if (failCount > maxFailCount) { + if (!serverListUnhealth.contains(serverIp)) { + serverListUnhealth.add(serverIp); + } + defaultLog.error("unhealthIp:{}, unhealthCount:{}", serverIp, failCount); + } + } + } - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "com.alibaba.nacos.CheckServerThreadFactory"); - thread.setDaemon(true); - return thread; - } - } + class CheckServerHealthTask implements Runnable { - public static boolean isAddressServerHealth() { - return isAddressServerHealth; - } + @Override + public void run() { + checkServerHealth(); + } - public static boolean isInIpList() { - return isInIpList; - } + } - // ========================== + private Boolean isUseAddressServer() { + return isUseAddressServer; + } - /** - * 和其他server的连接超时和socket超时 - */ - static final int TIMEOUT = 5000; - private int maxFailCount = 12; - private static volatile List serverList = new ArrayList(); - private static volatile List serverListUnhealth = new ArrayList(); - private static volatile boolean isAddressServerHealth = true; - private static volatile int addressServerFailCcount = 0; - private static volatile boolean isInIpList = true; + static class CheckServerThreadFactory implements ThreadFactory { - /** - * ip unhealth count - */ - private static volatile Map serverIp2unhealthCount = new HashMap(); - private RequestConfig requestConfig = RequestConfig.custom() - .setConnectTimeout(PropertyUtil.getNotifyConnectTimeout()) - .setSocketTimeout(PropertyUtil.getNotifySocketTimeout()).build(); + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "com.alibaba.nacos.CheckServerThreadFactory"); + thread.setDaemon(true); + return thread; + } + } - private CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig) - .build(); + public static boolean isAddressServerHealth() { + return isAddressServerHealth; + } - /** - * server之间通信的端口 - */ - public String serverPort; - public String domainName; - public String addressPort; - public String addressUrl; - public String envIdUrl; - public String addressServerUrl; - private Boolean isUseAddressServer = true; - private boolean isHealthCheck = true; - private final static String FALSE_STR = "false"; + public static boolean isInIpList() { + return isInIpList; + } + // ========================== - @Override - public void onApplicationEvent(WebServerInitializedEvent event) { - if (port == 0) { - port = event.getWebServer().getPort(); - List newList = new ArrayList(); - for (String serverAddrTmp : serverList) { - newList.add(getFormatServerAddr(serverAddrTmp)); - } - setServerList(new ArrayList(newList)); - } - } + /** + * 和其他server的连接超时和socket超时 + */ + static final int TIMEOUT = 5000; + private int maxFailCount = 12; + private static volatile List serverList = new ArrayList(); + private static volatile List serverListUnhealth = new ArrayList(); + private static volatile boolean isAddressServerHealth = true; + private static volatile int addressServerFailCcount = 0; + private static volatile boolean isInIpList = true; + + /** + * ip unhealth count + */ + private static volatile Map serverIp2unhealthCount = new HashMap(); + private RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(PropertyUtil.getNotifyConnectTimeout()) + .setSocketTimeout(PropertyUtil.getNotifySocketTimeout()).build(); + + private CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig) + .build(); + + /** + * server之间通信的端口 + */ + public String serverPort; + public String domainName; + public String addressPort; + public String addressUrl; + public String envIdUrl; + public String addressServerUrl; + private Boolean isUseAddressServer = true; + private boolean isHealthCheck = true; + private final static String FALSE_STR = "false"; + + @Override + public void onApplicationEvent(WebServerInitializedEvent event) { + if (port == 0) { + port = event.getWebServer().getPort(); + List newList = new ArrayList(); + for (String serverAddrTmp : serverList) { + newList.add(getFormatServerAddr(serverAddrTmp)); + } + setServerList(new ArrayList(newList)); + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/SwitchService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/SwitchService.java index a0303c428..0c6d59a92 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/SwitchService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/SwitchService.java @@ -15,32 +15,32 @@ */ package com.alibaba.nacos.config.server.service; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.springframework.stereotype.Service; - import com.alibaba.nacos.config.server.utils.LogUtil; - -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; import java.util.Map; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; + /** * Switch + * * @author Nacos */ @Service public class SwitchService { - public static final String SWITCH_META_DATAID = "com.alibaba.nacos.meta.switch"; + public static final String SWITCH_META_DATAID = "com.alibaba.nacos.meta.switch"; public static final String FIXED_POLLING = "isFixedPolling"; public static final String FIXED_POLLING_INTERVAL = "fixedPollingInertval"; - + public static final String FIXED_DELAY_TIME = "fixedDelayTime"; - + public static final String DISABLE_APP_COLLECTOR = "disableAppCollector"; private static volatile Map switches = new HashMap(); @@ -49,7 +49,7 @@ public class SwitchService { boolean rtn = defaultValue; try { String value = switches.get(key); - rtn = value != null ? Boolean.valueOf(value).booleanValue() : defaultValue; + rtn = value != null ? Boolean.valueOf(value).booleanValue() : defaultValue; } catch (Exception e) { rtn = defaultValue; LogUtil.fatalLog.error("corrupt switch value {}={}", key, switches.get(key)); @@ -61,7 +61,7 @@ public class SwitchService { int rtn = defaultValue; try { String status = switches.get(key); - rtn = status != null ? Integer.parseInt(status) : defaultValue; + rtn = status != null ? Integer.parseInt(status) : defaultValue; } catch (Exception e) { rtn = defaultValue; LogUtil.fatalLog.error("corrupt switch value {}={}", key, switches.get(key)); @@ -69,12 +69,11 @@ public class SwitchService { return rtn; } - - public static String getSwitchString(String key, String defaultValue){ - String value = switches.get(key); - return StringUtils.isBlank(value) ? defaultValue : value ; + public static String getSwitchString(String key, String defaultValue) { + String value = switches.get(key); + return StringUtils.isBlank(value) ? defaultValue : value; } - + public static void load(String config) { if (StringUtils.isBlank(config)) { fatalLog.error("switch config is blank."); @@ -84,7 +83,7 @@ public class SwitchService { Map map = new HashMap(30); try { - for (String line : IOUtils.readLines(new StringReader(config))) { + for (String line : IOUtils.readLines(new StringReader(config))) { if (!StringUtils.isBlank(line) && !line.startsWith("#")) { String[] array = line.split("="); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/TimerTaskService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/TimerTaskService.java index 11b929f31..95d42d5dd 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/TimerTaskService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/TimerTaskService.java @@ -21,28 +21,28 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; - /** * 定时任务服务 + * * @author Nacos */ public class TimerTaskService { - @SuppressWarnings("PMD.ThreadPoolCreationRule") + @SuppressWarnings("PMD.ThreadPoolCreationRule") private static ScheduledExecutorService scheduledExecutorService = Executors - .newScheduledThreadPool(10, new ThreadFactory() { - AtomicInteger count = new AtomicInteger(0); + .newScheduledThreadPool(10, new ThreadFactory() { + AtomicInteger count = new AtomicInteger(0); - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("com.alibaba.nacos.server.Timer-" + count.getAndIncrement()); - return t; - } - }); + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("com.alibaba.nacos.server.Timer-" + count.getAndIncrement()); + return t; + } + }); static public void scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, - TimeUnit unit) { + TimeUnit unit) { scheduledExecutorService.scheduleWithFixedDelay(command, initialDelay, delay, unit); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java index a5b8a213e..c3630a44d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java @@ -20,13 +20,12 @@ import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; import com.alibaba.nacos.config.server.model.capacity.TenantCapacity; import com.alibaba.nacos.config.server.service.PersistService; -import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.LogUtil; +import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.ThreadFactoryBuilder; - -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -44,456 +43,457 @@ import java.util.concurrent.TimeUnit; /** * Capacity service + * * @author hexu.hxy * @date 2018/03/05 */ @Service public class CapacityService { - private static final Logger LOGGER = LoggerFactory.getLogger(CapacityService.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CapacityService.class); - private static final Integer ZERO = 0; - private static final int INIT_PAGE_SIZE = 500; + private static final Integer ZERO = 0; + private static final int INIT_PAGE_SIZE = 500; - @Autowired - private GroupCapacityPersistService groupCapacityPersistService; - @Autowired - private TenantCapacityPersistService tenantCapacityPersistService; - @Autowired - private PersistService persistService; + @Autowired + private GroupCapacityPersistService groupCapacityPersistService; + @Autowired + private TenantCapacityPersistService tenantCapacityPersistService; + @Autowired + private PersistService persistService; - private ScheduledExecutorService scheduledExecutorService; + private ScheduledExecutorService scheduledExecutorService; - @PostConstruct - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public void init() { - // 每个Server都有修正usage的Job在跑,幂等 - ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat( - "com.alibaba.nacos.CapacityManagement-%d").setDaemon(true).build(); - scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(threadFactory); - scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { - @Override - public void run() { - LOGGER.info("[capacityManagement] start correct usage"); - Stopwatch stopwatch = Stopwatch.createStarted(); - correctUsage(); - LOGGER.info("[capacityManagement] end correct usage, cost: {}s", stopwatch.elapsed(TimeUnit.SECONDS)); + @PostConstruct + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public void init() { + // 每个Server都有修正usage的Job在跑,幂等 + ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat( + "com.alibaba.nacos.CapacityManagement-%d").setDaemon(true).build(); + scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(threadFactory); + scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + LOGGER.info("[capacityManagement] start correct usage"); + Stopwatch stopwatch = Stopwatch.createStarted(); + correctUsage(); + LOGGER.info("[capacityManagement] end correct usage, cost: {}s", stopwatch.elapsed(TimeUnit.SECONDS)); - } - }, PropertyUtil.getCorrectUsageDelay(), PropertyUtil.getCorrectUsageDelay(), TimeUnit.SECONDS); - } + } + }, PropertyUtil.getCorrectUsageDelay(), PropertyUtil.getCorrectUsageDelay(), TimeUnit.SECONDS); + } - @PreDestroy - public void destroy() { - scheduledExecutorService.shutdown(); - } + @PreDestroy + public void destroy() { + scheduledExecutorService.shutdown(); + } - public void correctUsage() { - correctGroupUsage(); - correctTenantUsage(); - } + public void correctUsage() { + correctGroupUsage(); + correctTenantUsage(); + } - public void correctGroupUsage(String group) { - groupCapacityPersistService.correctUsage(group, TimeUtils.getCurrentTime()); - } + public void correctGroupUsage(String group) { + groupCapacityPersistService.correctUsage(group, TimeUtils.getCurrentTime()); + } - public void correctTenantUsage(String tenant) { - tenantCapacityPersistService.correctUsage(tenant, TimeUtils.getCurrentTime()); - } + public void correctTenantUsage(String tenant) { + tenantCapacityPersistService.correctUsage(tenant, TimeUtils.getCurrentTime()); + } - public void initAllCapacity() { - initAllCapacity(false); - initAllCapacity(true); - } + public void initAllCapacity() { + initAllCapacity(false); + initAllCapacity(true); + } - private void initAllCapacity(boolean isTenant) { - int page = 1; - while (true) { - List list; - if (isTenant) { - list = persistService.getTenantIdList(page, INIT_PAGE_SIZE); - } else { - list = persistService.getGroupIdList(page, INIT_PAGE_SIZE); - } - for (String targetId : list) { - if (isTenant) { - insertTenantCapacity(targetId); - autoExpansion(null, targetId); - } else { - insertGroupCapacity(targetId); - autoExpansion(targetId, null); - } - } - if (list.size() < INIT_PAGE_SIZE) { - break; - } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // ignore - } - ++page; - } - } + private void initAllCapacity(boolean isTenant) { + int page = 1; + while (true) { + List list; + if (isTenant) { + list = persistService.getTenantIdList(page, INIT_PAGE_SIZE); + } else { + list = persistService.getGroupIdList(page, INIT_PAGE_SIZE); + } + for (String targetId : list) { + if (isTenant) { + insertTenantCapacity(targetId); + autoExpansion(null, targetId); + } else { + insertGroupCapacity(targetId); + autoExpansion(targetId, null); + } + } + if (list.size() < INIT_PAGE_SIZE) { + break; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignore + } + ++page; + } + } - /** - * 修正Group容量信息中的使用值(usage) - */ - private void correctGroupUsage() { - long lastId = 0; - int pageSize = 100; - while (true) { - List groupCapacityList = groupCapacityPersistService.getCapacityList4CorrectUsage(lastId, - pageSize); - if (groupCapacityList.isEmpty()) { - break; - } - lastId = groupCapacityList.get(groupCapacityList.size() - 1).getId(); - for (GroupCapacity groupCapacity : groupCapacityList) { - String group = groupCapacity.getGroup(); - groupCapacityPersistService.correctUsage(group, TimeUtils.getCurrentTime()); - } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // ignore - } - } - } + /** + * 修正Group容量信息中的使用值(usage) + */ + private void correctGroupUsage() { + long lastId = 0; + int pageSize = 100; + while (true) { + List groupCapacityList = groupCapacityPersistService.getCapacityList4CorrectUsage(lastId, + pageSize); + if (groupCapacityList.isEmpty()) { + break; + } + lastId = groupCapacityList.get(groupCapacityList.size() - 1).getId(); + for (GroupCapacity groupCapacity : groupCapacityList) { + String group = groupCapacity.getGroup(); + groupCapacityPersistService.correctUsage(group, TimeUtils.getCurrentTime()); + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignore + } + } + } - /** - * 修正Tenant容量信息中的使用值(usage) - */ - private void correctTenantUsage() { - long lastId = 0; - int pageSize = 100; - while (true) { - List tenantCapacityList = tenantCapacityPersistService.getCapacityList4CorrectUsage(lastId, - pageSize); - if (tenantCapacityList.isEmpty()) { - break; - } - lastId = tenantCapacityList.get(tenantCapacityList.size() - 1).getId(); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // ignore - } - for (TenantCapacity tenantCapacity : tenantCapacityList) { - String tenant = tenantCapacity.getTenant(); - tenantCapacityPersistService.correctUsage(tenant, TimeUtils.getCurrentTime()); - } - } - } + /** + * 修正Tenant容量信息中的使用值(usage) + */ + private void correctTenantUsage() { + long lastId = 0; + int pageSize = 100; + while (true) { + List tenantCapacityList = tenantCapacityPersistService.getCapacityList4CorrectUsage(lastId, + pageSize); + if (tenantCapacityList.isEmpty()) { + break; + } + lastId = tenantCapacityList.get(tenantCapacityList.size() - 1).getId(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignore + } + for (TenantCapacity tenantCapacity : tenantCapacityList) { + String tenant = tenantCapacity.getTenant(); + tenantCapacityPersistService.correctUsage(tenant, TimeUtils.getCurrentTime()); + } + } + } - /** - * 集群:1. 如果容量信息不存在,则初始化容量信息
2. 更新容量的使用量usage,加一或减一 - * - * @param counterMode 增加或者减少 - * @param ignoreQuotaLimit 是否忽略容量额度限制,在关闭容量管理的限制检验功能只计数的时候为true,开启容量管理的限制检验功能则为false - * @return 是否操作成功 - */ - public boolean insertAndUpdateClusterUsage(CounterMode counterMode, boolean ignoreQuotaLimit) { - Capacity capacity = groupCapacityPersistService.getClusterCapacity(); - if (capacity == null) { - insertGroupCapacity(GroupCapacityPersistService.CLUSTER); - } - return updateGroupUsage(counterMode, GroupCapacityPersistService.CLUSTER, - PropertyUtil.getDefaultClusterQuota(), ignoreQuotaLimit); - } + /** + * 集群:1. 如果容量信息不存在,则初始化容量信息
2. 更新容量的使用量usage,加一或减一 + * + * @param counterMode 增加或者减少 + * @param ignoreQuotaLimit 是否忽略容量额度限制,在关闭容量管理的限制检验功能只计数的时候为true,开启容量管理的限制检验功能则为false + * @return 是否操作成功 + */ + public boolean insertAndUpdateClusterUsage(CounterMode counterMode, boolean ignoreQuotaLimit) { + Capacity capacity = groupCapacityPersistService.getClusterCapacity(); + if (capacity == null) { + insertGroupCapacity(GroupCapacityPersistService.CLUSTER); + } + return updateGroupUsage(counterMode, GroupCapacityPersistService.CLUSTER, + PropertyUtil.getDefaultClusterQuota(), ignoreQuotaLimit); + } - public boolean updateClusterUsage(CounterMode counterMode) { - return updateGroupUsage(counterMode, GroupCapacityPersistService.CLUSTER, - PropertyUtil.getDefaultClusterQuota(), false); - } + public boolean updateClusterUsage(CounterMode counterMode) { + return updateGroupUsage(counterMode, GroupCapacityPersistService.CLUSTER, + PropertyUtil.getDefaultClusterQuota(), false); + } - /** - * 提供给关闭容量管理的限制检验功能时计数使用
Group:1. 如果容量信息不存在,则初始化容量信息
2. 更新容量的使用量usage,加一或减一 - * - * @param counterMode 增加或者减少 - * @param group group - * @param ignoreQuotaLimit 是否忽略容量额度限制,在关闭容量管理的限制检验功能只计数的时候为true,开启容量管理的限制检验功能则为false - * @return 是否操作成功 - */ - public boolean insertAndUpdateGroupUsage(CounterMode counterMode, String group, boolean ignoreQuotaLimit) { - GroupCapacity groupCapacity = getGroupCapacity(group); - if (groupCapacity == null) { - initGroupCapacity(group, null, null, null, null); - } - return updateGroupUsage(counterMode, group, PropertyUtil.getDefaultGroupQuota(), ignoreQuotaLimit); - } + /** + * 提供给关闭容量管理的限制检验功能时计数使用
Group:1. 如果容量信息不存在,则初始化容量信息
2. 更新容量的使用量usage,加一或减一 + * + * @param counterMode 增加或者减少 + * @param group group + * @param ignoreQuotaLimit 是否忽略容量额度限制,在关闭容量管理的限制检验功能只计数的时候为true,开启容量管理的限制检验功能则为false + * @return 是否操作成功 + */ + public boolean insertAndUpdateGroupUsage(CounterMode counterMode, String group, boolean ignoreQuotaLimit) { + GroupCapacity groupCapacity = getGroupCapacity(group); + if (groupCapacity == null) { + initGroupCapacity(group, null, null, null, null); + } + return updateGroupUsage(counterMode, group, PropertyUtil.getDefaultGroupQuota(), ignoreQuotaLimit); + } - public GroupCapacity getGroupCapacity(String group) { - return groupCapacityPersistService.getGroupCapacity(group); - } + public GroupCapacity getGroupCapacity(String group) { + return groupCapacityPersistService.getGroupCapacity(group); + } - public boolean updateGroupUsage(CounterMode counterMode, String group) { - return updateGroupUsage(counterMode, group, PropertyUtil.getDefaultGroupQuota(), false); - } + public boolean updateGroupUsage(CounterMode counterMode, String group) { + return updateGroupUsage(counterMode, group, PropertyUtil.getDefaultGroupQuota(), false); + } - /** - * 初始化该Group的容量信息,如果到达限额,将自动扩容,以降低运维成本 - */ - public boolean initGroupCapacity(String group) { - return initGroupCapacity(group, null, null, null, null); - } + /** + * 初始化该Group的容量信息,如果到达限额,将自动扩容,以降低运维成本 + */ + public boolean initGroupCapacity(String group) { + return initGroupCapacity(group, null, null, null, null); + } - /** - * 初始化该Group的容量信息,如果到达限额,将自动扩容,以降低运维成本 - */ - private boolean initGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, - Integer maxAggrSize) { - boolean insertSuccess = insertGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); - if (quota != null) { - return insertSuccess; - } - autoExpansion(group, null); - return insertSuccess; - } + /** + * 初始化该Group的容量信息,如果到达限额,将自动扩容,以降低运维成本 + */ + private boolean initGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, + Integer maxAggrSize) { + boolean insertSuccess = insertGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); + if (quota != null) { + return insertSuccess; + } + autoExpansion(group, null); + return insertSuccess; + } - /** - * 自动扩容 - */ - private void autoExpansion(String group, String tenant) { - Capacity capacity = getCapacity(group, tenant); - int defaultQuota = getDefaultQuota(tenant != null); - Integer usage = capacity.getUsage(); - if (usage < defaultQuota) { - return; - } - // 初始化的时候该Group/租户就已经到达限额,自动扩容,降低运维成本 - int initialExpansionPercent = PropertyUtil.getInitialExpansionPercent(); - if (initialExpansionPercent > 0) { - int finalQuota = (int)(usage + defaultQuota * (1.0 * initialExpansionPercent / 100)); - if (tenant != null) { - tenantCapacityPersistService.updateQuota(tenant, finalQuota); - LogUtil.defaultLog.warn("[capacityManagement] 初始化的时候该租户({})使用量({})就已经到达限额{},自动扩容到{}", tenant, - usage, defaultQuota, finalQuota); - } else { - groupCapacityPersistService.updateQuota(group, finalQuota); - LogUtil.defaultLog.warn("[capacityManagement] 初始化的时候该Group({})使用量({})就已经到达限额{},自动扩容到{}", group, - usage, defaultQuota, finalQuota); - } - } - } + /** + * 自动扩容 + */ + private void autoExpansion(String group, String tenant) { + Capacity capacity = getCapacity(group, tenant); + int defaultQuota = getDefaultQuota(tenant != null); + Integer usage = capacity.getUsage(); + if (usage < defaultQuota) { + return; + } + // 初始化的时候该Group/租户就已经到达限额,自动扩容,降低运维成本 + int initialExpansionPercent = PropertyUtil.getInitialExpansionPercent(); + if (initialExpansionPercent > 0) { + int finalQuota = (int)(usage + defaultQuota * (1.0 * initialExpansionPercent / 100)); + if (tenant != null) { + tenantCapacityPersistService.updateQuota(tenant, finalQuota); + LogUtil.defaultLog.warn("[capacityManagement] 初始化的时候该租户({})使用量({})就已经到达限额{},自动扩容到{}", tenant, + usage, defaultQuota, finalQuota); + } else { + groupCapacityPersistService.updateQuota(group, finalQuota); + LogUtil.defaultLog.warn("[capacityManagement] 初始化的时候该Group({})使用量({})就已经到达限额{},自动扩容到{}", group, + usage, defaultQuota, finalQuota); + } + } + } - private int getDefaultQuota(boolean isTenant) { - if (isTenant) { - return PropertyUtil.getDefaultTenantQuota(); - } - return PropertyUtil.getDefaultGroupQuota(); - } + private int getDefaultQuota(boolean isTenant) { + if (isTenant) { + return PropertyUtil.getDefaultTenantQuota(); + } + return PropertyUtil.getDefaultGroupQuota(); + } - public Capacity getCapacity(String group, String tenant) { - if (tenant != null) { - return getTenantCapacity(tenant); - } - return getGroupCapacity(group); - } + public Capacity getCapacity(String group, String tenant) { + if (tenant != null) { + return getTenantCapacity(tenant); + } + return getGroupCapacity(group); + } - public Capacity getCapacityWithDefault(String group, String tenant) { - Capacity capacity; - boolean isTenant = StringUtils.isNotBlank(tenant); - if (isTenant) { - capacity = getTenantCapacity(tenant); - } else { - capacity = getGroupCapacity(group); - } - if (capacity == null) { - return null; - } - Integer quota = capacity.getQuota(); - if (quota == 0) { - if (isTenant) { - capacity.setQuota(PropertyUtil.getDefaultTenantQuota()); - } else { - if (GroupCapacityPersistService.CLUSTER.equals(group)) { - capacity.setQuota(PropertyUtil.getDefaultClusterQuota()); - } else { - capacity.setQuota(PropertyUtil.getDefaultGroupQuota()); - } - } - } - Integer maxSize = capacity.getMaxSize(); - if (maxSize == 0) { - capacity.setMaxSize(PropertyUtil.getDefaultMaxSize()); - } - Integer maxAggrCount = capacity.getMaxAggrCount(); - if (maxAggrCount == 0) { - capacity.setMaxAggrCount(PropertyUtil.getDefaultMaxAggrCount()); - } - Integer maxAggrSize = capacity.getMaxAggrSize(); - if (maxAggrSize == 0) { - capacity.setMaxAggrSize(PropertyUtil.getDefaultMaxAggrSize()); - } - return capacity; - } + public Capacity getCapacityWithDefault(String group, String tenant) { + Capacity capacity; + boolean isTenant = StringUtils.isNotBlank(tenant); + if (isTenant) { + capacity = getTenantCapacity(tenant); + } else { + capacity = getGroupCapacity(group); + } + if (capacity == null) { + return null; + } + Integer quota = capacity.getQuota(); + if (quota == 0) { + if (isTenant) { + capacity.setQuota(PropertyUtil.getDefaultTenantQuota()); + } else { + if (GroupCapacityPersistService.CLUSTER.equals(group)) { + capacity.setQuota(PropertyUtil.getDefaultClusterQuota()); + } else { + capacity.setQuota(PropertyUtil.getDefaultGroupQuota()); + } + } + } + Integer maxSize = capacity.getMaxSize(); + if (maxSize == 0) { + capacity.setMaxSize(PropertyUtil.getDefaultMaxSize()); + } + Integer maxAggrCount = capacity.getMaxAggrCount(); + if (maxAggrCount == 0) { + capacity.setMaxAggrCount(PropertyUtil.getDefaultMaxAggrCount()); + } + Integer maxAggrSize = capacity.getMaxAggrSize(); + if (maxAggrSize == 0) { + capacity.setMaxAggrSize(PropertyUtil.getDefaultMaxAggrSize()); + } + return capacity; + } - public boolean initCapacity(String group, String tenant) { - if (StringUtils.isNotBlank(tenant)) { - return initTenantCapacity(tenant); - } - if (GroupCapacityPersistService.CLUSTER.equals(group)) { - return insertGroupCapacity(GroupCapacityPersistService.CLUSTER); - } - // Group会自动扩容 - return initGroupCapacity(group); - } + public boolean initCapacity(String group, String tenant) { + if (StringUtils.isNotBlank(tenant)) { + return initTenantCapacity(tenant); + } + if (GroupCapacityPersistService.CLUSTER.equals(group)) { + return insertGroupCapacity(GroupCapacityPersistService.CLUSTER); + } + // Group会自动扩容 + return initGroupCapacity(group); + } - private boolean insertGroupCapacity(String group) { - return insertGroupCapacity(group, null, null, null, null); - } + private boolean insertGroupCapacity(String group) { + return insertGroupCapacity(group, null, null, null, null); + } - private boolean insertGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, - Integer maxAggrSize) { - try { - final Timestamp now = TimeUtils.getCurrentTime(); - GroupCapacity groupCapacity = new GroupCapacity(); - groupCapacity.setGroup(group); - // 新增时,quota=0表示限额为默认值,为了在更新默认限额时只需修改nacos配置,而不需要更新表中大部分数据 - groupCapacity.setQuota(quota == null ? ZERO : quota); - // 新增时,maxSize=0表示大小为默认值,为了在更新默认大小时只需修改nacos配置,而不需要更新表中大部分数据 - groupCapacity.setMaxSize(maxSize == null ? ZERO : maxSize); - groupCapacity.setMaxAggrCount(maxAggrCount == null ? ZERO : maxAggrCount); - groupCapacity.setMaxAggrSize(maxAggrSize == null ? ZERO : maxAggrSize); - groupCapacity.setGmtCreate(now); - groupCapacity.setGmtModified(now); - return groupCapacityPersistService.insertGroupCapacity(groupCapacity); - } catch (DuplicateKeyException e) { - // 并发情况下同时insert会出现,ignore - LogUtil.defaultLog.warn("group: {}, message: {}", group, e.getMessage()); - } - return false; - } + private boolean insertGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, + Integer maxAggrSize) { + try { + final Timestamp now = TimeUtils.getCurrentTime(); + GroupCapacity groupCapacity = new GroupCapacity(); + groupCapacity.setGroup(group); + // 新增时,quota=0表示限额为默认值,为了在更新默认限额时只需修改nacos配置,而不需要更新表中大部分数据 + groupCapacity.setQuota(quota == null ? ZERO : quota); + // 新增时,maxSize=0表示大小为默认值,为了在更新默认大小时只需修改nacos配置,而不需要更新表中大部分数据 + groupCapacity.setMaxSize(maxSize == null ? ZERO : maxSize); + groupCapacity.setMaxAggrCount(maxAggrCount == null ? ZERO : maxAggrCount); + groupCapacity.setMaxAggrSize(maxAggrSize == null ? ZERO : maxAggrSize); + groupCapacity.setGmtCreate(now); + groupCapacity.setGmtModified(now); + return groupCapacityPersistService.insertGroupCapacity(groupCapacity); + } catch (DuplicateKeyException e) { + // 并发情况下同时insert会出现,ignore + LogUtil.defaultLog.warn("group: {}, message: {}", group, e.getMessage()); + } + return false; + } - private boolean updateGroupUsage(CounterMode counterMode, String group, int defaultQuota, - boolean ignoreQuotaLimit) { - final Timestamp now = TimeUtils.getCurrentTime(); - GroupCapacity groupCapacity = new GroupCapacity(); - groupCapacity.setGroup(group); - groupCapacity.setQuota(defaultQuota); - groupCapacity.setGmtModified(now); - if (CounterMode.INCREMENT == counterMode) { - if (ignoreQuotaLimit) { - return groupCapacityPersistService.incrementUsage(groupCapacity); - } - // 先按默认值限额更新,大部分情况下都是默认值,默认值表里面的quota字段为0 - return groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(groupCapacity) - || groupCapacityPersistService.incrementUsageWithQuotaLimit(groupCapacity); - } - return groupCapacityPersistService.decrementUsage(groupCapacity); - } + private boolean updateGroupUsage(CounterMode counterMode, String group, int defaultQuota, + boolean ignoreQuotaLimit) { + final Timestamp now = TimeUtils.getCurrentTime(); + GroupCapacity groupCapacity = new GroupCapacity(); + groupCapacity.setGroup(group); + groupCapacity.setQuota(defaultQuota); + groupCapacity.setGmtModified(now); + if (CounterMode.INCREMENT == counterMode) { + if (ignoreQuotaLimit) { + return groupCapacityPersistService.incrementUsage(groupCapacity); + } + // 先按默认值限额更新,大部分情况下都是默认值,默认值表里面的quota字段为0 + return groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(groupCapacity) + || groupCapacityPersistService.incrementUsageWithQuotaLimit(groupCapacity); + } + return groupCapacityPersistService.decrementUsage(groupCapacity); + } - /** - * 提供给关闭容量管理的限制检验功能时计数使用
租户: 1. 如果容量信息不存在,则初始化容量信息
2. 更新容量的使用量usage,加一或减一 - * - * @param counterMode 增加或者减少 - * @param tenant 租户 - * @param ignoreQuotaLimit 是否忽略容量额度限制,在关闭容量管理的限制检验功能只计数的时候为true,开启容量管理的限制检验功能则为false - * @return 是否操作成功 - */ - public boolean insertAndUpdateTenantUsage(CounterMode counterMode, String tenant, boolean ignoreQuotaLimit) { - TenantCapacity tenantCapacity = getTenantCapacity(tenant); - if (tenantCapacity == null) { - // 初始化容量信息 - initTenantCapacity(tenant); - } - return updateTenantUsage(counterMode, tenant, ignoreQuotaLimit); - } + /** + * 提供给关闭容量管理的限制检验功能时计数使用
租户: 1. 如果容量信息不存在,则初始化容量信息
2. 更新容量的使用量usage,加一或减一 + * + * @param counterMode 增加或者减少 + * @param tenant 租户 + * @param ignoreQuotaLimit 是否忽略容量额度限制,在关闭容量管理的限制检验功能只计数的时候为true,开启容量管理的限制检验功能则为false + * @return 是否操作成功 + */ + public boolean insertAndUpdateTenantUsage(CounterMode counterMode, String tenant, boolean ignoreQuotaLimit) { + TenantCapacity tenantCapacity = getTenantCapacity(tenant); + if (tenantCapacity == null) { + // 初始化容量信息 + initTenantCapacity(tenant); + } + return updateTenantUsage(counterMode, tenant, ignoreQuotaLimit); + } - private boolean updateTenantUsage(CounterMode counterMode, String tenant, boolean ignoreQuotaLimit) { - final Timestamp now = TimeUtils.getCurrentTime(); - TenantCapacity tenantCapacity = new TenantCapacity(); - tenantCapacity.setTenant(tenant); - tenantCapacity.setQuota(PropertyUtil.getDefaultTenantQuota()); - tenantCapacity.setGmtModified(now); - if (CounterMode.INCREMENT == counterMode) { - if (ignoreQuotaLimit) { - return tenantCapacityPersistService.incrementUsage(tenantCapacity); - } - // 先按默认值限额更新,大部分情况下都是默认值,默认值表里面的quota字段为0 - return tenantCapacityPersistService.incrementUsageWithDefaultQuotaLimit(tenantCapacity) - || tenantCapacityPersistService.incrementUsageWithQuotaLimit(tenantCapacity); - } - return tenantCapacityPersistService.decrementUsage(tenantCapacity); - } + private boolean updateTenantUsage(CounterMode counterMode, String tenant, boolean ignoreQuotaLimit) { + final Timestamp now = TimeUtils.getCurrentTime(); + TenantCapacity tenantCapacity = new TenantCapacity(); + tenantCapacity.setTenant(tenant); + tenantCapacity.setQuota(PropertyUtil.getDefaultTenantQuota()); + tenantCapacity.setGmtModified(now); + if (CounterMode.INCREMENT == counterMode) { + if (ignoreQuotaLimit) { + return tenantCapacityPersistService.incrementUsage(tenantCapacity); + } + // 先按默认值限额更新,大部分情况下都是默认值,默认值表里面的quota字段为0 + return tenantCapacityPersistService.incrementUsageWithDefaultQuotaLimit(tenantCapacity) + || tenantCapacityPersistService.incrementUsageWithQuotaLimit(tenantCapacity); + } + return tenantCapacityPersistService.decrementUsage(tenantCapacity); + } - public boolean updateTenantUsage(CounterMode counterMode, String tenant) { - return updateTenantUsage(counterMode, tenant, false); - } + public boolean updateTenantUsage(CounterMode counterMode, String tenant) { + return updateTenantUsage(counterMode, tenant, false); + } - /** - * 初始化该租户的容量信息,如果到达限额,将自动扩容,以降低运维成本 - */ - public boolean initTenantCapacity(String tenant) { - return initTenantCapacity(tenant, null, null, null, null); - } + /** + * 初始化该租户的容量信息,如果到达限额,将自动扩容,以降低运维成本 + */ + public boolean initTenantCapacity(String tenant) { + return initTenantCapacity(tenant, null, null, null, null); + } - /** - * 初始化该租户的容量信息,如果到达限额,将自动扩容,以降低运维成本 - */ - public boolean initTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, - Integer maxAggrSize) { - boolean insertSuccess = insertTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); - if (quota != null) { - return insertSuccess; - } - autoExpansion(null, tenant); - return insertSuccess; - } + /** + * 初始化该租户的容量信息,如果到达限额,将自动扩容,以降低运维成本 + */ + public boolean initTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, + Integer maxAggrSize) { + boolean insertSuccess = insertTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); + if (quota != null) { + return insertSuccess; + } + autoExpansion(null, tenant); + return insertSuccess; + } - private boolean insertTenantCapacity(String tenant) { - return insertTenantCapacity(tenant, null, null, null, null); - } + private boolean insertTenantCapacity(String tenant) { + return insertTenantCapacity(tenant, null, null, null, null); + } - private boolean insertTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, - Integer maxAggrSize) { - try { - final Timestamp now = TimeUtils.getCurrentTime(); - TenantCapacity tenantCapacity = new TenantCapacity(); - tenantCapacity.setTenant(tenant); - // 新增时,quota=0表示限额为默认值,为了在更新默认限额时只需修改nacos配置,而不需要更新表中大部分数据 - tenantCapacity.setQuota(quota == null ? ZERO : quota); - // 新增时,maxSize=0表示大小为默认值,为了在更新默认大小时只需修改nacos配置,而不需要更新表中大部分数据 - tenantCapacity.setMaxSize(maxSize == null ? ZERO : maxSize); - tenantCapacity.setMaxAggrCount(maxAggrCount == null ? ZERO : maxAggrCount); - tenantCapacity.setMaxAggrSize(maxAggrSize == null ? ZERO : maxAggrSize); - tenantCapacity.setGmtCreate(now); - tenantCapacity.setGmtModified(now); - return tenantCapacityPersistService.insertTenantCapacity(tenantCapacity); - } catch (DuplicateKeyException e) { - // 并发情况下同时insert会出现,ignore - LogUtil.defaultLog.warn("tenant: {}, message: {}", tenant, e.getMessage()); - } - return false; - } + private boolean insertTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, + Integer maxAggrSize) { + try { + final Timestamp now = TimeUtils.getCurrentTime(); + TenantCapacity tenantCapacity = new TenantCapacity(); + tenantCapacity.setTenant(tenant); + // 新增时,quota=0表示限额为默认值,为了在更新默认限额时只需修改nacos配置,而不需要更新表中大部分数据 + tenantCapacity.setQuota(quota == null ? ZERO : quota); + // 新增时,maxSize=0表示大小为默认值,为了在更新默认大小时只需修改nacos配置,而不需要更新表中大部分数据 + tenantCapacity.setMaxSize(maxSize == null ? ZERO : maxSize); + tenantCapacity.setMaxAggrCount(maxAggrCount == null ? ZERO : maxAggrCount); + tenantCapacity.setMaxAggrSize(maxAggrSize == null ? ZERO : maxAggrSize); + tenantCapacity.setGmtCreate(now); + tenantCapacity.setGmtModified(now); + return tenantCapacityPersistService.insertTenantCapacity(tenantCapacity); + } catch (DuplicateKeyException e) { + // 并发情况下同时insert会出现,ignore + LogUtil.defaultLog.warn("tenant: {}, message: {}", tenant, e.getMessage()); + } + return false; + } - public TenantCapacity getTenantCapacity(String tenant) { - return tenantCapacityPersistService.getTenantCapacity(tenant); - } + public TenantCapacity getTenantCapacity(String tenant) { + return tenantCapacityPersistService.getTenantCapacity(tenant); + } - /** - * 提供给API接口使用
租户:记录不存在则初始化,存在则直接更新容量限额或者内容大小 - * - * @param group Group ID - * @param tenant 租户 - * @param quota 容量限额 - * @param maxSize 配置内容(content)大小限制 - * @return 是否操作成功 - */ - public boolean insertOrUpdateCapacity(String group, String tenant, Integer quota, Integer maxSize, Integer - maxAggrCount, Integer maxAggrSize) { - if (StringUtils.isNotBlank(tenant)) { - Capacity capacity = tenantCapacityPersistService.getTenantCapacity(tenant); - if (capacity == null) { - return initTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); - } - return tenantCapacityPersistService.updateTenantCapacity(tenant, quota, maxSize, maxAggrCount, - maxAggrSize); - } - Capacity capacity = groupCapacityPersistService.getGroupCapacity(group); - if (capacity == null) { - return initGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); - } - return groupCapacityPersistService.updateGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); - } + /** + * 提供给API接口使用
租户:记录不存在则初始化,存在则直接更新容量限额或者内容大小 + * + * @param group Group ID + * @param tenant 租户 + * @param quota 容量限额 + * @param maxSize 配置内容(content)大小限制 + * @return 是否操作成功 + */ + public boolean insertOrUpdateCapacity(String group, String tenant, Integer quota, Integer maxSize, Integer + maxAggrCount, Integer maxAggrSize) { + if (StringUtils.isNotBlank(tenant)) { + Capacity capacity = tenantCapacityPersistService.getTenantCapacity(tenant); + if (capacity == null) { + return initTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); + } + return tenantCapacityPersistService.updateTenantCapacity(tenant, quota, maxSize, maxAggrCount, + maxAggrSize); + } + Capacity capacity = groupCapacityPersistService.getGroupCapacity(group); + if (capacity == null) { + return initGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); + } + return groupCapacityPersistService.updateGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService.java index 3d85f49e4..44b4dcced 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService.java @@ -17,16 +17,12 @@ package com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; -import com.alibaba.nacos.config.server.service.BasicDataSourceServiceImpl; import com.alibaba.nacos.config.server.service.DataSourceService; import com.alibaba.nacos.config.server.service.DynamicDataSource; -import com.alibaba.nacos.config.server.service.LocalDataSourceServiceImpl; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.google.common.collect.Lists; - import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; @@ -35,293 +31,296 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.stereotype.Service; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; - +import javax.annotation.PostConstruct; import java.sql.*; import java.util.List; -import javax.annotation.PostConstruct; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; /** * Group Capacity Service + * * @author hexu.hxy * @date 2018/03/05 */ @Service public class GroupCapacityPersistService { - static final String CLUSTER = ""; + static final String CLUSTER = ""; - private static final GroupCapacityRowMapper - GROUP_CAPACITY_ROW_MAPPER = new GroupCapacityRowMapper(); - private JdbcTemplate jdbcTemplate; + private static final GroupCapacityRowMapper + GROUP_CAPACITY_ROW_MAPPER = new GroupCapacityRowMapper(); + private JdbcTemplate jdbcTemplate; - @Autowired - private DynamicDataSource dynamicDataSource; - private DataSourceService dataSourceService; - - @PostConstruct - public void init() { - this.dataSourceService = dynamicDataSource.getDataSource(); - this.jdbcTemplate = dataSourceService.getJdbcTemplate(); - } + @Autowired + private DynamicDataSource dynamicDataSource; + private DataSourceService dataSourceService; - private static final class GroupCapacityRowMapper implements - RowMapper { - @Override - public GroupCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { - GroupCapacity groupCapacity = new GroupCapacity(); - groupCapacity.setId(rs.getLong("id")); - groupCapacity.setQuota(rs.getInt("quota")); - groupCapacity.setUsage(rs.getInt("usage")); - groupCapacity.setMaxSize(rs.getInt("max_size")); - groupCapacity.setMaxAggrCount(rs.getInt("max_aggr_count")); - groupCapacity.setMaxAggrSize(rs.getInt("max_aggr_size")); - groupCapacity.setGroup(rs.getString("group_id")); - return groupCapacity; - } - } + @PostConstruct + public void init() { + this.dataSourceService = dynamicDataSource.getDataSource(); + this.jdbcTemplate = dataSourceService.getJdbcTemplate(); + } - public GroupCapacity getGroupCapacity(String groupId) { - String sql - = "select id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, group_id from group_capacity " - + "where group_id=?"; - List list = jdbcTemplate.query(sql, new Object[] {groupId}, - GROUP_CAPACITY_ROW_MAPPER); - if (list.isEmpty()) { - return null; - } - return list.get(0); - } + private static final class GroupCapacityRowMapper implements + RowMapper { + @Override + public GroupCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { + GroupCapacity groupCapacity = new GroupCapacity(); + groupCapacity.setId(rs.getLong("id")); + groupCapacity.setQuota(rs.getInt("quota")); + groupCapacity.setUsage(rs.getInt("usage")); + groupCapacity.setMaxSize(rs.getInt("max_size")); + groupCapacity.setMaxAggrCount(rs.getInt("max_aggr_count")); + groupCapacity.setMaxAggrSize(rs.getInt("max_aggr_size")); + groupCapacity.setGroup(rs.getString("group_id")); + return groupCapacity; + } + } - public Capacity getClusterCapacity() { - return getGroupCapacity(CLUSTER); - } + public GroupCapacity getGroupCapacity(String groupId) { + String sql + = "SELECT id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, group_id FROM group_capacity " + + "WHERE group_id=?"; + List list = jdbcTemplate.query(sql, new Object[] {groupId}, + GROUP_CAPACITY_ROW_MAPPER); + if (list.isEmpty()) { + return null; + } + return list.get(0); + } - public boolean insertGroupCapacity(final GroupCapacity capacity) { - String sql; - if (CLUSTER.equals(capacity.getGroup())) { - sql - = "insert into group_capacity (group_id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, " - + "gmt_create, gmt_modified) select ?, ?, count(*), ?, ?, ?, ?, ? from config_info;"; - } else { - // 注意这里要加"tenant_id = ''"条件 - sql = - "insert into group_capacity (group_id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, " - + "gmt_create, gmt_modified) select ?, ?, count(*), ?, ?, ?, ?, ? from config_info where " - + "group_id=? and tenant_id = '';"; - } - return insertGroupCapacity(sql, capacity); - } + public Capacity getClusterCapacity() { + return getGroupCapacity(CLUSTER); + } - public int getClusterUsage() { - Capacity clusterCapacity = getClusterCapacity(); - if (clusterCapacity != null) { - return clusterCapacity.getUsage(); - } - String sql = "select count(*) from config_info"; - Integer result = jdbcTemplate.queryForObject(sql, Integer.class); - if (result ==null) { - throw new IllegalArgumentException("configInfoCount error"); - } - return result.intValue(); - } + public boolean insertGroupCapacity(final GroupCapacity capacity) { + String sql; + if (CLUSTER.equals(capacity.getGroup())) { + sql + = "insert into group_capacity (group_id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, " + + "gmt_create, gmt_modified) select ?, ?, count(*), ?, ?, ?, ?, ? from config_info;"; + } else { + // 注意这里要加"tenant_id = ''"条件 + sql = + "insert into group_capacity (group_id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, " + + "gmt_create, gmt_modified) select ?, ?, count(*), ?, ?, ?, ?, ? from config_info where " + + "group_id=? and tenant_id = '';"; + } + return insertGroupCapacity(sql, capacity); + } - private boolean insertGroupCapacity(final String sql, final GroupCapacity capacity) { - try { - GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); - PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { - @Override - @SuppressFBWarnings(value = { "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", - "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" }, justification = "findbugs does not trust jdbctemplate, sql is constant in practice") - public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { - PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - String group = capacity.getGroup(); - ps.setString(1, group); - ps.setInt(2, capacity.getQuota()); - ps.setInt(3, capacity.getMaxSize()); - ps.setInt(4, capacity.getMaxAggrCount()); - ps.setInt(5, capacity.getMaxAggrSize()); - ps.setTimestamp(6, capacity.getGmtCreate()); - ps.setTimestamp(7, capacity.getGmtModified()); - if (!CLUSTER.equals(group)) { - ps.setString(8, group); - } - return ps; - } - }; - jdbcTemplate.update(preparedStatementCreator, generatedKeyHolder); - return generatedKeyHolder.getKey() != null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public int getClusterUsage() { + Capacity clusterCapacity = getClusterCapacity(); + if (clusterCapacity != null) { + return clusterCapacity.getUsage(); + } + String sql = "SELECT count(*) FROM config_info"; + Integer result = jdbcTemplate.queryForObject(sql, Integer.class); + if (result == null) { + throw new IllegalArgumentException("configInfoCount error"); + } + return result.intValue(); + } - public boolean incrementUsageWithDefaultQuotaLimit(GroupCapacity groupCapacity) { - String sql = - "update group_capacity set `usage` = `usage` + 1, gmt_modified = ? where group_id = ? and `usage` <" - + " ? and quota = 0"; - try { - int affectRow = jdbcTemplate.update(sql, - groupCapacity.getGmtModified(), groupCapacity.getGroup(), groupCapacity.getQuota()); - return affectRow == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + private boolean insertGroupCapacity(final String sql, final GroupCapacity capacity) { + try { + GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); + PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { + @Override + @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", + "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"}, + justification = "findbugs does not trust jdbctemplate, sql is constant in practice") + public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + String group = capacity.getGroup(); + ps.setString(1, group); + ps.setInt(2, capacity.getQuota()); + ps.setInt(3, capacity.getMaxSize()); + ps.setInt(4, capacity.getMaxAggrCount()); + ps.setInt(5, capacity.getMaxAggrSize()); + ps.setTimestamp(6, capacity.getGmtCreate()); + ps.setTimestamp(7, capacity.getGmtModified()); + if (!CLUSTER.equals(group)) { + ps.setString(8, group); + } + return ps; + } + }; + jdbcTemplate.update(preparedStatementCreator, generatedKeyHolder); + return generatedKeyHolder.getKey() != null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean incrementUsageWithQuotaLimit(GroupCapacity groupCapacity) { - String sql - = "update group_capacity set `usage` = `usage` + 1, gmt_modified = ? where group_id = ? and `usage` < " - + "quota and quota != 0"; - try { - return jdbcTemplate.update(sql, - groupCapacity.getGmtModified(), groupCapacity.getGroup()) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; + public boolean incrementUsageWithDefaultQuotaLimit(GroupCapacity groupCapacity) { + String sql = + "UPDATE group_capacity SET `usage` = `usage` + 1, gmt_modified = ? WHERE group_id = ? AND `usage` <" + + " ? AND quota = 0"; + try { + int affectRow = jdbcTemplate.update(sql, + groupCapacity.getGmtModified(), groupCapacity.getGroup(), groupCapacity.getQuota()); + return affectRow == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - } - } + public boolean incrementUsageWithQuotaLimit(GroupCapacity groupCapacity) { + String sql + = "UPDATE group_capacity SET `usage` = `usage` + 1, gmt_modified = ? WHERE group_id = ? AND `usage` < " + + "quota AND quota != 0"; + try { + return jdbcTemplate.update(sql, + groupCapacity.getGmtModified(), groupCapacity.getGroup()) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; - public boolean incrementUsage(GroupCapacity groupCapacity) { - String sql = "update group_capacity set `usage` = `usage` + 1, gmt_modified = ? where group_id = ?"; - try { - int affectRow = jdbcTemplate.update(sql, - groupCapacity.getGmtModified(), groupCapacity.getGroup()); - return affectRow == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + } + } - public boolean decrementUsage(GroupCapacity groupCapacity) { - String sql = - "update group_capacity set `usage` = `usage` - 1, gmt_modified = ? where group_id = ? and `usage` > 0"; - try { - return jdbcTemplate.update(sql, - groupCapacity.getGmtModified(), groupCapacity.getGroup()) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean incrementUsage(GroupCapacity groupCapacity) { + String sql = "UPDATE group_capacity SET `usage` = `usage` + 1, gmt_modified = ? WHERE group_id = ?"; + try { + int affectRow = jdbcTemplate.update(sql, + groupCapacity.getGmtModified(), groupCapacity.getGroup()); + return affectRow == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean updateGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, - Integer maxAggrSize) { - List argList = Lists.newArrayList(); - StringBuilder sql = new StringBuilder("update group_capacity set"); - if (quota != null) { - sql.append(" quota = ?,"); - argList.add(quota); - } - if (maxSize != null) { - sql.append(" max_size = ?,"); - argList.add(maxSize); - } - if (maxAggrCount != null) { - sql.append(" max_aggr_count = ?,"); - argList.add(maxAggrCount); - } - if (maxAggrSize != null) { - sql.append(" max_aggr_size = ?,"); - argList.add(maxAggrSize); - } - sql.append(" gmt_modified = ?"); - argList.add(TimeUtils.getCurrentTime()); + public boolean decrementUsage(GroupCapacity groupCapacity) { + String sql = + "UPDATE group_capacity SET `usage` = `usage` - 1, gmt_modified = ? WHERE group_id = ? AND `usage` > 0"; + try { + return jdbcTemplate.update(sql, + groupCapacity.getGmtModified(), groupCapacity.getGroup()) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - sql.append(" where group_id = ?"); - argList.add(group); - try { - return jdbcTemplate.update(sql.toString(), argList.toArray()) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean updateGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, + Integer maxAggrSize) { + List argList = Lists.newArrayList(); + StringBuilder sql = new StringBuilder("update group_capacity set"); + if (quota != null) { + sql.append(" quota = ?,"); + argList.add(quota); + } + if (maxSize != null) { + sql.append(" max_size = ?,"); + argList.add(maxSize); + } + if (maxAggrCount != null) { + sql.append(" max_aggr_count = ?,"); + argList.add(maxAggrCount); + } + if (maxAggrSize != null) { + sql.append(" max_aggr_size = ?,"); + argList.add(maxAggrSize); + } + sql.append(" gmt_modified = ?"); + argList.add(TimeUtils.getCurrentTime()); - public boolean updateQuota(String group, Integer quota) { - return updateGroupCapacity(group, quota, null, null, null); - } + sql.append(" where group_id = ?"); + argList.add(group); + try { + return jdbcTemplate.update(sql.toString(), argList.toArray()) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean updateMaxSize(String group, Integer maxSize) { - return updateGroupCapacity(group, null, maxSize, null, null); - } + public boolean updateQuota(String group, Integer quota) { + return updateGroupCapacity(group, quota, null, null, null); + } - public boolean correctUsage(String group, Timestamp gmtModified) { - String sql; - if (CLUSTER.equals(group)) { - sql = "update group_capacity set `usage` = (select count(*) from config_info), gmt_modified = ? where " - + "group_id = ?"; - try { - return jdbcTemplate.update(sql, gmtModified, group) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } else { - // 注意这里要加"tenant_id = ''"条件 - sql = "update group_capacity set `usage` = (select count(*) from config_info where group_id=? and " - + "tenant_id = ''), gmt_modified = ? where group_id = ?"; - try { - return jdbcTemplate.update(sql, group, gmtModified, group) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } - } + public boolean updateMaxSize(String group, Integer maxSize) { + return updateGroupCapacity(group, null, maxSize, null, null); + } - /** - * 获取GroupCapacity列表,只有id、groupId有值 - * - * @param lastId id > lastId - * @param pageSize 页数 - * @return GroupCapacity列表 - */ - public List getCapacityList4CorrectUsage(long lastId, int pageSize) { - String sql = "select id, group_id from group_capacity where id>? limit ?"; + public boolean correctUsage(String group, Timestamp gmtModified) { + String sql; + if (CLUSTER.equals(group)) { + sql = "UPDATE group_capacity SET `usage` = (SELECT count(*) FROM config_info), gmt_modified = ? WHERE " + + "group_id = ?"; + try { + return jdbcTemplate.update(sql, gmtModified, group) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } else { + // 注意这里要加"tenant_id = ''"条件 + sql = "UPDATE group_capacity SET `usage` = (SELECT count(*) FROM config_info WHERE group_id=? AND " + + "tenant_id = ''), gmt_modified = ? WHERE group_id = ?"; + try { + return jdbcTemplate.update(sql, group, gmtModified, group) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } + } - if (PropertyUtil.isStandaloneMode()) { - sql = "select id, group_id from group_capacity where id>? OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"; - } - try { - return jdbcTemplate.query(sql, new Object[] {lastId, pageSize}, - new RowMapper() { - @Override - public GroupCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { - GroupCapacity groupCapacity = new GroupCapacity(); - groupCapacity.setId(rs.getLong("id")); - groupCapacity.setGroup(rs.getString("group_id")); - return groupCapacity; - } - }); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + /** + * 获取GroupCapacity列表,只有id、groupId有值 + * + * @param lastId id > lastId + * @param pageSize 页数 + * @return GroupCapacity列表 + */ + public List getCapacityList4CorrectUsage(long lastId, int pageSize) { + String sql = "SELECT id, group_id FROM group_capacity WHERE id>? LIMIT ?"; - public boolean deleteGroupCapacity(final String group) { - try { - PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { - @Override - @SuppressFBWarnings(value = { "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", - "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" }, justification = "findbugs does not trust jdbctemplate, sql is constant in practice") - public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { - PreparedStatement ps = connection.prepareStatement( - "delete from group_capacity where group_id = ?;"); - ps.setString(1, group); - return ps; - } - }; - return jdbcTemplate.update(preparedStatementCreator) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + sql = "SELECT id, group_id FROM group_capacity WHERE id>? OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"; + } + try { + return jdbcTemplate.query(sql, new Object[] {lastId, pageSize}, + new RowMapper() { + @Override + public GroupCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { + GroupCapacity groupCapacity = new GroupCapacity(); + groupCapacity.setId(rs.getLong("id")); + groupCapacity.setGroup(rs.getString("group_id")); + return groupCapacity; + } + }); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - } + public boolean deleteGroupCapacity(final String group) { + try { + PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { + @Override + @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", + "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"}, + justification = "findbugs does not trust jdbctemplate, sql is constant in practice") + public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( + "DELETE FROM group_capacity WHERE group_id = ?;"); + ps.setString(1, group); + return ps; + } + }; + return jdbcTemplate.update(preparedStatementCreator) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService.java index 3f3c25621..3f7cbc753 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService.java @@ -16,16 +16,12 @@ package com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.config.server.model.capacity.TenantCapacity; -import com.alibaba.nacos.config.server.service.BasicDataSourceServiceImpl; import com.alibaba.nacos.config.server.service.DataSourceService; import com.alibaba.nacos.config.server.service.DynamicDataSource; -import com.alibaba.nacos.config.server.service.LocalDataSourceServiceImpl; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.google.common.collect.Lists; - import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; @@ -34,244 +30,247 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.stereotype.Service; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; - +import javax.annotation.PostConstruct; import java.sql.*; import java.util.List; -import javax.annotation.PostConstruct; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; /** * Tenant Capacity Service + * * @author hexu.hxy * @date 2018/03/05 */ @Service public class TenantCapacityPersistService { - private static final TenantCapacityRowMapper - TENANT_CAPACITY_ROW_MAPPER = new TenantCapacityRowMapper(); - private JdbcTemplate jdbcTemplate; + private static final TenantCapacityRowMapper + TENANT_CAPACITY_ROW_MAPPER = new TenantCapacityRowMapper(); + private JdbcTemplate jdbcTemplate; - @Autowired - private DynamicDataSource dynamicDataSource; - private DataSourceService dataSourceService; + @Autowired + private DynamicDataSource dynamicDataSource; + private DataSourceService dataSourceService; - @PostConstruct - public void init() { - this.dataSourceService = dynamicDataSource.getDataSource(); - this.jdbcTemplate = dataSourceService.getJdbcTemplate(); - } + @PostConstruct + public void init() { + this.dataSourceService = dynamicDataSource.getDataSource(); + this.jdbcTemplate = dataSourceService.getJdbcTemplate(); + } - private static final class TenantCapacityRowMapper implements - RowMapper { - @Override - public TenantCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { - TenantCapacity tenantCapacity = new TenantCapacity(); - tenantCapacity.setId(rs.getLong("id")); - tenantCapacity.setQuota(rs.getInt("quota")); - tenantCapacity.setUsage(rs.getInt("usage")); - tenantCapacity.setMaxSize(rs.getInt("max_size")); - tenantCapacity.setMaxAggrCount(rs.getInt("max_aggr_count")); - tenantCapacity.setMaxAggrSize(rs.getInt("max_aggr_size")); - tenantCapacity.setTenant(rs.getString("tenant_id")); - return tenantCapacity; - } - } + private static final class TenantCapacityRowMapper implements + RowMapper { + @Override + public TenantCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { + TenantCapacity tenantCapacity = new TenantCapacity(); + tenantCapacity.setId(rs.getLong("id")); + tenantCapacity.setQuota(rs.getInt("quota")); + tenantCapacity.setUsage(rs.getInt("usage")); + tenantCapacity.setMaxSize(rs.getInt("max_size")); + tenantCapacity.setMaxAggrCount(rs.getInt("max_aggr_count")); + tenantCapacity.setMaxAggrSize(rs.getInt("max_aggr_size")); + tenantCapacity.setTenant(rs.getString("tenant_id")); + return tenantCapacity; + } + } - public TenantCapacity getTenantCapacity(String tenantId) { - String sql - = "select id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, tenant_id from tenant_capacity " - + "where tenant_id=?"; - List list = jdbcTemplate.query(sql, new Object[] {tenantId}, - TENANT_CAPACITY_ROW_MAPPER); - if (list.isEmpty()) { - return null; - } - return list.get(0); - } + public TenantCapacity getTenantCapacity(String tenantId) { + String sql + = "SELECT id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, tenant_id FROM tenant_capacity " + + "WHERE tenant_id=?"; + List list = jdbcTemplate.query(sql, new Object[] {tenantId}, + TENANT_CAPACITY_ROW_MAPPER); + if (list.isEmpty()) { + return null; + } + return list.get(0); + } - public boolean insertTenantCapacity(final TenantCapacity tenantCapacity) { - final String sql = - "insert into tenant_capacity (tenant_id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, " - + "gmt_create, gmt_modified) select ?, ?, count(*), ?, ?, ?, ?, ? from config_info where tenant_id=?;"; - try { - GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); - PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { - @Override - @SuppressFBWarnings(value = { "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", - "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" }, justification = "findbugs does not trust jdbctemplate, sql is constant in practice") - public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { - PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - String tenant = tenantCapacity.getTenant(); - ps.setString(1, tenant); - ps.setInt(2, tenantCapacity.getQuota()); - ps.setInt(3, tenantCapacity.getMaxSize()); - ps.setInt(4, tenantCapacity.getMaxAggrCount()); - ps.setInt(5, tenantCapacity.getMaxAggrSize()); - ps.setTimestamp(6, tenantCapacity.getGmtCreate()); - ps.setTimestamp(7, tenantCapacity.getGmtModified()); - ps.setString(8, tenantCapacity.getTenant()); - return ps; - } - }; - jdbcTemplate.update(preparedStatementCreator, generatedKeyHolder); - return generatedKeyHolder.getKey() != null; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } + public boolean insertTenantCapacity(final TenantCapacity tenantCapacity) { + final String sql = + "INSERT INTO tenant_capacity (tenant_id, quota, `usage`, `max_size`, max_aggr_count, max_aggr_size, " + + "gmt_create, gmt_modified) SELECT ?, ?, count(*), ?, ?, ?, ?, ? FROM config_info WHERE tenant_id=?;"; + try { + GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); + PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { + @Override + @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", + "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"}, + justification = "findbugs does not trust jdbctemplate, sql is constant in practice") + public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + String tenant = tenantCapacity.getTenant(); + ps.setString(1, tenant); + ps.setInt(2, tenantCapacity.getQuota()); + ps.setInt(3, tenantCapacity.getMaxSize()); + ps.setInt(4, tenantCapacity.getMaxAggrCount()); + ps.setInt(5, tenantCapacity.getMaxAggrSize()); + ps.setTimestamp(6, tenantCapacity.getGmtCreate()); + ps.setTimestamp(7, tenantCapacity.getGmtModified()); + ps.setString(8, tenantCapacity.getTenant()); + return ps; + } + }; + jdbcTemplate.update(preparedStatementCreator, generatedKeyHolder); + return generatedKeyHolder.getKey() != null; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } - } + } - public boolean incrementUsageWithDefaultQuotaLimit(TenantCapacity tenantCapacity) { - String sql = - "update tenant_capacity set `usage` = `usage` + 1, gmt_modified = ? where tenant_id = ? and `usage` <" - + " ? and quota = 0"; - try { - int affectRow = jdbcTemplate.update(sql, - tenantCapacity.getGmtModified(), tenantCapacity.getTenant(), tenantCapacity.getQuota()); - return affectRow == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean incrementUsageWithDefaultQuotaLimit(TenantCapacity tenantCapacity) { + String sql = + "UPDATE tenant_capacity SET `usage` = `usage` + 1, gmt_modified = ? WHERE tenant_id = ? AND `usage` <" + + " ? AND quota = 0"; + try { + int affectRow = jdbcTemplate.update(sql, + tenantCapacity.getGmtModified(), tenantCapacity.getTenant(), tenantCapacity.getQuota()); + return affectRow == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean incrementUsageWithQuotaLimit(TenantCapacity tenantCapacity) { - String sql - = "update tenant_capacity set `usage` = `usage` + 1, gmt_modified = ? where tenant_id = ? and `usage` < " - + "quota and quota != 0"; - try { - return jdbcTemplate.update(sql, - tenantCapacity.getGmtModified(), tenantCapacity.getTenant()) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; + public boolean incrementUsageWithQuotaLimit(TenantCapacity tenantCapacity) { + String sql + = "UPDATE tenant_capacity SET `usage` = `usage` + 1, gmt_modified = ? WHERE tenant_id = ? AND `usage` < " + + "quota AND quota != 0"; + try { + return jdbcTemplate.update(sql, + tenantCapacity.getGmtModified(), tenantCapacity.getTenant()) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; - } - } + } + } - public boolean incrementUsage(TenantCapacity tenantCapacity) { - String sql = "update tenant_capacity set `usage` = `usage` + 1, gmt_modified = ? where tenant_id = ?"; - try { - int affectRow = jdbcTemplate.update(sql, - tenantCapacity.getGmtModified(), tenantCapacity.getTenant()); - return affectRow == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean incrementUsage(TenantCapacity tenantCapacity) { + String sql = "UPDATE tenant_capacity SET `usage` = `usage` + 1, gmt_modified = ? WHERE tenant_id = ?"; + try { + int affectRow = jdbcTemplate.update(sql, + tenantCapacity.getGmtModified(), tenantCapacity.getTenant()); + return affectRow == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean decrementUsage(TenantCapacity tenantCapacity) { - String sql = - "update tenant_capacity set `usage` = `usage` - 1, gmt_modified = ? where tenant_id = ? and `usage` > 0"; - try { - return jdbcTemplate.update(sql, - tenantCapacity.getGmtModified(), tenantCapacity.getTenant()) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean decrementUsage(TenantCapacity tenantCapacity) { + String sql = + "UPDATE tenant_capacity SET `usage` = `usage` - 1, gmt_modified = ? WHERE tenant_id = ? AND `usage` > 0"; + try { + return jdbcTemplate.update(sql, + tenantCapacity.getGmtModified(), tenantCapacity.getTenant()) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean updateTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, - Integer maxAggrSize) { - List argList = Lists.newArrayList(); - StringBuilder sql = new StringBuilder("update tenant_capacity set"); - if (quota != null) { - sql.append(" quota = ?,"); - argList.add(quota); - } - if (maxSize != null) { - sql.append(" max_size = ?,"); - argList.add(maxSize); - } - if (maxAggrCount != null) { - sql.append(" max_aggr_count = ?,"); - argList.add(maxAggrCount); - } - if (maxAggrSize != null) { - sql.append(" max_aggr_size = ?,"); - argList.add(maxAggrSize); - } - sql.append(" gmt_modified = ?"); - argList.add(TimeUtils.getCurrentTime()); + public boolean updateTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, + Integer maxAggrSize) { + List argList = Lists.newArrayList(); + StringBuilder sql = new StringBuilder("update tenant_capacity set"); + if (quota != null) { + sql.append(" quota = ?,"); + argList.add(quota); + } + if (maxSize != null) { + sql.append(" max_size = ?,"); + argList.add(maxSize); + } + if (maxAggrCount != null) { + sql.append(" max_aggr_count = ?,"); + argList.add(maxAggrCount); + } + if (maxAggrSize != null) { + sql.append(" max_aggr_size = ?,"); + argList.add(maxAggrSize); + } + sql.append(" gmt_modified = ?"); + argList.add(TimeUtils.getCurrentTime()); - sql.append(" where tenant_id = ?"); - argList.add(tenant); - try { - return jdbcTemplate.update(sql.toString(), argList.toArray()) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + sql.append(" where tenant_id = ?"); + argList.add(tenant); + try { + return jdbcTemplate.update(sql.toString(), argList.toArray()) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean updateQuota(String tenant, Integer quota) { - return updateTenantCapacity(tenant, quota, null, null, null); - } + public boolean updateQuota(String tenant, Integer quota) { + return updateTenantCapacity(tenant, quota, null, null, null); + } - public boolean correctUsage(String tenant, Timestamp gmtModified) { - String sql = "update tenant_capacity set `usage` = (select count(*) from config_info where tenant_id = ?), " - + "gmt_modified = ? where tenant_id = ?"; - try { - return jdbcTemplate.update(sql, tenant, gmtModified, tenant) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean correctUsage(String tenant, Timestamp gmtModified) { + String sql = "UPDATE tenant_capacity SET `usage` = (SELECT count(*) FROM config_info WHERE tenant_id = ?), " + + "gmt_modified = ? WHERE tenant_id = ?"; + try { + return jdbcTemplate.update(sql, tenant, gmtModified, tenant) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - /** - * 获取TenantCapacity列表,只有id、tenantId有值 - * - * @param lastId id > lastId - * @param pageSize 页数 - * @return TenantCapacity列表 - */ - public List getCapacityList4CorrectUsage(long lastId, int pageSize) { - String sql = "select id, tenant_id from tenant_capacity where id>? limit ?"; + /** + * 获取TenantCapacity列表,只有id、tenantId有值 + * + * @param lastId id > lastId + * @param pageSize 页数 + * @return TenantCapacity列表 + */ + public List getCapacityList4CorrectUsage(long lastId, int pageSize) { + String sql = "SELECT id, tenant_id FROM tenant_capacity WHERE id>? LIMIT ?"; - if (PropertyUtil.isStandaloneMode()) { - sql = "select id, tenant_id from tenant_capacity where id>? OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"; - } + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + sql = "SELECT id, tenant_id FROM tenant_capacity WHERE id>? OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"; + } - try { - return jdbcTemplate.query(sql, new Object[] {lastId, pageSize}, - new RowMapper() { - @Override - public TenantCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { - TenantCapacity tenantCapacity = new TenantCapacity(); - tenantCapacity.setId(rs.getLong("id")); - tenantCapacity.setTenant(rs.getString("tenant_id")); - return tenantCapacity; - } - }); - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + try { + return jdbcTemplate.query(sql, new Object[] {lastId, pageSize}, + new RowMapper() { + @Override + public TenantCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { + TenantCapacity tenantCapacity = new TenantCapacity(); + tenantCapacity.setId(rs.getLong("id")); + tenantCapacity.setTenant(rs.getString("tenant_id")); + return tenantCapacity; + } + }); + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } - public boolean deleteTenantCapacity(final String tenant) { - try { - PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { - @Override - @SuppressFBWarnings(value = { "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", - "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" }, justification = "findbugs does not trust jdbctemplate, sql is constant in practice") - public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { - PreparedStatement ps = connection.prepareStatement( - "delete from tenant_capacity where tenant_id = ?;"); - ps.setString(1, tenant); - return ps; - } - }; - return jdbcTemplate.update(preparedStatementCreator) == 1; - } catch (CannotGetJdbcConnectionException e) { - fatalLog.error("[db-error]", e); - throw e; - } - } + public boolean deleteTenantCapacity(final String tenant) { + try { + PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() { + @Override + @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", + "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"}, + justification = "findbugs does not trust jdbctemplate, sql is constant in practice") + public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( + "DELETE FROM tenant_capacity WHERE tenant_id = ?;"); + ps.setString(1, tenant); + return ps; + } + }; + return jdbcTemplate.update(preparedStatementCreator) == 1; + } catch (CannotGetJdbcConnectionException e) { + fatalLog.error("[db-error]", e); + throw e; + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpService.java index 5c17497b3..7b3be9ab6 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpService.java @@ -21,383 +21,383 @@ import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoAggr; import com.alibaba.nacos.config.server.model.ConfigInfoChanged; import com.alibaba.nacos.config.server.model.Page; -import com.alibaba.nacos.config.server.service.ConfigService; -import com.alibaba.nacos.config.server.service.DiskUtil; -import com.alibaba.nacos.config.server.service.PersistService; -import com.alibaba.nacos.config.server.service.ServerListService; -import com.alibaba.nacos.config.server.service.TimerTaskService; +import com.alibaba.nacos.config.server.service.*; import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.merge.MergeTaskProcessor; import com.alibaba.nacos.config.server.utils.*; - import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; -import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; - +import javax.annotation.PostConstruct; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.net.URI; -import java.net.URL; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import java.util.Properties; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.PostConstruct; +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; +import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog; /** * Dump data service - * @author Nacos * + * @author Nacos */ @Service public class DumpService { - - @Autowired + + @Autowired private Environment env; - + @Autowired PersistService persistService; - + @PostConstruct public void init() { - LogUtil.defaultLog.warn("DumpService start"); - DumpProcessor processor = new DumpProcessor(this); - DumpAllProcessor dumpAllProcessor = new DumpAllProcessor(this); - DumpAllBetaProcessor dumpAllBetaProcessor = new DumpAllBetaProcessor(this); - DumpAllTagProcessor dumpAllTagProcessor = new DumpAllTagProcessor(this); - - dumpTaskMgr = new TaskManager( - "com.alibaba.nacos.server.DumpTaskManager"); - dumpTaskMgr.setDefaultTaskProcessor(processor); + LogUtil.defaultLog.warn("DumpService start"); + DumpProcessor processor = new DumpProcessor(this); + DumpAllProcessor dumpAllProcessor = new DumpAllProcessor(this); + DumpAllBetaProcessor dumpAllBetaProcessor = new DumpAllBetaProcessor(this); + DumpAllTagProcessor dumpAllTagProcessor = new DumpAllTagProcessor(this); - dumpAllTaskMgr = new TaskManager( - "com.alibaba.nacos.server.DumpAllTaskManager"); - dumpAllTaskMgr.setDefaultTaskProcessor(dumpAllProcessor); + dumpTaskMgr = new TaskManager( + "com.alibaba.nacos.server.DumpTaskManager"); + dumpTaskMgr.setDefaultTaskProcessor(processor); - Runnable dumpAll = new Runnable() { - @Override - public void run() { - dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()); - } - }; - - Runnable dumpAllBeta = new Runnable() { - @Override - public void run() { - dumpAllTaskMgr.addTask(DumpAllBetaTask.TASK_ID, new DumpAllBetaTask()); - } - }; - - Runnable clearConfigHistory = new Runnable() { - @Override - public void run() { - log.warn("clearConfigHistory start"); - if (ServerListService.isFirstIp()) { - try { - Timestamp startTime = getBeforeStamp(TimeUtils.getCurrentTime(), 24 * 30); - int totalCount = persistService.findConfigHistoryCountByTime(startTime); - if (totalCount > 0) { - int pageSize = 1000; - int removeTime = (totalCount + pageSize - 1) / pageSize; - log.warn("clearConfigHistory, getBeforeStamp:{}, totalCount:{}, pageSize:{}, removeTime:{}", - new Object[] { startTime, totalCount, pageSize, removeTime }); - while (removeTime > 0) { - // 分页删除,以免批量太大报错 - persistService.removeConfigHistory(startTime, pageSize); - removeTime--; - } - } - } catch (Throwable e) { - log.error("clearConfigHistory error", e); - } - } - } - }; - - try { - dumpConfigInfo(dumpAllProcessor); + dumpAllTaskMgr = new TaskManager( + "com.alibaba.nacos.server.DumpAllTaskManager"); + dumpAllTaskMgr.setDefaultTaskProcessor(dumpAllProcessor); - // 更新beta缓存 - LogUtil.defaultLog.info("start clear all config-info-beta."); - DiskUtil.clearAllBeta(); - if (persistService.isExistTable(BETA_TABLE_NAME)) { - dumpAllBetaProcessor.process(DumpAllBetaTask.TASK_ID, new DumpAllBetaTask()); - } - // 更新Tag缓存 - LogUtil.defaultLog.info("start clear all config-info-tag."); - DiskUtil.clearAllTag(); - if (persistService.isExistTable(TAG_TABLE_NAME)) { - dumpAllTagProcessor.process(DumpAllTagTask.TASK_ID, new DumpAllTagTask()); - } + Runnable dumpAll = new Runnable() { + @Override + public void run() { + dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()); + } + }; - // add to dump aggr - List configList = persistService.findAllAggrGroup(); - if (configList != null && !configList.isEmpty()) { - total = configList.size(); - List> splitList = splitList(configList, INIT_THREAD_COUNT); - for (List list : splitList) { - MergeAllDataWorker work = new MergeAllDataWorker(list); - work.start(); - } - log.info("server start, schedule merge end."); - } - } catch (Exception e) { - LogUtil.fatalLog.error( - "Nacos Server did not start because dumpservice bean construction failure :\n" + e.getMessage(), - e.getCause()); - throw new RuntimeException( - "Nacos Server did not start because dumpservice bean construction failure :\n" + e.getMessage()); - } - if (!PropertyUtil.isStandaloneMode()) { - Runnable heartbeat = new Runnable() { - @Override - public void run() { - String heartBeatTime = TimeUtils.getCurrentTime().toString(); - // write disk - try { - DiskUtil.saveHeartBeatToDisk(heartBeatTime); - } catch (IOException e) { - LogUtil.fatalLog.error("save heartbeat fail" + e.getMessage()); - } - } - }; + Runnable dumpAllBeta = new Runnable() { + @Override + public void run() { + dumpAllTaskMgr.addTask(DumpAllBetaTask.TASK_ID, new DumpAllBetaTask()); + } + }; - TimerTaskService.scheduleWithFixedDelay(heartbeat, 0, 10, TimeUnit.SECONDS); + Runnable clearConfigHistory = new Runnable() { + @Override + public void run() { + log.warn("clearConfigHistory start"); + if (ServerListService.isFirstIp()) { + try { + Timestamp startTime = getBeforeStamp(TimeUtils.getCurrentTime(), 24 * 30); + int totalCount = persistService.findConfigHistoryCountByTime(startTime); + if (totalCount > 0) { + int pageSize = 1000; + int removeTime = (totalCount + pageSize - 1) / pageSize; + log.warn("clearConfigHistory, getBeforeStamp:{}, totalCount:{}, pageSize:{}, removeTime:{}", + new Object[] {startTime, totalCount, pageSize, removeTime}); + while (removeTime > 0) { + // 分页删除,以免批量太大报错 + persistService.removeConfigHistory(startTime, pageSize); + removeTime--; + } + } + } catch (Throwable e) { + log.error("clearConfigHistory error", e); + } + } + } + }; - long initialDelay = new Random().nextInt(INITIAL_DELAY_IN_MINUTE) + 10; - LogUtil.defaultLog.warn("initialDelay:{}", initialDelay); + try { + dumpConfigInfo(dumpAllProcessor); - TimerTaskService.scheduleWithFixedDelay(dumpAll, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); + // 更新beta缓存 + LogUtil.defaultLog.info("start clear all config-info-beta."); + DiskUtil.clearAllBeta(); + if (persistService.isExistTable(BETA_TABLE_NAME)) { + dumpAllBetaProcessor.process(DumpAllBetaTask.TASK_ID, new DumpAllBetaTask()); + } + // 更新Tag缓存 + LogUtil.defaultLog.info("start clear all config-info-tag."); + DiskUtil.clearAllTag(); + if (persistService.isExistTable(TAG_TABLE_NAME)) { + dumpAllTagProcessor.process(DumpAllTagTask.TASK_ID, new DumpAllTagTask()); + } - TimerTaskService.scheduleWithFixedDelay(dumpAllBeta, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); - } + // add to dump aggr + List configList = persistService.findAllAggrGroup(); + if (configList != null && !configList.isEmpty()) { + total = configList.size(); + List> splitList = splitList(configList, INIT_THREAD_COUNT); + for (List list : splitList) { + MergeAllDataWorker work = new MergeAllDataWorker(list); + work.start(); + } + log.info("server start, schedule merge end."); + } + } catch (Exception e) { + LogUtil.fatalLog.error( + "Nacos Server did not start because dumpservice bean construction failure :\n" + e.getMessage(), + e.getCause()); + throw new RuntimeException( + "Nacos Server did not start because dumpservice bean construction failure :\n" + e.getMessage()); + } + if (!STANDALONE_MODE) { + Runnable heartbeat = new Runnable() { + @Override + public void run() { + String heartBeatTime = TimeUtils.getCurrentTime().toString(); + // write disk + try { + DiskUtil.saveHeartBeatToDisk(heartBeatTime); + } catch (IOException e) { + LogUtil.fatalLog.error("save heartbeat fail" + e.getMessage()); + } + } + }; - TimerTaskService.scheduleWithFixedDelay(clearConfigHistory, 10, 10, TimeUnit.MINUTES); - - } + TimerTaskService.scheduleWithFixedDelay(heartbeat, 0, 10, TimeUnit.SECONDS); - private void dumpConfigInfo(DumpAllProcessor dumpAllProcessor) - throws IOException { - int timeStep = 6; - Boolean isAllDump = true; - // initial dump all - FileInputStream fis = null; - Timestamp heartheatLastStamp = null; - try { - if (isQuickStart()) { - File heartbeatFile = DiskUtil.heartBeatFile(); - if (heartbeatFile.exists()) { - fis = new FileInputStream(heartbeatFile); - String heartheatTempLast = IOUtils.toString(fis, - Constants.ENCODE); - heartheatLastStamp = Timestamp.valueOf(heartheatTempLast); - if (TimeUtils.getCurrentTime().getTime() - - heartheatLastStamp.getTime() < timeStep * 60 * 60 * 1000) { - isAllDump = false; - } - } - } - if (isAllDump) { - LogUtil.defaultLog.info("start clear all config-info."); - DiskUtil.clearAll(); - dumpAllProcessor.process(DumpAllTask.TASK_ID, new DumpAllTask()); - } else { - Timestamp beforeTimeStamp = getBeforeStamp(heartheatLastStamp, - timeStep); - DumpChangeProcessor dumpChangeProcessor = new DumpChangeProcessor( - this, beforeTimeStamp, TimeUtils.getCurrentTime()); - dumpChangeProcessor.process(DumpChangeTask.TASK_ID, - new DumpChangeTask()); - Runnable checkMd5Task = new Runnable() { - @Override - public void run() { - LogUtil.defaultLog.error("start checkMd5Task"); - List diffList = ConfigService.checkMd5(); - for (String groupKey : diffList) { - String[] dg = GroupKey.parseKey(groupKey); - String dataId = dg[0]; - String group = dg[1]; - String tenant = dg[2]; - ConfigInfoWrapper configInfo = persistService.queryConfigInfo(dataId, group, tenant); - ConfigService.dumpChange(dataId, group, tenant, configInfo.getContent(), - configInfo.getLastModified()); - } - LogUtil.defaultLog.error("end checkMd5Task"); - } - }; - TimerTaskService.scheduleWithFixedDelay(checkMd5Task, 0, 12, - TimeUnit.HOURS); - } - } catch (IOException e) { - LogUtil.fatalLog.error("dump config fail" + e.getMessage()); - throw e; - } finally { - if (null != fis) { - try { - fis.close(); - } catch (IOException e) { - LogUtil.defaultLog.warn("close file failed"); - } - } - } - } - - private Timestamp getBeforeStamp(Timestamp date , int step) { - Calendar cal = Calendar.getInstance(); - /** - * date 换成已经已知的Date对象 - */ - cal.setTime(date); - /** - * before 6 hour - */ - cal.add(Calendar.HOUR_OF_DAY, -step); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return Timestamp.valueOf(format.format(cal.getTime())); - } - - private Boolean isQuickStart() - { - try { - String val = null; - val = env.getProperty("isQuickStart"); - if (val != null && TRUE_STR.equals(val)) { - isQuickStart = true; - } - fatalLog.warn("isQuickStart:{}", isQuickStart); - } catch (Exception e) { - fatalLog.error("read application.properties wrong", e); - } - return isQuickStart; - } + long initialDelay = new Random().nextInt(INITIAL_DELAY_IN_MINUTE) + 10; + LogUtil.defaultLog.warn("initialDelay:{}", initialDelay); - public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp) { - dump(dataId, group, tenant, tag, lastModified, handleIp, false); - } - - public void dump(String dataId, String group, String tenant, long lastModified, String handleIp) { - dump(dataId, group, tenant, lastModified, handleIp, false); - } - - public void dump(String dataId, String group, String tenant, long lastModified, String handleIp, boolean isBeta) { - String groupKey = GroupKey2.getKey(dataId, group, tenant); - dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, lastModified, handleIp, isBeta)); - } - - public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp, - boolean isBeta) { - String groupKey = GroupKey2.getKey(dataId, group, tenant); - dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta)); - } + TimerTaskService.scheduleWithFixedDelay(dumpAll, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, + TimeUnit.MINUTES); + + TimerTaskService.scheduleWithFixedDelay(dumpAllBeta, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, + TimeUnit.MINUTES); + } + + TimerTaskService.scheduleWithFixedDelay(clearConfigHistory, 10, 10, TimeUnit.MINUTES); + + } + + private void dumpConfigInfo(DumpAllProcessor dumpAllProcessor) + throws IOException { + int timeStep = 6; + Boolean isAllDump = true; + // initial dump all + FileInputStream fis = null; + Timestamp heartheatLastStamp = null; + try { + if (isQuickStart()) { + File heartbeatFile = DiskUtil.heartBeatFile(); + if (heartbeatFile.exists()) { + fis = new FileInputStream(heartbeatFile); + String heartheatTempLast = IOUtils.toString(fis, + Constants.ENCODE); + heartheatLastStamp = Timestamp.valueOf(heartheatTempLast); + if (TimeUtils.getCurrentTime().getTime() + - heartheatLastStamp.getTime() < timeStep * 60 * 60 * 1000) { + isAllDump = false; + } + } + } + if (isAllDump) { + LogUtil.defaultLog.info("start clear all config-info."); + DiskUtil.clearAll(); + dumpAllProcessor.process(DumpAllTask.TASK_ID, new DumpAllTask()); + } else { + Timestamp beforeTimeStamp = getBeforeStamp(heartheatLastStamp, + timeStep); + DumpChangeProcessor dumpChangeProcessor = new DumpChangeProcessor( + this, beforeTimeStamp, TimeUtils.getCurrentTime()); + dumpChangeProcessor.process(DumpChangeTask.TASK_ID, + new DumpChangeTask()); + Runnable checkMd5Task = new Runnable() { + @Override + public void run() { + LogUtil.defaultLog.error("start checkMd5Task"); + List diffList = ConfigService.checkMd5(); + for (String groupKey : diffList) { + String[] dg = GroupKey.parseKey(groupKey); + String dataId = dg[0]; + String group = dg[1]; + String tenant = dg[2]; + ConfigInfoWrapper configInfo = persistService.queryConfigInfo(dataId, group, tenant); + ConfigService.dumpChange(dataId, group, tenant, configInfo.getContent(), + configInfo.getLastModified()); + } + LogUtil.defaultLog.error("end checkMd5Task"); + } + }; + TimerTaskService.scheduleWithFixedDelay(checkMd5Task, 0, 12, + TimeUnit.HOURS); + } + } catch (IOException e) { + LogUtil.fatalLog.error("dump config fail" + e.getMessage()); + throw e; + } finally { + if (null != fis) { + try { + fis.close(); + } catch (IOException e) { + LogUtil.defaultLog.warn("close file failed"); + } + } + } + } + + private Timestamp getBeforeStamp(Timestamp date, int step) { + Calendar cal = Calendar.getInstance(); + /** + * date 换成已经已知的Date对象 + */ + cal.setTime(date); + /** + * before 6 hour + */ + cal.add(Calendar.HOUR_OF_DAY, -step); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return Timestamp.valueOf(format.format(cal.getTime())); + } + + private Boolean isQuickStart() { + try { + String val = null; + val = env.getProperty("isQuickStart"); + if (val != null && TRUE_STR.equals(val)) { + isQuickStart = true; + } + fatalLog.warn("isQuickStart:{}", isQuickStart); + } catch (Exception e) { + fatalLog.error("read application.properties wrong", e); + } + return isQuickStart; + } + + public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp) { + dump(dataId, group, tenant, tag, lastModified, handleIp, false); + } + + public void dump(String dataId, String group, String tenant, long lastModified, String handleIp) { + dump(dataId, group, tenant, lastModified, handleIp, false); + } + + public void dump(String dataId, String group, String tenant, long lastModified, String handleIp, boolean isBeta) { + String groupKey = GroupKey2.getKey(dataId, group, tenant); + dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, lastModified, handleIp, isBeta)); + } + + public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp, + boolean isBeta) { + String groupKey = GroupKey2.getKey(dataId, group, tenant); + dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta)); + } public void dumpAll() { dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()); } - static List> splitList(List list, int count){ - List> result = new ArrayList>(count); - for(int i = 0 ; i < count ; i++){ - result.add(new ArrayList()); - } - for(int i = 0; i < list.size() ; i++){ - ConfigInfoChanged config = list.get(i); - result.get(i % count).add(config); - } - return result; - } - - class MergeAllDataWorker extends Thread{ - static final int PAGE_SIZE = 10000; - - private List configInfoList; - public MergeAllDataWorker(List configInfoList) { - super("MergeAllDataWorker"); - this.configInfoList = configInfoList; - } - - @Override - public void run() { - for (ConfigInfoChanged configInfo : configInfoList) { - String dataId = configInfo.getDataId(); - String group = configInfo.getGroup(); - String tenant = configInfo.getTenant(); - try { - List datumList = new ArrayList(); - int rowCount = persistService.aggrConfigInfoCount(dataId, group, tenant); - int pageCount = (int) Math.ceil(rowCount * 1.0 / PAGE_SIZE); - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - Page page = persistService.findConfigInfoAggrByPage(dataId, group, tenant, pageNo, PAGE_SIZE); - if (page != null) { - datumList.addAll(page.getPageItems()); - log.info("[merge-query] {}, {}, size/total={}/{}", new Object[] { dataId, group, datumList.size(), rowCount }); - } - } - final Timestamp time = TimeUtils.getCurrentTime(); - // 聚合 - if (datumList.size() > 0) { - ConfigInfo cf = MergeTaskProcessor.merge(dataId, group, tenant, datumList); - String aggrContent = cf.getContent(); - String localContentMD5 = ConfigService.getContentMd5(GroupKey.getKey(dataId, group)); - String aggrConetentMD5 = MD5.getInstance().getMD5String(aggrContent); - if(!StringUtils.equals(localContentMD5, aggrConetentMD5)){ - persistService.insertOrUpdate(null, null, cf, time, null, false); - log.info("[merge-ok] {}, {}, size={}, length={}, md5={}, content={}", new Object[] { dataId, group, datumList.size(), - cf.getContent().length(), cf.getMd5(), ContentUtils.truncateContent(cf.getContent()) }); - } - } - // 删除 - else { - persistService.removeConfigInfo(dataId, group, tenant, SystemConfig.LOCAL_IP, null); - log.warn("[merge-delete] delete config info because no datum. dataId=" + dataId + ", groupId=" + group); - } - - } catch (Throwable e) { - log.info("[merge-error] " + dataId + ", " + group + ", " + e.toString(), e); - } - FINISHED.incrementAndGet(); - if(FINISHED.get() % 100 == 0){ - log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); - } - } - log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); - } + static List> splitList(List list, int count) { + List> result = new ArrayList>(count); + for (int i = 0; i < count; i++) { + result.add(new ArrayList()); + } + for (int i = 0; i < list.size(); i++) { + ConfigInfoChanged config = list.get(i); + result.get(i % count).add(config); + } + return result; } - + + class MergeAllDataWorker extends Thread { + static final int PAGE_SIZE = 10000; + + private List configInfoList; + + public MergeAllDataWorker(List configInfoList) { + super("MergeAllDataWorker"); + this.configInfoList = configInfoList; + } + + @Override + public void run() { + for (ConfigInfoChanged configInfo : configInfoList) { + String dataId = configInfo.getDataId(); + String group = configInfo.getGroup(); + String tenant = configInfo.getTenant(); + try { + List datumList = new ArrayList(); + int rowCount = persistService.aggrConfigInfoCount(dataId, group, tenant); + int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE); + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + Page page = persistService.findConfigInfoAggrByPage(dataId, group, tenant, + pageNo, PAGE_SIZE); + if (page != null) { + datumList.addAll(page.getPageItems()); + log.info("[merge-query] {}, {}, size/total={}/{}", dataId, group, datumList.size(), + rowCount); + } + } + + final Timestamp time = TimeUtils.getCurrentTime(); + // 聚合 + if (datumList.size() > 0) { + ConfigInfo cf = MergeTaskProcessor.merge(dataId, group, tenant, datumList); + String aggrContent = cf.getContent(); + String localContentMD5 = ConfigService.getContentMd5(GroupKey.getKey(dataId, group)); + String aggrConetentMD5 = MD5.getInstance().getMD5String(aggrContent); + if (!StringUtils.equals(localContentMD5, aggrConetentMD5)) { + persistService.insertOrUpdate(null, null, cf, time, null, false); + log.info("[merge-ok] {}, {}, size={}, length={}, md5={}, content={}", dataId, group, + datumList.size(), cf.getContent().length(), cf.getMd5(), + ContentUtils.truncateContent(cf.getContent())); + } + } + // 删除 + else { + persistService.removeConfigInfo(dataId, group, tenant, LOCAL_IP, null); + log.warn("[merge-delete] delete config info because no datum. dataId=" + dataId + ", groupId=" + + group); + } + + } catch (Throwable e) { + log.info("[merge-error] " + dataId + ", " + group + ", " + e.toString(), e); + } + FINISHED.incrementAndGet(); + if (FINISHED.get() % 100 == 0) { + log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); + } + } + log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); + } + } + /** - * 全量dump间隔 + * 全量dump间隔 */ - static final int DUMP_ALL_INTERVAL_IN_MINUTE = 6 * 60; - /** - * 全量dump间隔 - */ - static final int INITIAL_DELAY_IN_MINUTE = 6 * 60; - + static final int DUMP_ALL_INTERVAL_IN_MINUTE = 6 * 60; + /** + * 全量dump间隔 + */ + static final int INITIAL_DELAY_IN_MINUTE = 6 * 60; + private TaskManager dumpTaskMgr; private TaskManager dumpAllTaskMgr; - + private static final Logger log = LoggerFactory.getLogger(DumpService.class); - + static final AtomicInteger FINISHED = new AtomicInteger(); - + static final int INIT_THREAD_COUNT = 10; int total = 0; private final static String TRUE_STR = "true"; private final static String BETA_TABLE_NAME = "config_info_beta"; private final static String TAG_TABLE_NAME = "config_info_tag"; - + Boolean isQuickStart = false; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpTask.java b/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpTask.java index e3c2453da..2c93dacb1 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpTask.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpTask.java @@ -15,22 +15,13 @@ */ package com.alibaba.nacos.config.server.service.dump; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; - -import java.sql.Timestamp; -import java.util.List; - import com.alibaba.nacos.config.server.manager.AbstractTask; import com.alibaba.nacos.config.server.manager.TaskProcessor; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfo4Beta; import com.alibaba.nacos.config.server.model.ConfigInfo4Tag; import com.alibaba.nacos.config.server.model.Page; -import com.alibaba.nacos.config.server.service.AggrWhitelist; -import com.alibaba.nacos.config.server.service.ClientIpWhiteList; -import com.alibaba.nacos.config.server.service.ConfigService; -import com.alibaba.nacos.config.server.service.PersistService; -import com.alibaba.nacos.config.server.service.SwitchService; +import com.alibaba.nacos.config.server.service.*; import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoWrapper; @@ -40,48 +31,53 @@ import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5; import com.alibaba.nacos.config.server.utils.StringUtils; +import java.sql.Timestamp; +import java.util.List; + +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; + /** * Dump data task - * @author Nacos * + * @author Nacos */ public class DumpTask extends AbstractTask { - public DumpTask(String groupKey, long lastModified, String handleIp) { - this.groupKey = groupKey; - this.lastModified = lastModified; - this.handleIp = handleIp; - this.isBeta = false; - this.tag = null; - /** - * retry interval: 1s - */ - setTaskInterval(1000L); - } + public DumpTask(String groupKey, long lastModified, String handleIp) { + this.groupKey = groupKey; + this.lastModified = lastModified; + this.handleIp = handleIp; + this.isBeta = false; + this.tag = null; + /** + * retry interval: 1s + */ + setTaskInterval(1000L); + } - public DumpTask(String groupKey, long lastModified, String handleIp, boolean isBeta) { - this.groupKey = groupKey; - this.lastModified = lastModified; - this.handleIp = handleIp; - this.isBeta = isBeta; - this.tag = null; - /** - * retry interval: 1s - */ - setTaskInterval(1000L); - } - - public DumpTask(String groupKey, String tag, long lastModified, String handleIp, boolean isBeta) { - this.groupKey = groupKey; - this.lastModified = lastModified; - this.handleIp = handleIp; - this.isBeta = isBeta; - this.tag = tag; - /** - * retry interval: 1s - */ - setTaskInterval(1000L); - } + public DumpTask(String groupKey, long lastModified, String handleIp, boolean isBeta) { + this.groupKey = groupKey; + this.lastModified = lastModified; + this.handleIp = handleIp; + this.isBeta = isBeta; + this.tag = null; + /** + * retry interval: 1s + */ + setTaskInterval(1000L); + } + + public DumpTask(String groupKey, String tag, long lastModified, String handleIp, boolean isBeta) { + this.groupKey = groupKey; + this.lastModified = lastModified; + this.handleIp = handleIp; + this.isBeta = isBeta; + this.tag = tag; + /** + * retry interval: 1s + */ + setTaskInterval(1000L); + } @Override public void merge(AbstractTask task) { @@ -103,29 +99,28 @@ class DumpAllTask extends AbstractTask { } class DumpAllBetaTask extends AbstractTask { - @Override - public void merge(AbstractTask task) { - } - - static final String TASK_ID = "dumpAllBetaConfigTask"; + @Override + public void merge(AbstractTask task) { + } + + static final String TASK_ID = "dumpAllBetaConfigTask"; } class DumpAllTagTask extends AbstractTask { - @Override - public void merge(AbstractTask task) { - } - - static final String TASK_ID = "dumpAllTagConfigTask"; + @Override + public void merge(AbstractTask task) { + } + + static final String TASK_ID = "dumpAllTagConfigTask"; } class DumpChangeTask extends AbstractTask { - @Override - public void merge(AbstractTask task) { - } - - static final String TASK_ID = "dumpChangeConfigTask"; -} + @Override + public void merge(AbstractTask task) { + } + static final String TASK_ID = "dumpChangeConfigTask"; +} class DumpProcessor implements TaskProcessor { @@ -135,7 +130,7 @@ class DumpProcessor implements TaskProcessor { @Override public boolean process(String taskType, AbstractTask task) { - DumpTask dumpTask = (DumpTask) task; + DumpTask dumpTask = (DumpTask)task; String[] pair = GroupKey2.parseKey(dumpTask.groupKey); String dataId = pair[0]; String group = pair[1]; @@ -144,100 +139,97 @@ class DumpProcessor implements TaskProcessor { String handleIp = dumpTask.handleIp; boolean isBeta = dumpTask.isBeta; String tag = dumpTask.tag; - if (isBeta) { - // beta发布,则dump数据,更新beta缓存 - ConfigInfo4Beta cf = dumpService.persistService.findConfigInfo4Beta(dataId, group, tenant); - boolean result; - if (null != cf) { - result = ConfigService.dumpBeta(dataId, group, tenant, cf.getContent(), lastModified, cf.getBetaIps()); - if (result) { - ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, - ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified, - cf.getContent().length()); - } - } else { - result = ConfigService.removeBeta(dataId, group, tenant); - if (result) { - ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, - ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); - } - } - return result; - } else { - if (StringUtils.isBlank(tag)) { - ConfigInfo cf = dumpService.persistService.findConfigInfo(dataId, group, tenant); - if (dataId.equals(AggrWhitelist.AGGRIDS_METADATA)) { - if (null != cf) { - AggrWhitelist.load(cf.getContent()); - } else { - AggrWhitelist.load(null); - } - } + if (isBeta) { + // beta发布,则dump数据,更新beta缓存 + ConfigInfo4Beta cf = dumpService.persistService.findConfigInfo4Beta(dataId, group, tenant); + boolean result; + if (null != cf) { + result = ConfigService.dumpBeta(dataId, group, tenant, cf.getContent(), lastModified, cf.getBetaIps()); + if (result) { + ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, + ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified, + cf.getContent().length()); + } + } else { + result = ConfigService.removeBeta(dataId, group, tenant); + if (result) { + ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, + ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); + } + } + return result; + } else { + if (StringUtils.isBlank(tag)) { + ConfigInfo cf = dumpService.persistService.findConfigInfo(dataId, group, tenant); + if (dataId.equals(AggrWhitelist.AGGRIDS_METADATA)) { + if (null != cf) { + AggrWhitelist.load(cf.getContent()); + } else { + AggrWhitelist.load(null); + } + } - if (dataId.equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) { - if (null != cf) { - ClientIpWhiteList.load(cf.getContent()); - } else { - ClientIpWhiteList.load(null); - } - } + if (dataId.equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) { + if (null != cf) { + ClientIpWhiteList.load(cf.getContent()); + } else { + ClientIpWhiteList.load(null); + } + } - if (dataId.equals(SwitchService.SWITCH_META_DATAID)) { - if (null != cf) { - SwitchService.load(cf.getContent()); - } else { - SwitchService.load(null); - } - } + if (dataId.equals(SwitchService.SWITCH_META_DATAID)) { + if (null != cf) { + SwitchService.load(cf.getContent()); + } else { + SwitchService.load(null); + } + } - boolean result; - if (null != cf) { - result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified); + boolean result; + if (null != cf) { + result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified); - if (result) { - ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, - ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified, - cf.getContent().length()); - } - } else { - result = ConfigService.remove(dataId, group, tenant); + if (result) { + ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, + ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified, + cf.getContent().length()); + } + } else { + result = ConfigService.remove(dataId, group, tenant); - if (result) { - ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, - ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); - } - } - return result; - } else { - ConfigInfo4Tag cf = dumpService.persistService.findConfigInfo4Tag(dataId, group, tenant, tag); - // - boolean result; - if (null != cf) { - result = ConfigService.dumpTag(dataId, group, tenant, tag, cf.getContent(), lastModified); - if (result) { - ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, - ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified, - cf.getContent().length()); - } - } else { - result = ConfigService.removeTag(dataId, group, tenant, tag); - if (result) { - ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, - ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); - } - } - return result; - } - } - + if (result) { + ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, + ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); + } + } + return result; + } else { + ConfigInfo4Tag cf = dumpService.persistService.findConfigInfo4Tag(dataId, group, tenant, tag); + // + boolean result; + if (null != cf) { + result = ConfigService.dumpTag(dataId, group, tenant, tag, cf.getContent(), lastModified); + if (result) { + ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, + ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified, + cf.getContent().length()); + } + } else { + result = ConfigService.removeTag(dataId, group, tenant, tag); + if (result) { + ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp, + ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); + } + } + return result; + } + } - } final DumpService dumpService; } - class DumpAllProcessor implements TaskProcessor { DumpAllProcessor(DumpService dumpService) { @@ -246,45 +238,43 @@ class DumpAllProcessor implements TaskProcessor { } @Override - public boolean process(String taskType, AbstractTask task) { - long currentMaxId = persistService.findConfigMaxId(); - long lastMaxId = 0; - while (lastMaxId < currentMaxId) { - Page page = persistService.findAllConfigInfoFragment(lastMaxId, - PAGE_SIZE); - if (page != null && page.getPageItems() != null) { - for (PersistService.ConfigInfoWrapper cf : page.getPageItems()) { - long id = cf.getId(); - lastMaxId = id > lastMaxId ? id : lastMaxId; - if (cf.getDataId().equals(AggrWhitelist.AGGRIDS_METADATA)) { - AggrWhitelist.load(cf.getContent()); - } + public boolean process(String taskType, AbstractTask task) { + long currentMaxId = persistService.findConfigMaxId(); + long lastMaxId = 0; + while (lastMaxId < currentMaxId) { + Page page = persistService.findAllConfigInfoFragment(lastMaxId, + PAGE_SIZE); + if (page != null && page.getPageItems() != null) { + for (PersistService.ConfigInfoWrapper cf : page.getPageItems()) { + long id = cf.getId(); + lastMaxId = id > lastMaxId ? id : lastMaxId; + if (cf.getDataId().equals(AggrWhitelist.AGGRIDS_METADATA)) { + AggrWhitelist.load(cf.getContent()); + } - if (cf.getDataId().equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) { - ClientIpWhiteList.load(cf.getContent()); - } + if (cf.getDataId().equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) { + ClientIpWhiteList.load(cf.getContent()); + } - if (cf.getDataId().equals(SwitchService.SWITCH_META_DATAID)) { - SwitchService.load(cf.getContent()); - } + if (cf.getDataId().equals(SwitchService.SWITCH_META_DATAID)) { + SwitchService.load(cf.getContent()); + } - boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(), - cf.getLastModified()); - - final String content = cf.getContent(); - final String md5 = MD5.getInstance().getMD5String(content); - LogUtil.dumpLog.info("[dump-all-ok] {}, {}, length={}, md5={}", - new Object[] { GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), - content.length(), md5 }); - } - defaultLog.info("[all-dump] {} / {}", lastMaxId, currentMaxId); - } else { - lastMaxId += PAGE_SIZE; - } - } - return true; - } + boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(), + cf.getLastModified()); + final String content = cf.getContent(); + final String md5 = MD5.getInstance().getMD5String(content); + LogUtil.dumpLog.info("[dump-all-ok] {}, {}, length={}, md5={}", + GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), content.length(), md5); + } + defaultLog.info("[all-dump] {} / {}", lastMaxId, currentMaxId); + } else { + lastMaxId += PAGE_SIZE; + } + } + return true; + } static final int PAGE_SIZE = 1000; @@ -293,152 +283,150 @@ class DumpAllProcessor implements TaskProcessor { } class DumpAllBetaProcessor implements TaskProcessor { - - DumpAllBetaProcessor(DumpService dumpService) { - this.dumpService = dumpService; - this.persistService = dumpService.persistService; - } - - @Override - public boolean process(String taskType, AbstractTask task) { - int rowCount = persistService.configInfoBetaCount(); - int pageCount = (int) Math.ceil(rowCount * 1.0 / PAGE_SIZE); - - int actualRowCount = 0; - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - Page page = persistService.findAllConfigInfoBetaForDumpAll(pageNo, PAGE_SIZE); - if (page != null) { - for (ConfigInfoBetaWrapper cf : page.getPageItems()) { - boolean result = ConfigService.dumpBeta(cf.getDataId(), cf.getGroup(), cf.getTenant(), - cf.getContent(), cf.getLastModified(), cf.getBetaIps()); - LogUtil.dumpLog.info("[dump-all-beta-ok] result={}, {}, {}, length={}, md5={}", - new Object[] { result, GroupKey2.getKey(cf.getDataId(), cf.getGroup()), - cf.getLastModified(), cf.getContent().length(), cf.getMd5() }); - } - actualRowCount += page.getPageItems().size(); - defaultLog.info("[all-dump-beta] {} / {}", actualRowCount, rowCount); - } - } - return true; - } - + DumpAllBetaProcessor(DumpService dumpService) { + this.dumpService = dumpService; + this.persistService = dumpService.persistService; + } - static final int PAGE_SIZE = 1000; - - final DumpService dumpService; - final PersistService persistService; + @Override + public boolean process(String taskType, AbstractTask task) { + int rowCount = persistService.configInfoBetaCount(); + int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE); + + int actualRowCount = 0; + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + Page page = persistService.findAllConfigInfoBetaForDumpAll(pageNo, PAGE_SIZE); + if (page != null) { + for (ConfigInfoBetaWrapper cf : page.getPageItems()) { + boolean result = ConfigService.dumpBeta(cf.getDataId(), cf.getGroup(), cf.getTenant(), + cf.getContent(), cf.getLastModified(), cf.getBetaIps()); + LogUtil.dumpLog.info("[dump-all-beta-ok] result={}, {}, {}, length={}, md5={}", result, + GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), cf.getContent() + .length(), cf.getMd5()); + } + + actualRowCount += page.getPageItems().size(); + defaultLog.info("[all-dump-beta] {} / {}", actualRowCount, rowCount); + } + } + return true; + } + + static final int PAGE_SIZE = 1000; + + final DumpService dumpService; + final PersistService persistService; } class DumpAllTagProcessor implements TaskProcessor { - - DumpAllTagProcessor(DumpService dumpService) { - this.dumpService = dumpService; - this.persistService = dumpService.persistService; - } - - @Override - public boolean process(String taskType, AbstractTask task) { - int rowCount = persistService.configInfoTagCount(); - int pageCount = (int) Math.ceil(rowCount * 1.0 / PAGE_SIZE); - - int actualRowCount = 0; - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - Page page = persistService.findAllConfigInfoTagForDumpAll(pageNo, PAGE_SIZE); - if (page != null) { - for (ConfigInfoTagWrapper cf : page.getPageItems()) { - boolean result = ConfigService.dumpTag(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getTag(), - cf.getContent(), cf.getLastModified()); - LogUtil.dumpLog.info("[dump-all-Tag-ok] result={}, {}, {}, length={}, md5={}", - new Object[] { result, GroupKey2.getKey(cf.getDataId(), cf.getGroup()), - cf.getLastModified(), cf.getContent().length(), cf.getMd5() }); - } - - actualRowCount += page.getPageItems().size(); - defaultLog.info("[all-dump-tag] {} / {}", actualRowCount, rowCount); - } - } - return true; - } - - static final int PAGE_SIZE = 1000; - - final DumpService dumpService; - final PersistService persistService; + DumpAllTagProcessor(DumpService dumpService) { + this.dumpService = dumpService; + this.persistService = dumpService.persistService; + } + + @Override + public boolean process(String taskType, AbstractTask task) { + int rowCount = persistService.configInfoTagCount(); + int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE); + + int actualRowCount = 0; + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + Page page = persistService.findAllConfigInfoTagForDumpAll(pageNo, PAGE_SIZE); + if (page != null) { + for (ConfigInfoTagWrapper cf : page.getPageItems()) { + boolean result = ConfigService.dumpTag(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getTag(), + cf.getContent(), cf.getLastModified()); + LogUtil.dumpLog.info("[dump-all-Tag-ok] result={}, {}, {}, length={}, md5={}", result, + GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), cf.getContent() + .length(), cf.getMd5()); + } + + actualRowCount += page.getPageItems().size(); + defaultLog.info("[all-dump-tag] {} / {}", actualRowCount, rowCount); + } + } + return true; + } + + static final int PAGE_SIZE = 1000; + + final DumpService dumpService; + final PersistService persistService; } class DumpChangeProcessor implements TaskProcessor { - DumpChangeProcessor(DumpService dumpService, Timestamp startTime, - Timestamp endTime) { - this.dumpService = dumpService; - this.persistService = dumpService.persistService; - this.startTime = startTime; - this.endTime = endTime; - } + DumpChangeProcessor(DumpService dumpService, Timestamp startTime, + Timestamp endTime) { + this.dumpService = dumpService; + this.persistService = dumpService.persistService; + this.startTime = startTime; + this.endTime = endTime; + } - @Override - public boolean process(String taskType, AbstractTask task) { - LogUtil.defaultLog.warn("quick start; startTime:{},endTime:{}", - startTime, endTime); - LogUtil.defaultLog.warn("updateMd5 start"); - long startUpdateMd5 = System.currentTimeMillis(); - List updateMd5List = persistService - .listAllGroupKeyMd5(); - LogUtil.defaultLog.warn("updateMd5 count:{}", updateMd5List.size()); - for (ConfigInfoWrapper config : updateMd5List) { - final String groupKey = GroupKey2.getKey(config.getDataId(), - config.getGroup()); - ConfigService.updateMd5(groupKey, config.getMd5(), - config.getLastModified()); - } - long endUpdateMd5 = System.currentTimeMillis(); - LogUtil.defaultLog.warn("updateMd5 done,cost:{}", endUpdateMd5 - - startUpdateMd5); + @Override + public boolean process(String taskType, AbstractTask task) { + LogUtil.defaultLog.warn("quick start; startTime:{},endTime:{}", + startTime, endTime); + LogUtil.defaultLog.warn("updateMd5 start"); + long startUpdateMd5 = System.currentTimeMillis(); + List updateMd5List = persistService + .listAllGroupKeyMd5(); + LogUtil.defaultLog.warn("updateMd5 count:{}", updateMd5List.size()); + for (ConfigInfoWrapper config : updateMd5List) { + final String groupKey = GroupKey2.getKey(config.getDataId(), + config.getGroup()); + ConfigService.updateMd5(groupKey, config.getMd5(), + config.getLastModified()); + } + long endUpdateMd5 = System.currentTimeMillis(); + LogUtil.defaultLog.warn("updateMd5 done,cost:{}", endUpdateMd5 + - startUpdateMd5); - LogUtil.defaultLog.warn("deletedConfig start"); - long startDeletedConfigTime = System.currentTimeMillis(); - List configDeleted = persistService.findDeletedConfig( - startTime, endTime); - LogUtil.defaultLog.warn("deletedConfig count:{}", configDeleted.size()); - for (ConfigInfo configInfo : configDeleted) { - if (persistService.findConfigInfo(configInfo.getDataId(), configInfo.getGroup(), - configInfo.getTenant()) == null) { - ConfigService.remove(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); - } - } - long endDeletedConfigTime = System.currentTimeMillis(); - LogUtil.defaultLog.warn("deletedConfig done,cost:{}", - endDeletedConfigTime - startDeletedConfigTime); + LogUtil.defaultLog.warn("deletedConfig start"); + long startDeletedConfigTime = System.currentTimeMillis(); + List configDeleted = persistService.findDeletedConfig( + startTime, endTime); + LogUtil.defaultLog.warn("deletedConfig count:{}", configDeleted.size()); + for (ConfigInfo configInfo : configDeleted) { + if (persistService.findConfigInfo(configInfo.getDataId(), configInfo.getGroup(), + configInfo.getTenant()) == null) { + ConfigService.remove(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); + } + } + long endDeletedConfigTime = System.currentTimeMillis(); + LogUtil.defaultLog.warn("deletedConfig done,cost:{}", + endDeletedConfigTime - startDeletedConfigTime); - LogUtil.defaultLog.warn("changeConfig start"); - long startChangeConfigTime = System.currentTimeMillis(); - List changeConfigs = persistService - .findChangeConfig(startTime, endTime); - LogUtil.defaultLog.warn("changeConfig count:{}", changeConfigs.size()); - for (PersistService.ConfigInfoWrapper cf : changeConfigs) { - boolean result = ConfigService.dumpChange(cf.getDataId(), cf.getGroup(), cf.getTenant(), - cf.getContent(), cf.getLastModified()); - final String content = cf.getContent(); - final String md5 = MD5.getInstance().getMD5String(content); - LogUtil.defaultLog.info( - "[dump-change-ok] {}, {}, length={}, md5={}", - new Object[] { - GroupKey2.getKey(cf.getDataId(), cf.getGroup()), - cf.getLastModified(), content.length(), md5 }); - } - ConfigService.reloadConfig(); - long endChangeConfigTime = System.currentTimeMillis(); - LogUtil.defaultLog.warn("changeConfig done,cost:{}", - endChangeConfigTime - startChangeConfigTime); - return true; - } + LogUtil.defaultLog.warn("changeConfig start"); + long startChangeConfigTime = System.currentTimeMillis(); + List changeConfigs = persistService + .findChangeConfig(startTime, endTime); + LogUtil.defaultLog.warn("changeConfig count:{}", changeConfigs.size()); + for (PersistService.ConfigInfoWrapper cf : changeConfigs) { + boolean result = ConfigService.dumpChange(cf.getDataId(), cf.getGroup(), cf.getTenant(), + cf.getContent(), cf.getLastModified()); + final String content = cf.getContent(); + final String md5 = MD5.getInstance().getMD5String(content); + LogUtil.defaultLog.info( + "[dump-change-ok] {}, {}, length={}, md5={}", + new Object[] { + GroupKey2.getKey(cf.getDataId(), cf.getGroup()), + cf.getLastModified(), content.length(), md5}); + } + ConfigService.reloadConfig(); + long endChangeConfigTime = System.currentTimeMillis(); + LogUtil.defaultLog.warn("changeConfig done,cost:{}", + endChangeConfigTime - startChangeConfigTime); + return true; + } - // ===================== - - final DumpService dumpService; - final PersistService persistService; - final Timestamp startTime; - final Timestamp endTime; + // ===================== + + final DumpService dumpService; + final PersistService persistService; + final Timestamp startTime; + final Timestamp endTime; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDataTask.java b/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDataTask.java index b211358b6..ac4bf2ad1 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDataTask.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDataTask.java @@ -19,7 +19,7 @@ import com.alibaba.nacos.config.server.manager.AbstractTask; /** * 表示对数据进行聚合的任务。 - * + * * @author jiuRen */ class MergeDataTask extends AbstractTask { @@ -27,17 +27,17 @@ class MergeDataTask extends AbstractTask { MergeDataTask(String dataId, String groupId, String tenant, String clientIp) { this(dataId, groupId, tenant, null, clientIp); } - + MergeDataTask(String dataId, String groupId, String tenant, String tag, String clientIp) { - this.dataId = dataId; - this.groupId = groupId; - this.tenant = tenant; - this.tag = tag; - this.clientIp = clientIp; - - // 聚合延迟 - setTaskInterval(DELAY); - setLastProcessTime(System.currentTimeMillis()); + this.dataId = dataId; + this.groupId = groupId; + this.tenant = tenant; + this.tag = tag; + this.clientIp = clientIp; + + // 聚合延迟 + setTaskInterval(DELAY); + setLastProcessTime(System.currentTimeMillis()); } @Override @@ -48,18 +48,17 @@ class MergeDataTask extends AbstractTask { return toString(); } - @Override - public String toString() { - return "MergeTask[" + dataId + ", " + groupId + ", " + tenant + ", " + clientIp + "]"; - } - - public String getClientIp() { - return clientIp; - } + @Override + public String toString() { + return "MergeTask[" + dataId + ", " + groupId + ", " + tenant + ", " + clientIp + "]"; + } + public String getClientIp() { + return clientIp; + } static final long DELAY = 0L; - + final String dataId; final String groupId; final String tenant; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDatumService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDatumService.java index 0bc686ce8..3ead310c1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDatumService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeDatumService.java @@ -15,16 +15,6 @@ */ package com.alibaba.nacos.config.server.service.merge; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - import com.alibaba.nacos.config.server.manager.TaskManager; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoAggr; @@ -32,15 +22,24 @@ import com.alibaba.nacos.config.server.model.ConfigInfoChanged; import com.alibaba.nacos.config.server.model.Page; import com.alibaba.nacos.config.server.service.PersistService; import com.alibaba.nacos.config.server.utils.ContentUtils; -import com.alibaba.nacos.config.server.utils.SystemConfig; import com.alibaba.nacos.config.server.utils.TimeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * 数据聚合服务。 - * + *

* 启动时做全量聚合 + 修改数据触发的单条聚合 - * + * * @author jiuRen */ @Service @@ -50,7 +49,7 @@ public class MergeDatumService { static final int INIT_THREAD_COUNT = 40; static final AtomicInteger FINISHED = new AtomicInteger(); static int total = 0; - + @Autowired public MergeDatumService(PersistService persistService) { this.persistService = persistService; @@ -58,17 +57,17 @@ public class MergeDatumService { mergeTasks.setDefaultTaskProcessor(new MergeTaskProcessor(persistService, this)); } - - static List> splitList(List list, int count){ - List> result = new ArrayList>(count); - for(int i = 0 ; i < count ; i++){ - result.add(new ArrayList()); - } - for(int i = 0; i < list.size() ; i++){ - ConfigInfoChanged config = list.get(i); - result.get(i % count).add(config); - } - return result; + + static List> splitList(List list, int count) { + List> result = new ArrayList>(count); + for (int i = 0; i < count; i++) { + result.add(new ArrayList()); + } + for (int i = 0; i < list.size(); i++) { + ConfigInfoChanged config = list.get(i); + result.get(i % count).add(config); + } + return result; } /** @@ -78,78 +77,83 @@ public class MergeDatumService { MergeDataTask task = new MergeDataTask(dataId, groupId, tenant, tag, clientIp); mergeTasks.addTask(task.getId(), task); } - + /** * 数据变更后调用,添加聚合任务 */ public void addMergeTask(String dataId, String groupId, String tenant, String clientIp) { - MergeDataTask task = new MergeDataTask(dataId, groupId, tenant, clientIp); - mergeTasks.addTask(task.getId(), task); + MergeDataTask task = new MergeDataTask(dataId, groupId, tenant, clientIp); + mergeTasks.addTask(task.getId(), task); } - - public void mergeAll() { - for (ConfigInfoChanged item : persistService.findAllAggrGroup()) { - addMergeTask(item.getDataId(), item.getGroup(), item.getTenant(), SystemConfig.LOCAL_IP); - } - } - - class MergeAllDataWorker extends Thread{ - static final int PAGE_SIZE = 10000; - - private List configInfoList; - public MergeAllDataWorker(List configInfoList) { - super("MergeAllDataWorker"); - this.configInfoList = configInfoList; - } - - @Override - public void run() { - for (ConfigInfoChanged configInfo : configInfoList) { - String dataId = configInfo.getDataId(); - String group = configInfo.getGroup(); - String tenant = configInfo.getTenant(); - try { - List datumList = new ArrayList(); - int rowCount = persistService.aggrConfigInfoCount(dataId, group, tenant); - int pageCount = (int) Math.ceil(rowCount * 1.0 / PAGE_SIZE); - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - Page page = persistService.findConfigInfoAggrByPage(dataId, group, tenant, pageNo, PAGE_SIZE); - if (page != null) { - datumList.addAll(page.getPageItems()); - log.info("[merge-query] {}, {}, size/total={}/{}", new Object[] { dataId, group, datumList.size(), rowCount }); - } - } - final Timestamp time = TimeUtils.getCurrentTime(); - // 聚合 - if (datumList.size() > 0) { - ConfigInfo cf = MergeTaskProcessor.merge(dataId, group, tenant, datumList); - persistService.insertOrUpdate(null, null, cf, time, null, false); - log.info("[merge-ok] {}, {}, size={}, length={}, md5={}, content={}", new Object[] { dataId, group, datumList.size(), - cf.getContent().length(), cf.getMd5(), ContentUtils.truncateContent(cf.getContent()) }); - } - // 删除 - else { - persistService.removeConfigInfo(dataId, group, tenant, SystemConfig.LOCAL_IP, null); - log.warn("[merge-delete] delete config info because no datum. dataId=" + dataId + ", groupId=" + group); - } - - } catch (Exception e) { - log.info("[merge-error] " + dataId + ", " + group + ", " + e.toString(), e); - } - FINISHED.incrementAndGet(); - if(FINISHED.get() % 100 == 0){ - log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); - } - } - log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); - } + public void mergeAll() { + for (ConfigInfoChanged item : persistService.findAllAggrGroup()) { + addMergeTask(item.getDataId(), item.getGroup(), item.getTenant(), LOCAL_IP); + } + } + + class MergeAllDataWorker extends Thread { + static final int PAGE_SIZE = 10000; + + private List configInfoList; + + public MergeAllDataWorker(List configInfoList) { + super("MergeAllDataWorker"); + this.configInfoList = configInfoList; + } + + @Override + public void run() { + for (ConfigInfoChanged configInfo : configInfoList) { + String dataId = configInfo.getDataId(); + String group = configInfo.getGroup(); + String tenant = configInfo.getTenant(); + try { + List datumList = new ArrayList(); + int rowCount = persistService.aggrConfigInfoCount(dataId, group, tenant); + int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE); + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + Page page = persistService.findConfigInfoAggrByPage(dataId, group, tenant, + pageNo, PAGE_SIZE); + if (page != null) { + datumList.addAll(page.getPageItems()); + log.info("[merge-query] {}, {}, size/total={}/{}", dataId, group, datumList.size(), + rowCount); + } + } + + final Timestamp time = TimeUtils.getCurrentTime(); + // 聚合 + if (datumList.size() > 0) { + ConfigInfo cf = MergeTaskProcessor.merge(dataId, group, tenant, datumList); + persistService.insertOrUpdate(null, null, cf, time, null, false); + log.info("[merge-ok] {}, {}, size={}, length={}, md5={}, content={}", dataId, group, + datumList.size(), cf.getContent().length(), cf.getMd5(), + ContentUtils.truncateContent(cf.getContent())); + } + // 删除 + else { + persistService.removeConfigInfo(dataId, group, tenant, LOCAL_IP, null); + log.warn("[merge-delete] delete config info because no datum. dataId=" + dataId + ", groupId=" + + group); + } + + } catch (Exception e) { + log.info("[merge-error] " + dataId + ", " + group + ", " + e.toString(), e); + } + FINISHED.incrementAndGet(); + if (FINISHED.get() % 100 == 0) { + log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); + } + } + log.info("[all-merge-dump] {} / {}", FINISHED.get(), total); + } } // ===================== private static final Logger log = LoggerFactory.getLogger(MergeDatumService.class); - + final TaskManager mergeTasks; - + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeTaskProcessor.java b/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeTaskProcessor.java index 38ac2aada..cf658c7db 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeTaskProcessor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/merge/MergeTaskProcessor.java @@ -26,22 +26,21 @@ import com.alibaba.nacos.config.server.service.PersistService; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.ContentUtils; import com.alibaba.nacos.config.server.utils.StringUtils; -import com.alibaba.nacos.config.server.utils.SystemConfig; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.config.server.utils.event.EventDispatcher; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Timestamp; -import java.util.List; import java.util.ArrayList; +import java.util.List; +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * Merge task processor - * @author Nacos * + * @author Nacos */ public class MergeTaskProcessor implements TaskProcessor { final int PAGE_SIZE = 10000; @@ -53,25 +52,24 @@ public class MergeTaskProcessor implements TaskProcessor { @Override public boolean process(String taskType, AbstractTask task) { - MergeDataTask mergeTask = (MergeDataTask) task; + MergeDataTask mergeTask = (MergeDataTask)task; final String dataId = mergeTask.dataId; final String group = mergeTask.groupId; final String tenant = mergeTask.tenant; final String tag = mergeTask.tag; final String clientIp = mergeTask.getClientIp(); try { - List datumList = new ArrayList(); - int rowCount = persistService.aggrConfigInfoCount(dataId, group, tenant); - int pageCount = (int) Math.ceil(rowCount * 1.0 / PAGE_SIZE); - for (int pageNo = 1; pageNo <= pageCount; pageNo++) { - Page page = persistService.findConfigInfoAggrByPage(dataId, group, tenant, pageNo, - PAGE_SIZE); - if (page != null) { - datumList.addAll(page.getPageItems()); - log.info("[merge-query] {}, {}, size/total={}/{}", - new Object[] { dataId, group, datumList.size(), rowCount }); - } - } + List datumList = new ArrayList(); + int rowCount = persistService.aggrConfigInfoCount(dataId, group, tenant); + int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE); + for (int pageNo = 1; pageNo <= pageCount; pageNo++) { + Page page = persistService.findConfigInfoAggrByPage(dataId, group, tenant, pageNo, + PAGE_SIZE); + if (page != null) { + datumList.addAll(page.getPageItems()); + log.info("[merge-query] {}, {}, size/total={}/{}", dataId, group, datumList.size(), rowCount); + } + } final Timestamp time = TimeUtils.getCurrentTime(); // 聚合 @@ -80,44 +78,44 @@ public class MergeTaskProcessor implements TaskProcessor { persistService.insertOrUpdate(null, null, cf, time, null); - log.info("[merge-ok] {}, {}, size={}, length={}, md5={}, content={}", new Object[] { - dataId, group, datumList.size(), cf.getContent().length(), cf.getMd5(), - ContentUtils.truncateContent(cf.getContent()) }); + log.info("[merge-ok] {}, {}, size={}, length={}, md5={}, content={}", dataId, group, datumList.size(), + cf.getContent().length(), cf.getMd5(), ContentUtils.truncateContent(cf.getContent())); - ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), SystemConfig.LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_MERGE, cf.getContent()); + ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), LOCAL_IP, + ConfigTraceService.PERSISTENCE_EVENT_MERGE, cf.getContent()); } // 删除 else { - if (StringUtils.isBlank(tag)) { - persistService.removeConfigInfo(dataId, group, tenant, clientIp, null); - } else { - persistService.removeConfigInfoTag(dataId, group, tenant, tag, clientIp, null); - } + if (StringUtils.isBlank(tag)) { + persistService.removeConfigInfo(dataId, group, tenant, clientIp, null); + } else { + persistService.removeConfigInfoTag(dataId, group, tenant, tag, clientIp, null); + } log.warn("[merge-delete] delete config info because no datum. dataId=" + dataId - + ", groupId=" + group); + + ", groupId=" + group); - ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), SystemConfig.LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null); + ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), LOCAL_IP, + ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null); } EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime())); } catch (Exception e) { - mergeService.addMergeTask(dataId, group, tenant, mergeTask.getClientIp()); + mergeService.addMergeTask(dataId, group, tenant, mergeTask.getClientIp()); log.info("[merge-error] " + dataId + ", " + group + ", " + e.toString(), e); } return true; } - - - public static ConfigInfo merge(String dataId, String group, String tenant, List datumList) { + + public static ConfigInfo merge(String dataId, String group, String tenant, List datumList) { StringBuilder sb = new StringBuilder(); String appName = null; for (ConfigInfoAggr aggrInfo : datumList) { - if (aggrInfo.getAppName() != null) { - appName = aggrInfo.getAppName(); - } + if (aggrInfo.getAppName() != null) { + appName = aggrInfo.getAppName(); + } sb.append(aggrInfo.getContent()); sb.append(Constants.NACOS_LINE_SEPARATOR); } @@ -128,7 +126,7 @@ public class MergeTaskProcessor implements TaskProcessor { // ===================== private static final Logger log = LoggerFactory.getLogger(MergeTaskProcessor.class); - + private PersistService persistService; private MergeDatumService mergeService; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyService.java index 3afdbc3d1..61492bde5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyService.java @@ -15,19 +15,13 @@ */ package com.alibaba.nacos.config.server.service.notify; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent; +import com.alibaba.nacos.config.server.service.ServerListService; +import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; +import com.alibaba.nacos.config.server.utils.*; +import com.alibaba.nacos.config.server.utils.event.EventDispatcher.AbstractEventListener; +import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; @@ -41,326 +35,327 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent; -import com.alibaba.nacos.config.server.service.ServerListService; -import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; -import com.alibaba.nacos.config.server.utils.LogUtil; -import com.alibaba.nacos.config.server.utils.PropertyUtil; -import com.alibaba.nacos.config.server.utils.RunningConfigUtils; -import com.alibaba.nacos.config.server.utils.StringUtils; -import com.alibaba.nacos.config.server.utils.SystemConfig; -import com.alibaba.nacos.config.server.utils.event.EventDispatcher.AbstractEventListener; -import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.*; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * Async notify service + * * @author Nacos */ @Service public class AsyncNotifyService extends AbstractEventListener { - @Override - public List> interest() { - List> types = new ArrayList>(); - // 触发配置变更同步通知 - types.add(ConfigDataChangeEvent.class); - return types; - } + @Override + public List> interest() { + List> types = new ArrayList>(); + // 触发配置变更同步通知 + types.add(ConfigDataChangeEvent.class); + return types; + } - @Override - public void onEvent(Event event) { - - // 并发产生 ConfigDataChangeEvent - if (event instanceof ConfigDataChangeEvent) { - ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event; - long dumpTs = evt.lastModifiedTs; - String dataId = evt.dataId; - String group = evt.group; - String tenant = evt.tenant; - String tag = evt.tag; - List ipList = serverListService.getServerList(); - - // 其实这里任何类型队列都可以 - Queue queue = new LinkedList(); - for (int i = 0; i < ipList.size(); i++) { - queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, (String) ipList.get(i), evt.isBeta)); - } - EXCUTOR.execute(new AsyncTask(httpclient, queue)); - } - } + @Override + public void onEvent(Event event) { - @Autowired - public AsyncNotifyService(ServerListService serverListService) { - this.serverListService = serverListService; - httpclient.start(); - } + // 并发产生 ConfigDataChangeEvent + if (event instanceof ConfigDataChangeEvent) { + ConfigDataChangeEvent evt = (ConfigDataChangeEvent)event; + long dumpTs = evt.lastModifiedTs; + String dataId = evt.dataId; + String group = evt.group; + String tenant = evt.tenant; + String tag = evt.tag; + List ipList = serverListService.getServerList(); - public Executor getExecutor(){ - return EXCUTOR; - } - @SuppressWarnings("PMD.ThreadPoolCreationRule") - private static final Executor EXCUTOR = Executors.newScheduledThreadPool(100,new NotifyThreadFactory()); + // 其实这里任何类型队列都可以 + Queue queue = new LinkedList(); + for (int i = 0; i < ipList.size(); i++) { + queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, (String)ipList.get(i), evt.isBeta)); + } + EXCUTOR.execute(new AsyncTask(httpclient, queue)); + } + } - private RequestConfig requestConfig = RequestConfig.custom() - .setConnectTimeout(PropertyUtil.getNotifyConnectTimeout()) - .setSocketTimeout(PropertyUtil.getNotifySocketTimeout()).build(); + @Autowired + public AsyncNotifyService(ServerListService serverListService) { + this.serverListService = serverListService; + httpclient.start(); + } - private CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() - .setDefaultRequestConfig(requestConfig).build(); + public Executor getExecutor() { + return EXCUTOR; + } - static final Logger log = LoggerFactory.getLogger(AsyncNotifyService.class); + @SuppressWarnings("PMD.ThreadPoolCreationRule") + private static final Executor EXCUTOR = Executors.newScheduledThreadPool(100, new NotifyThreadFactory()); - private ServerListService serverListService; - - class AsyncTask implements Runnable { + private RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(PropertyUtil.getNotifyConnectTimeout()) + .setSocketTimeout(PropertyUtil.getNotifySocketTimeout()).build(); - - public AsyncTask(CloseableHttpAsyncClient httpclient,Queue queue) { - this.httpclient = httpclient; - this.queue = queue; - } + private CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() + .setDefaultRequestConfig(requestConfig).build(); - @Override - public void run() { + static final Logger log = LoggerFactory.getLogger(AsyncNotifyService.class); - executeAsyncInvoke(); + private ServerListService serverListService; - } + class AsyncTask implements Runnable { - private void executeAsyncInvoke() { - - while (!queue.isEmpty()) { + public AsyncTask(CloseableHttpAsyncClient httpclient, Queue queue) { + this.httpclient = httpclient; + this.queue = queue; + } - NotifySingleTask task = queue.poll(); - String targetIp = task.getTargetIP(); - if (serverListService.getServerList().contains( - targetIp)) { - // 启动健康检查且有不监控的ip则直接把放到通知队列,否则通知 - if (serverListService.isHealthCheck() - && ServerListService.getServerListUnhealth().contains(targetIp)) { - // target ip 不健康,则放入通知列表中 - ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null, task.getLastModified(), - SystemConfig.LOCAL_IP, ConfigTraceService.NOTIFY_EVENT_UNHEALTH, 0, task.target); - // get delay time and set fail count to the task - int delay = getDelayTime(task); - Queue queue = new LinkedList(); - queue.add(task); - AsyncTask asyncTask = new AsyncTask(httpclient, queue); - ((ScheduledThreadPoolExecutor) EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); - } else { - HttpGet request = new HttpGet(task.url); - request.setHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED, - String.valueOf(task.getLastModified())); - request.setHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP, SystemConfig.LOCAL_IP); - if (task.isBeta) { - request.setHeader("isBeta", "true"); - } - httpclient.execute(request, new AyscNotifyCallBack(httpclient, task)); - } - } - } - } + @Override + public void run() { - private Queue queue; - private CloseableHttpAsyncClient httpclient; + executeAsyncInvoke(); - } - + } - class AyscNotifyCallBack implements FutureCallback { + private void executeAsyncInvoke() { - public AyscNotifyCallBack(CloseableHttpAsyncClient httpclient,NotifySingleTask task - ) { - this.task = task; - this.httpclient = httpclient; - } + while (!queue.isEmpty()) { - @Override - public void completed(HttpResponse response) { + NotifySingleTask task = queue.poll(); + String targetIp = task.getTargetIP(); + if (serverListService.getServerList().contains( + targetIp)) { + // 启动健康检查且有不监控的ip则直接把放到通知队列,否则通知 + if (serverListService.isHealthCheck() + && ServerListService.getServerListUnhealth().contains(targetIp)) { + // target ip 不健康,则放入通知列表中 + ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null, + task.getLastModified(), + LOCAL_IP, ConfigTraceService.NOTIFY_EVENT_UNHEALTH, 0, task.target); + // get delay time and set fail count to the task + int delay = getDelayTime(task); + Queue queue = new LinkedList(); + queue.add(task); + AsyncTask asyncTask = new AsyncTask(httpclient, queue); + ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); + } else { + HttpGet request = new HttpGet(task.url); + request.setHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED, + String.valueOf(task.getLastModified())); + request.setHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP, LOCAL_IP); + if (task.isBeta) { + request.setHeader("isBeta", "true"); + } + httpclient.execute(request, new AyscNotifyCallBack(httpclient, task)); + } + } + } + } - long delayed = System.currentTimeMillis() - task.getLastModified(); - - if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { - ConfigTraceService.logNotifyEvent(task.getDataId(), - task.getGroup(), task.getTenant(), null, task.getLastModified(), - SystemConfig.LOCAL_IP, - ConfigTraceService.NOTIFY_EVENT_OK, delayed, - task.target); - } else { - log.error("[notify-error] {}, {}, to {}, result {}", - new Object[] { task.getDataId(), task.getGroup(), - task.target, - response.getStatusLine().getStatusCode() }); - ConfigTraceService.logNotifyEvent(task.getDataId(), - task.getGroup(), task.getTenant(), null, task.getLastModified(), - SystemConfig.LOCAL_IP, - ConfigTraceService.NOTIFY_EVENT_ERROR, delayed, - task.target); + private Queue queue; + private CloseableHttpAsyncClient httpclient; - //get delay time and set fail count to the task - int delay = getDelayTime(task); - - Queue queue = new LinkedList(); + } - queue.add(task); - AsyncTask asyncTask = new AsyncTask(httpclient,queue); - - ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); - - LogUtil.notifyLog.error( - "[notify-retry] target:{} dataid:{} group:{} ts:{}", - new Object[] { task.target, task.getDataId(), - task.getGroup(), task.getLastModified() }); + class AyscNotifyCallBack implements FutureCallback { - } - HttpClientUtils.closeQuietly(response); - } + public AyscNotifyCallBack(CloseableHttpAsyncClient httpclient, NotifySingleTask task + ) { + this.task = task; + this.httpclient = httpclient; + } - @Override - public void failed(Exception ex) { + @Override + public void completed(HttpResponse response) { - long delayed = System.currentTimeMillis() - task.getLastModified(); - log.error("[notify-exception] " + task.getDataId() + ", " + task.getGroup() + ", to " + task.target + ", " - + ex.toString()); - log.debug("[notify-exception] " + task.getDataId() + ", " + task.getGroup() + ", to " + task.target + ", " - + ex.toString(), ex); - ConfigTraceService.logNotifyEvent(task.getDataId(), - task.getGroup(), task.getTenant(), null, task.getLastModified(), - SystemConfig.LOCAL_IP, - ConfigTraceService.NOTIFY_EVENT_EXCEPTION, delayed, - task.target); + long delayed = System.currentTimeMillis() - task.getLastModified(); - //get delay time and set fail count to the task - int delay = getDelayTime(task); - Queue queue = new LinkedList(); + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + ConfigTraceService.logNotifyEvent(task.getDataId(), + task.getGroup(), task.getTenant(), null, task.getLastModified(), + LOCAL_IP, + ConfigTraceService.NOTIFY_EVENT_OK, delayed, + task.target); + } else { + log.error("[notify-error] {}, {}, to {}, result {}", + new Object[] {task.getDataId(), task.getGroup(), + task.target, + response.getStatusLine().getStatusCode()}); + ConfigTraceService.logNotifyEvent(task.getDataId(), + task.getGroup(), task.getTenant(), null, task.getLastModified(), + LOCAL_IP, + ConfigTraceService.NOTIFY_EVENT_ERROR, delayed, + task.target); - queue.add(task); - AsyncTask asyncTask = new AsyncTask(httpclient,queue); - - ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); - LogUtil.notifyLog.error( - "[notify-retry] target:{} dataid:{} group:{} ts:{}", - new Object[] { task.target, task.getDataId(), - task.getGroup(), task.getLastModified() }); + //get delay time and set fail count to the task + int delay = getDelayTime(task); - } + Queue queue = new LinkedList(); - @Override - public void cancelled() { + queue.add(task); + AsyncTask asyncTask = new AsyncTask(httpclient, queue); - LogUtil.notifyLog.error( - "[notify-exception] target:{} dataid:{} group:{} ts:{}", - new Object[] { task.target, task.getGroup(), - task.getGroup(), task.getLastModified() }, - "CANCELED"); + ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); - //get delay time and set fail count to the task - int delay = getDelayTime(task); - Queue queue = new LinkedList(); - - queue.add(task); - AsyncTask asyncTask = new AsyncTask(httpclient,queue); - - ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); - LogUtil.notifyLog.error( - "[notify-retry] target:{} dataid:{} group:{} ts:{}", - new Object[] { task.target, task.getDataId(), - task.getGroup(), task.getLastModified() }); + LogUtil.notifyLog.error( + "[notify-retry] target:{} dataid:{} group:{} ts:{}", + new Object[] {task.target, task.getDataId(), + task.getGroup(), task.getLastModified()}); - } - private NotifySingleTask task; - private CloseableHttpAsyncClient httpclient; - } - + } + HttpClientUtils.closeQuietly(response); + } - static class NotifySingleTask extends NotifyTask { + @Override + public void failed(Exception ex) { - private String target; - public String url; - private boolean isBeta; - private static final String URL_PATTERN = "http://{0}{1}" + Constants.COMMUNICATION_CONTROLLER_PATH + "/dataChange" - + "?dataId={2}&group={3}"; - private static final String URL_PATTERN_TENANT = "http://{0}{1}" + Constants.COMMUNICATION_CONTROLLER_PATH - + "/dataChange" + "?dataId={2}&group={3}&tenant={4}"; - private int failCount; + long delayed = System.currentTimeMillis() - task.getLastModified(); + log.error("[notify-exception] " + task.getDataId() + ", " + task.getGroup() + ", to " + task.target + ", " + + ex.toString()); + log.debug("[notify-exception] " + task.getDataId() + ", " + task.getGroup() + ", to " + task.target + ", " + + ex.toString(), ex); + ConfigTraceService.logNotifyEvent(task.getDataId(), + task.getGroup(), task.getTenant(), null, task.getLastModified(), + LOCAL_IP, + ConfigTraceService.NOTIFY_EVENT_EXCEPTION, delayed, + task.target); - public NotifySingleTask(String dataId, String group, String tenant, long lastModified, String target) { - this(dataId, group, tenant, lastModified, target, false); - } + //get delay time and set fail count to the task + int delay = getDelayTime(task); + Queue queue = new LinkedList(); - public NotifySingleTask(String dataId, String group, String tenant, long lastModified, String target, - boolean isBeta) { - this(dataId, group, tenant, null, lastModified, target, isBeta); - } - - public NotifySingleTask(String dataId, String group, String tenant, String tag, long lastModified, - String target, boolean isBeta) { - super(dataId, group, tenant, lastModified); - this.target = target; - this.isBeta = isBeta; - try { - dataId = URLEncoder.encode(dataId, Constants.ENCODE); - group = URLEncoder.encode(group, Constants.ENCODE); - } catch (UnsupportedEncodingException e) { - log.error("URLEncoder encode error", e); - } - if (StringUtils.isBlank(tenant)) { - this.url = MessageFormat.format(URL_PATTERN, target, RunningConfigUtils.getContextPath(), dataId, - group); - } else { - this.url = MessageFormat.format(URL_PATTERN_TENANT, target, RunningConfigUtils.getContextPath(), dataId, - group, tenant); - } - if (StringUtils.isNotEmpty(tag)) { - url = url + "&tag=" + tag; - } - failCount = 0; - // this.executor = executor; - } - - public void setFailCount(int count){ - this.failCount = count; - } - - public int getFailCount(){ - return failCount; - } - public String getTargetIP(){ - return target; - } - - } - + queue.add(task); + AsyncTask asyncTask = new AsyncTask(httpclient, queue); - static class NotifyThreadFactory implements ThreadFactory { + ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); + LogUtil.notifyLog.error( + "[notify-retry] target:{} dataid:{} group:{} ts:{}", + new Object[] {task.target, task.getDataId(), + task.getGroup(), task.getLastModified()}); - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, - "com.alibaba.nacos.AsyncNotifyServiceThread"); - thread.setDaemon(true); - return thread; - } - } - - /** - * get delayTime and also set failCount to - * task;失败时间指数增加,以免断网场景不断重试无效任务,影响正常同步 - * @param task notify task - * @return delay - */ - private static int getDelayTime(NotifySingleTask task) { - int failCount = task.getFailCount(); - int delay = MINRETRYINTERVAL + failCount * failCount * INCREASESTEPS; - if (failCount <= MAXCOUNT) { - task.setFailCount(failCount + 1); - } - return delay; - } + } + + @Override + public void cancelled() { + + LogUtil.notifyLog.error( + "[notify-exception] target:{} dataid:{} group:{} ts:{}", + new Object[] {task.target, task.getGroup(), + task.getGroup(), task.getLastModified()}, + "CANCELED"); + + //get delay time and set fail count to the task + int delay = getDelayTime(task); + Queue queue = new LinkedList(); + + queue.add(task); + AsyncTask asyncTask = new AsyncTask(httpclient, queue); + + ((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS); + LogUtil.notifyLog.error( + "[notify-retry] target:{} dataid:{} group:{} ts:{}", + new Object[] {task.target, task.getDataId(), + task.getGroup(), task.getLastModified()}); + + } + + private NotifySingleTask task; + private CloseableHttpAsyncClient httpclient; + } + + static class NotifySingleTask extends NotifyTask { + + private String target; + public String url; + private boolean isBeta; + private static final String URL_PATTERN = "http://{0}{1}" + Constants.COMMUNICATION_CONTROLLER_PATH + + "/dataChange" + + "?dataId={2}&group={3}"; + private static final String URL_PATTERN_TENANT = "http://{0}{1}" + Constants.COMMUNICATION_CONTROLLER_PATH + + "/dataChange" + "?dataId={2}&group={3}&tenant={4}"; + private int failCount; + + public NotifySingleTask(String dataId, String group, String tenant, long lastModified, String target) { + this(dataId, group, tenant, lastModified, target, false); + } + + public NotifySingleTask(String dataId, String group, String tenant, long lastModified, String target, + boolean isBeta) { + this(dataId, group, tenant, null, lastModified, target, isBeta); + } + + public NotifySingleTask(String dataId, String group, String tenant, String tag, long lastModified, + String target, boolean isBeta) { + super(dataId, group, tenant, lastModified); + this.target = target; + this.isBeta = isBeta; + try { + dataId = URLEncoder.encode(dataId, Constants.ENCODE); + group = URLEncoder.encode(group, Constants.ENCODE); + } catch (UnsupportedEncodingException e) { + log.error("URLEncoder encode error", e); + } + if (StringUtils.isBlank(tenant)) { + this.url = MessageFormat.format(URL_PATTERN, target, RunningConfigUtils.getContextPath(), dataId, + group); + } else { + this.url = MessageFormat.format(URL_PATTERN_TENANT, target, RunningConfigUtils.getContextPath(), dataId, + group, tenant); + } + if (StringUtils.isNotEmpty(tag)) { + url = url + "&tag=" + tag; + } + failCount = 0; + // this.executor = executor; + } + + public void setFailCount(int count) { + this.failCount = count; + } + + public int getFailCount() { + return failCount; + } + + public String getTargetIP() { + return target; + } + + } + + static class NotifyThreadFactory implements ThreadFactory { + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, + "com.alibaba.nacos.AsyncNotifyServiceThread"); + thread.setDaemon(true); + return thread; + } + } + + /** + * get delayTime and also set failCount to task;失败时间指数增加,以免断网场景不断重试无效任务,影响正常同步 + * + * @param task notify task + * @return delay + */ + private static int getDelayTime(NotifySingleTask task) { + int failCount = task.getFailCount(); + int delay = MINRETRYINTERVAL + failCount * failCount * INCREASESTEPS; + if (failCount <= MAXCOUNT) { + task.setFailCount(failCount + 1); + } + return delay; + } + + private static int MINRETRYINTERVAL = 500; + private static int INCREASESTEPS = 1000; + private static int MAXCOUNT = 6; - private static int MINRETRYINTERVAL = 500; - private static int INCREASESTEPS = 1000; - private static int MAXCOUNT = 6; - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyService.java index 439000e16..04790dc87 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyService.java @@ -15,28 +15,25 @@ */ package com.alibaba.nacos.config.server.service.notify; +import com.alibaba.nacos.config.server.manager.TaskManager; +import com.alibaba.nacos.config.server.service.ServerListService; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.Iterator; import java.util.List; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; - -import com.alibaba.nacos.config.server.manager.TaskManager; -import com.alibaba.nacos.config.server.service.ServerListService; - - /** * 通知其他节点取最新数据的服务。 监听数据变更事件,通知所有的server。 + * * @author jiuRen */ -public class NotifyService -{ +public class NotifyService { @Autowired public NotifyService(ServerListService serverListService) { @@ -48,7 +45,7 @@ public class NotifyService } /** - * 為了方便系统beta,不改变notify.do接口,新增lastModifed参数通过Http header传递 + * 為了方便系统beta,不改变notify.do接口,新增lastModifed参数通过Http header传递 */ static public final String NOTIFY_HEADER_LAST_MODIFIED = "lastModified"; static public final String NOTIFY_HEADER_OP_HANDLE_IP = "opHandleIp"; @@ -56,14 +53,14 @@ public class NotifyService static public HttpResult invokeURL(String url, List headers, String encoding) throws IOException { HttpURLConnection conn = null; try { - conn = (HttpURLConnection) new URL(url).openConnection(); + conn = (HttpURLConnection)new URL(url).openConnection(); conn.setConnectTimeout(TIMEOUT); conn.setReadTimeout(TIMEOUT); conn.setRequestMethod("GET"); if (null != headers && !StringUtils.isEmpty(encoding)) { - for (Iterator iter = headers.iterator(); iter.hasNext();) { + for (Iterator iter = headers.iterator(); iter.hasNext(); ) { conn.addRequestProperty(iter.next(), iter.next()); } } @@ -71,13 +68,13 @@ public class NotifyService /** * 建立TCP连接 */ - conn.connect(); + conn.connect(); /** * 这里内部发送请求 */ - int respCode = conn.getResponseCode(); + int respCode = conn.getResponseCode(); String resp = null; - + if (HttpServletResponse.SC_OK == respCode) { resp = IOUtils.toString(conn.getInputStream()); } else { @@ -90,17 +87,17 @@ public class NotifyService } } } - + static public class HttpResult { final public int code; final public String content; - + public HttpResult(int code, String content) { this.code = code; this.content = content; } } - + /** * 和其他server的连接超时和socket超时 */ diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifySingleService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifySingleService.java index a2024f8cf..5fbdecadb 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifySingleService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifySingleService.java @@ -15,47 +15,39 @@ */ package com.alibaba.nacos.config.server.service.notify; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.slf4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; - import com.alibaba.nacos.config.server.manager.AbstractTask; import com.alibaba.nacos.config.server.service.ServerListService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; /** * Notify Single server - * @author Nacos * + * @author Nacos */ -public class NotifySingleService -{ +public class NotifySingleService { static class NotifyTaskProcessorWrapper extends NotifyTaskProcessor { public NotifyTaskProcessorWrapper() { - /** - * serverListManager在这里没有用了 - */ - super(null); + /** + * serverListManager在这里没有用了 + */ + super(null); } @Override - public boolean process(String taskType, AbstractTask task) { - NotifySingleTask notifyTask = (NotifySingleTask) task; - return notifyToDump(notifyTask.getDataId(), notifyTask.getGroup(), notifyTask.getTenant(), - notifyTask.getLastModified(), notifyTask.target); - } + public boolean process(String taskType, AbstractTask task) { + NotifySingleTask notifyTask = (NotifySingleTask)task; + return notifyToDump(notifyTask.getDataId(), notifyTask.getGroup(), notifyTask.getTenant(), + notifyTask.getLastModified(), notifyTask.target); + } } static class NotifySingleTask extends NotifyTask implements Runnable { @@ -65,7 +57,8 @@ public class NotifySingleService private boolean isSuccess = false; - public NotifySingleTask(String dataId, String group, String tenant, long lastModified, String target, Executor executor) { + public NotifySingleTask(String dataId, String group, String tenant, long lastModified, String target, + Executor executor) { super(dataId, group, tenant, lastModified); this.target = target; this.executor = executor; @@ -79,17 +72,18 @@ public class NotifySingleService this.isSuccess = false; LogUtil.notifyLog.error("[notify-exception] target:{} dataid:{} group:{} ts:{}", target, getDataId(), getGroup(), getLastModified()); - LogUtil.notifyLog.debug("[notify-exception] target:{} dataid:{} group:{} ts:{}", - new Object[] { target, getDataId(), getGroup(), getLastModified() }, e); + LogUtil.notifyLog.debug("[notify-exception] target:{} dataid:{} group:{} ts:{}", + new Object[] {target, getDataId(), getGroup(), getLastModified()}, e); } if (!this.isSuccess) { LogUtil.notifyLog.error("[notify-retry] target:{} dataid:{} group:{} ts:{}", target, getDataId(), getGroup(), getLastModified()); try { - ((ScheduledThreadPoolExecutor) executor).schedule(this, 500L, TimeUnit.MILLISECONDS); + ((ScheduledThreadPoolExecutor)executor).schedule(this, 500L, TimeUnit.MILLISECONDS); } catch (Exception e) { // 通知虽然失败,但是同时此前节点也下线了 - logger.warn("[notify-thread-pool] cluster remove node {}, current thread was tear down.", target, e); + logger.warn("[notify-thread-pool] cluster remove node {}, current thread was tear down.", target, + e); } } } @@ -110,7 +104,6 @@ public class NotifySingleService } } - @Autowired public NotifySingleService(ServerListService serverListService) { this.serverListService = serverListService; @@ -118,15 +111,14 @@ public class NotifySingleService } /** - * 系统启动时 or 集群扩容、下线时:单线程setupNotifyExecutors - * executors使用ConcurrentHashMap目的在于保证可见性 + * 系统启动时 or 集群扩容、下线时:单线程setupNotifyExecutors executors使用ConcurrentHashMap目的在于保证可见性 */ private void setupNotifyExecutors() { List clusterIps = serverListService.getServerList(); for (String ip : clusterIps) { // 固定线程数,无界队列(基于假设: 线程池的吞吐量不错,不会出现持续任务堆积,存在偶尔的瞬间压力) - @SuppressWarnings("PMD.ThreadPoolCreationRule") + @SuppressWarnings("PMD.ThreadPoolCreationRule") Executor executor = Executors.newScheduledThreadPool(1, new NotifyThreadFactory(ip)); if (null == executors.putIfAbsent(ip, executor)) { @@ -140,8 +132,8 @@ public class NotifySingleService /** * 集群节点下线 */ - if (!clusterIps.contains(target)) { - ThreadPoolExecutor executor = (ThreadPoolExecutor) entry.getValue(); + if (!clusterIps.contains(target)) { + ThreadPoolExecutor executor = (ThreadPoolExecutor)entry.getValue(); executor.shutdown(); executors.remove(target); logger.warn("[notify-thread-pool] tear down thread target ip {} ok.", target); @@ -150,7 +142,6 @@ public class NotifySingleService } - private final static Logger logger = LogUtil.fatalLog; private ServerListService serverListService; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTask.java b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTask.java index 5df454c61..27ccb330d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTask.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTask.java @@ -19,8 +19,8 @@ import com.alibaba.nacos.config.server.manager.AbstractTask; /** * Notify task - * @author Nacos * + * @author Nacos */ public class NotifyTask extends AbstractTask { @@ -30,41 +30,34 @@ public class NotifyTask extends AbstractTask { private long lastModified; private int failCount; - - public NotifyTask(String dataId, String group, String tenant, long lastModified) { - this.dataId = dataId; - this.group = group; - this.setTenant(tenant); - this.lastModified = lastModified; - setTaskInterval(3000L); - } - + public NotifyTask(String dataId, String group, String tenant, long lastModified) { + this.dataId = dataId; + this.group = group; + this.setTenant(tenant); + this.lastModified = lastModified; + setTaskInterval(3000L); + } public String getDataId() { return dataId; } - public void setDataId(String dataId) { this.dataId = dataId; } - public String getGroup() { return group; } - public void setGroup(String group) { this.group = group; } - public int getFailCount() { return failCount; } - public void setFailCount(int failCount) { this.failCount = failCount; } @@ -83,12 +76,12 @@ public class NotifyTask extends AbstractTask { } - public String getTenant() { - return tenant; - } + public String getTenant() { + return tenant; + } - public void setTenant(String tenant) { - this.tenant = tenant; - } + public void setTenant(String tenant) { + this.tenant = tenant; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTaskProcessor.java b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTaskProcessor.java index 722ca42d5..403b04ea7 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTaskProcessor.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/notify/NotifyTaskProcessor.java @@ -15,14 +15,6 @@ */ package com.alibaba.nacos.config.server.service.notify; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.List; - -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.manager.AbstractTask; import com.alibaba.nacos.config.server.manager.TaskProcessor; @@ -30,35 +22,43 @@ import com.alibaba.nacos.config.server.service.ServerListService; import com.alibaba.nacos.config.server.service.notify.NotifyService.HttpResult; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.RunningConfigUtils; -import com.alibaba.nacos.config.server.utils.SystemConfig; +import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; /** * 通知服务。数据库变更后,通知所有server,包括自己,加载新数据。 + * * @author Nacos */ public class NotifyTaskProcessor implements TaskProcessor { - + public NotifyTaskProcessor(ServerListService serverListService) { this.serverListService = serverListService; } - + @Override public boolean process(String taskType, AbstractTask task) { - NotifyTask notifyTask = (NotifyTask) task; + NotifyTask notifyTask = (NotifyTask)task; String dataId = notifyTask.getDataId(); String group = notifyTask.getGroup(); String tenant = notifyTask.getTenant(); long lastModified = notifyTask.getLastModified(); - + boolean isok = true; - + for (String ip : serverListService.getServerList()) { - isok = notifyToDump(dataId, group, tenant,lastModified, ip) && isok; + isok = notifyToDump(dataId, group, tenant, lastModified, ip) && isok; } return isok; } - + /** * 通知其他server */ @@ -67,36 +67,38 @@ public class NotifyTaskProcessor implements TaskProcessor { try { // XXX 為了方便系统beta,不改变notify.do接口,新增lastModifed参数通过Http header传递 List headers = Arrays.asList( - NotifyService.NOTIFY_HEADER_LAST_MODIFIED, String.valueOf(lastModified), - NotifyService.NOTIFY_HEADER_OP_HANDLE_IP, SystemConfig.LOCAL_IP); - String urlString = MessageFormat.format(URL_PATTERN, serverIp, RunningConfigUtils.getContextPath(), dataId, - group); + NotifyService.NOTIFY_HEADER_LAST_MODIFIED, String.valueOf(lastModified), + NotifyService.NOTIFY_HEADER_OP_HANDLE_IP, LOCAL_IP); + String urlString = MessageFormat.format(URL_PATTERN, serverIp, RunningConfigUtils.getContextPath(), dataId, + group); HttpResult result = NotifyService.invokeURL(urlString, headers, Constants.ENCODE); if (result.code == HttpStatus.SC_OK) { - ConfigTraceService.logNotifyEvent(dataId, group, tenant, null, lastModified, SystemConfig.LOCAL_IP, ConfigTraceService.NOTIFY_EVENT_OK, delayed, serverIp); + ConfigTraceService.logNotifyEvent(dataId, group, tenant, null, lastModified, LOCAL_IP, + ConfigTraceService.NOTIFY_EVENT_OK, delayed, serverIp); return true; } else { - log.error("[notify-error] {}, {}, to {}, result {}", new Object[] { dataId, group, - serverIp, result.code }); - ConfigTraceService.logNotifyEvent(dataId, group, tenant, null, lastModified, SystemConfig.LOCAL_IP, ConfigTraceService.NOTIFY_EVENT_ERROR, delayed, serverIp); + log.error("[notify-error] {}, {}, to {}, result {}", new Object[] {dataId, group, + serverIp, result.code}); + ConfigTraceService.logNotifyEvent(dataId, group, tenant, null, lastModified, LOCAL_IP, + ConfigTraceService.NOTIFY_EVENT_ERROR, delayed, serverIp); return false; } } catch (Exception e) { log.error( - "[notify-exception] " + dataId + ", " + group + ", to " + serverIp + ", " - + e.toString()); - log.debug("[notify-exception] " + dataId + ", " + group + ", to " + serverIp + ", " + e.toString(), e); - ConfigTraceService.logNotifyEvent(dataId, group, tenant, null, lastModified, SystemConfig.LOCAL_IP, ConfigTraceService.NOTIFY_EVENT_EXCEPTION, delayed, serverIp); + "[notify-exception] " + dataId + ", " + group + ", to " + serverIp + ", " + + e.toString()); + log.debug("[notify-exception] " + dataId + ", " + group + ", to " + serverIp + ", " + e.toString(), e); + ConfigTraceService.logNotifyEvent(dataId, group, tenant, null, lastModified, LOCAL_IP, + ConfigTraceService.NOTIFY_EVENT_EXCEPTION, delayed, serverIp); return false; } } + static final Logger log = LoggerFactory.getLogger(NotifyTaskProcessor.class); - static final Logger log = LoggerFactory.getLogger(NotifyTaskProcessor.class); + static final String URL_PATTERN = "http://{0}{1}" + Constants.COMMUNICATION_CONTROLLER_PATH + "/dataChange" + + "?dataId={2}&group={3}"; - static final String URL_PATTERN = "http://{0}{1}" + Constants.COMMUNICATION_CONTROLLER_PATH + "/dataChange" - + "?dataId={2}&group={3}"; - - final ServerListService serverListService; + final ServerListService serverListService; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/trace/ConfigTraceService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/trace/ConfigTraceService.java index 83e87ae07..28d424053 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/trace/ConfigTraceService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/trace/ConfigTraceService.java @@ -17,98 +17,106 @@ package com.alibaba.nacos.config.server.service.trace; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5; -import com.alibaba.nacos.config.server.utils.SystemConfig; - -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; + +import static com.alibaba.nacos.common.util.SystemUtils.LOCAL_IP; + /** * Config trace - * @author Nacos * + * @author Nacos */ @Service public class ConfigTraceService { - public static final String PERSISTENCE_EVENT_PUB = "pub"; - public static final String PERSISTENCE_EVENT_REMOVE = "remove"; - public static final String PERSISTENCE_EVENT_MERGE = "merge"; + public static final String PERSISTENCE_EVENT_PUB = "pub"; + public static final String PERSISTENCE_EVENT_REMOVE = "remove"; + public static final String PERSISTENCE_EVENT_MERGE = "merge"; - public static final String NOTIFY_EVENT_OK = "ok"; - public static final String NOTIFY_EVENT_ERROR = "error"; - public static final String NOTIFY_EVENT_UNHEALTH = "unhealth"; - public static final String NOTIFY_EVENT_EXCEPTION = "exception"; + public static final String NOTIFY_EVENT_OK = "ok"; + public static final String NOTIFY_EVENT_ERROR = "error"; + public static final String NOTIFY_EVENT_UNHEALTH = "unhealth"; + public static final String NOTIFY_EVENT_EXCEPTION = "exception"; - public static final String DUMP_EVENT_OK = "ok"; - public static final String DUMP_EVENT_REMOVE_OK = "remove-ok"; - public static final String DUMP_EVENT_ERROR = "error"; + public static final String DUMP_EVENT_OK = "ok"; + public static final String DUMP_EVENT_REMOVE_OK = "remove-ok"; + public static final String DUMP_EVENT_ERROR = "error"; - public static final String PULL_EVENT_OK = "ok"; - public static final String PULL_EVENT_NOTFOUND = "not-found"; - public static final String PULL_EVENT_CONFLICT = "conflict"; - public static final String PULL_EVENT_ERROR = "error"; + public static final String PULL_EVENT_OK = "ok"; + public static final String PULL_EVENT_NOTFOUND = "not-found"; + public static final String PULL_EVENT_CONFLICT = "conflict"; + public static final String PULL_EVENT_ERROR = "error"; - public static void logPersistenceEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String type , String content) { - if (!LogUtil.traceLog.isInfoEnabled()) { - return; - } - // 方便tlog切分 - if (StringUtils.isBlank(tenant)) { - tenant = null; - } - //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed = -1] | ext(md5) + public static void logPersistenceEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, + String handleIp, String type, String content) { + if (!LogUtil.traceLog.isInfoEnabled()) { + return; + } + // 方便tlog切分 + if (StringUtils.isBlank(tenant)) { + tenant = null; + } + //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed = -1] | ext + // (md5) String md5 = content == null ? null : MD5.getInstance().getMD5String(content); - LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", SystemConfig.LOCAL_IP, dataId, group, tenant, - requestIpAppName, ts, handleIp, "persist", type, -1, md5); + LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", LOCAL_IP, dataId, group, tenant, + requestIpAppName, ts, handleIp, "persist", type, -1, md5); } - public static void logNotifyEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String type,long delayed, String targetIp) { - if (!LogUtil.traceLog.isInfoEnabled()) { - return; - } - // 方便tlog切分 - if (StringUtils.isBlank(tenant)) { - tenant = null; - } - //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed] | ext(targetIp) - LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", SystemConfig.LOCAL_IP, dataId, group, tenant, - requestIpAppName, ts, handleIp, "notify", type, delayed, targetIp); + public static void logNotifyEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, + String handleIp, String type, long delayed, String targetIp) { + if (!LogUtil.traceLog.isInfoEnabled()) { + return; + } + // 方便tlog切分 + if (StringUtils.isBlank(tenant)) { + tenant = null; + } + //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed] | ext + // (targetIp) + LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", LOCAL_IP, dataId, group, tenant, + requestIpAppName, ts, handleIp, "notify", type, delayed, targetIp); } - public static void logDumpEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String type, long delayed, long length) { - if (!LogUtil.traceLog.isInfoEnabled()) { - return; - } - // 方便tlog切分 - if (StringUtils.isBlank(tenant)) { - tenant = null; - } + public static void logDumpEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, + String handleIp, String type, long delayed, long length) { + if (!LogUtil.traceLog.isInfoEnabled()) { + return; + } + // 方便tlog切分 + if (StringUtils.isBlank(tenant)) { + tenant = null; + } //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed] | length - LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", SystemConfig.LOCAL_IP, dataId, group, tenant, - requestIpAppName, ts, handleIp, "dump", type, delayed, length); + LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", LOCAL_IP, dataId, group, tenant, + requestIpAppName, ts, handleIp, "dump", type, delayed, length); } - public static void logDumpAllEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String type) { - if (!LogUtil.traceLog.isInfoEnabled()) { - return; - } - // 方便tlog切分 - if (StringUtils.isBlank(tenant)) { - tenant = null; - } - //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed = -1] - LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", SystemConfig.LOCAL_IP, dataId, group, tenant, - requestIpAppName, ts, handleIp, "dump-all", type, -1); + public static void logDumpAllEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, + String handleIp, String type) { + if (!LogUtil.traceLog.isInfoEnabled()) { + return; + } + // 方便tlog切分 + if (StringUtils.isBlank(tenant)) { + tenant = null; + } + //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed = -1] + LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", LOCAL_IP, dataId, group, tenant, + requestIpAppName, ts, handleIp, "dump-all", type, -1); } - public static void logPullEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String type, long delayed, String clientIp) { - if (!LogUtil.traceLog.isInfoEnabled()) { - return; - } - // 方便tlog切分 - if (StringUtils.isBlank(tenant)) { - tenant = null; - } - //localIp | dataid | group | tenant| requestIpAppName| ts | event | type | [delayed] | ext(clientIp) - LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", SystemConfig.LOCAL_IP, dataId, group, tenant, - requestIpAppName, ts, "pull", type, delayed, clientIp); + public static void logPullEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, + String type, long delayed, String clientIp) { + if (!LogUtil.traceLog.isInfoEnabled()) { + return; + } + // 方便tlog切分 + if (StringUtils.isBlank(tenant)) { + tenant = null; + } + //localIp | dataid | group | tenant| requestIpAppName| ts | event | type | [delayed] | ext(clientIp) + LogUtil.traceLog.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", LOCAL_IP, dataId, group, tenant, + requestIpAppName, ts, "pull", type, delayed, clientIp); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/AccumulateStatCount.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/AccumulateStatCount.java index 813e1f7a3..93fab2cca 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/AccumulateStatCount.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/AccumulateStatCount.java @@ -17,22 +17,20 @@ package com.alibaba.nacos.config.server.utils; import java.util.concurrent.atomic.AtomicLong; - /** * Accumulate Stat Count - * @author Nacos * + * @author Nacos */ public class AccumulateStatCount { - + final AtomicLong total = new AtomicLong(0); long lastStatValue = 0; - - + public long increase() { return total.incrementAndGet(); } - + public long stat() { long tmp = total.get() - lastStatValue; lastStatValue += tmp; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/AppNameUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/AppNameUtils.java index 53a53faa8..d27f29594 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/AppNameUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/AppNameUtils.java @@ -19,9 +19,8 @@ import java.io.File; /** * App util - * - * @author Nacos * + * @author Nacos */ public class AppNameUtils { @@ -35,38 +34,34 @@ public class AppNameUtils { private static final String SERVER_JETTY = "jetty"; private static final String SERVER_TOMCAT = "tomcat"; private static final String SERVER_UNKNOWN = "unknown server"; - - public static String getAppName() { - String appName = null; - appName = getAppNameByProjectName(); - if (appName != null) { - return appName; - } + public static String getAppName() { + String appName = null; - appName = getAppNameByServerHome(); - if (appName != null) { - return appName; - } + appName = getAppNameByProjectName(); + if (appName != null) { + return appName; + } - return "unknown"; - } + appName = getAppNameByServerHome(); + if (appName != null) { + return appName; + } + return "unknown"; + } private static String getAppNameByProjectName() { return System.getProperty(PARAM_MARKING_PROJECT); } - private static String getAppNameByServerHome() { String serverHome = null; if (SERVER_JBOSS.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_JBOSS); - } - else if (SERVER_JETTY.equals(getServerType())) { + } else if (SERVER_JETTY.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_JETTY); - } - else if (SERVER_TOMCAT.equals(getServerType())) { + } else if (SERVER_TOMCAT.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_TOMCAT); } @@ -81,14 +76,11 @@ public class AppNameUtils { String serverType = null; if (System.getProperty(PARAM_MARKING_JBOSS) != null) { serverType = SERVER_JBOSS; - } - else if (System.getProperty(PARAM_MARKING_JETTY) != null) { + } else if (System.getProperty(PARAM_MARKING_JETTY) != null) { serverType = SERVER_JETTY; - } - else if (System.getProperty(PARAM_MARKING_TOMCAT) != null) { + } else if (System.getProperty(PARAM_MARKING_TOMCAT) != null) { serverType = SERVER_TOMCAT; - } - else { + } else { serverType = SERVER_UNKNOWN; } return serverType; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ContentUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ContentUtils.java index 45154a0a0..765d1f814 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ContentUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ContentUtils.java @@ -15,14 +15,14 @@ */ package com.alibaba.nacos.config.server.utils; -import static com.alibaba.nacos.config.server.constant.Constants.WORD_SEPARATOR; - import com.alibaba.nacos.config.server.constant.Constants; +import static com.alibaba.nacos.config.server.constant.Constants.WORD_SEPARATOR; + /** * Content utils - * @author Nacos * + * @author Nacos */ public class ContentUtils { @@ -42,7 +42,6 @@ public class ContentUtils { } } - public static String getContentIdentity(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { @@ -51,7 +50,6 @@ public class ContentUtils { return content.substring(0, index); } - public static String getContent(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { @@ -63,13 +61,12 @@ public class ContentUtils { public static String truncateContent(String content) { if (content == null) { return ""; - } - else if (content.length() <= LIMIT_CONTENT_SIZE) { + } else if (content.length() <= LIMIT_CONTENT_SIZE) { return content; - } - else { - return content.substring(0, 100) + "..."; + } else { + return content.substring(0, 100) + "..."; } } + private final static int LIMIT_CONTENT_SIZE = 100; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey.java index 0081e2ba4..6227812ea 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey.java @@ -17,7 +17,7 @@ package com.alibaba.nacos.config.server.utils; /** * 合成dataId+groupId的形式。对dataId和groupId中的保留字符做转义。 - * + * * @author jiuRen */ public class GroupKey { @@ -30,28 +30,28 @@ public class GroupKey { return sb.toString(); } - static public String getKeyTenant(String dataId, String group, String tenant) { - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append('+'); - urlEncode(group, sb); - if (StringUtils.isNotEmpty(tenant)) { - sb.append('+'); - urlEncode(tenant, sb); - } - return sb.toString(); - } - - static public String getKey(String dataId, String group, String datumStr) { - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append('+'); - urlEncode(group, sb); - sb.append('+'); - urlEncode(datumStr, sb); - return sb.toString(); - } - + static public String getKeyTenant(String dataId, String group, String tenant) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + if (StringUtils.isNotEmpty(tenant)) { + sb.append('+'); + urlEncode(tenant, sb); + } + return sb.toString(); + } + + static public String getKey(String dataId, String group, String datumStr) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + sb.append('+'); + urlEncode(datumStr, sb); + return sb.toString(); + } + static public String[] parseKey(String groupKey) { StringBuilder sb = new StringBuilder(); String dataId = null; @@ -61,15 +61,15 @@ public class GroupKey { for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if ('+' == c) { - if (null == dataId) { - dataId = sb.toString(); - sb.setLength(0); - } else if (null == group) { - group = sb.toString(); - sb.setLength(0); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } + if (null == dataId) { + dataId = sb.toString(); + sb.setLength(0); + } else if (null == group) { + group = sb.toString(); + sb.setLength(0); + } else { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } } else if ('%' == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); @@ -84,25 +84,24 @@ public class GroupKey { sb.append(c); } } - - if (StringUtils.isBlank(group)) { - group = sb.toString(); - if (group.length() == 0) { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else { - tenant = sb.toString(); - if (group.length() == 0) { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } - - return new String[] { dataId, group, tenant }; + + if (StringUtils.isBlank(group)) { + group = sb.toString(); + if (group.length() == 0) { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } else { + tenant = sb.toString(); + if (group.length() == 0) { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } + + return new String[] {dataId, group, tenant}; } - + /** - * + -> %2B - * % -> %25 + * + -> %2B % -> %25 */ static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey2.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey2.java index 4736dbe4a..8f0c7d301 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey2.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey2.java @@ -14,33 +14,34 @@ * limitations under the License. */ package com.alibaba.nacos.config.server.utils; + /** * Group key util - * @author Nacos * + * @author Nacos */ public class GroupKey2 { - static public String getKey(String dataId, String group) { - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append('+'); - urlEncode(group, sb); - return sb.toString(); - } - - static public String getKey(String dataId, String group, String tenant) { - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append('+'); - urlEncode(group, sb); - if (StringUtils.isNotEmpty(tenant)) { - sb.append('+'); - urlEncode(tenant, sb); - } - return sb.toString(); - } - + static public String getKey(String dataId, String group) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + return sb.toString(); + } + + static public String getKey(String dataId, String group, String tenant) { + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append('+'); + urlEncode(group, sb); + if (StringUtils.isNotEmpty(tenant)) { + sb.append('+'); + urlEncode(tenant, sb); + } + return sb.toString(); + } + static public String[] parseKey(String groupKey) { StringBuilder sb = new StringBuilder(); String dataId = null; @@ -50,15 +51,15 @@ public class GroupKey2 { for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if ('+' == c) { - if (null == dataId) { - dataId = sb.toString(); - sb.setLength(0); - } else if (null == group) { - group = sb.toString(); - sb.setLength(0); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } + if (null == dataId) { + dataId = sb.toString(); + sb.setLength(0); + } else if (null == group) { + group = sb.toString(); + sb.setLength(0); + } else { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } } else if ('%' == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); @@ -73,25 +74,24 @@ public class GroupKey2 { sb.append(c); } } - - if (StringUtils.isBlank(group)) { - group = sb.toString(); - if (group.length() == 0) { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else { - tenant = sb.toString(); - if (group.length() == 0) { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } - - return new String[] { dataId, group, tenant }; + + if (StringUtils.isBlank(group)) { + group = sb.toString(); + if (group.length() == 0) { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } else { + tenant = sb.toString(); + if (group.length() == 0) { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } + + return new String[] {dataId, group, tenant}; } - + /** - * + -> %2B - * % -> %25 + * + -> %2B % -> %25 */ static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { @@ -105,5 +105,5 @@ public class GroupKey2 { } } } - + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java index f266f83b7..d97cbf9ac 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/IPUtil.java @@ -17,39 +17,40 @@ package com.alibaba.nacos.config.server.utils; import java.util.regex.Matcher; import java.util.regex.Pattern; + /** * ip util - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class IPUtil { - public static boolean isIPV4(String addr) { - if (null == addr) { - return false; - } - String rexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"; + public static boolean isIPV4(String addr) { + if (null == addr) { + return false; + } + String rexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"; - Pattern pat = Pattern.compile(rexp); + Pattern pat = Pattern.compile(rexp); - Matcher mat = pat.matcher(addr); + Matcher mat = pat.matcher(addr); - boolean ipAddress = mat.find(); - return ipAddress; - } + boolean ipAddress = mat.find(); + return ipAddress; + } - public static boolean isIPV6(String addr) { - if (null == addr) { - return false; - } - String rexp = "^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"; + public static boolean isIPV6(String addr) { + if (null == addr) { + return false; + } + String rexp = "^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"; - Pattern pat = Pattern.compile(rexp); + Pattern pat = Pattern.compile(rexp); - Matcher mat = pat.matcher(addr); + Matcher mat = pat.matcher(addr); - boolean ipAddress = mat.find(); - return ipAddress; - } + boolean ipAddress = mat.find(); + return ipAddress; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/JSONUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/JSONUtils.java index db6b5c217..9ae850fb5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/JSONUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/JSONUtils.java @@ -15,37 +15,37 @@ */ package com.alibaba.nacos.config.server.utils; -import java.io.IOException; - -import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.DeserializationConfig.Feature; +import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.JavaType; import org.codehaus.jackson.type.TypeReference; +import java.io.IOException; + /** * json util - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class JSONUtils { static ObjectMapper mapper = new ObjectMapper(); - + static { - mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES); + mapper.disable(Feature.FAIL_ON_UNKNOWN_PROPERTIES); } - public static String serializeObject(Object o) throws IOException { - return mapper.writeValueAsString(o); - } + public static String serializeObject(Object o) throws IOException { + return mapper.writeValueAsString(o); + } public static Object deserializeObject(String s, Class clazz) throws IOException { return mapper.readValue(s, clazz); } public static Object deserializeObject(String s, TypeReference typeReference) - throws IOException { + throws IOException { return mapper.readValue(s, typeReference); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/LogUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/LogUtil.java index c9072505c..c4c9a8fe4 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/LogUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/LogUtil.java @@ -17,40 +17,13 @@ package com.alibaba.nacos.config.server.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.joran.spi.JoranException; /** * log util - * @author Nacos * + * @author Nacos */ public class LogUtil { - - static { - LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); - lc.reset(); - - JoranConfigurator configurator = new JoranConfigurator(); - - String nacosDir = System.getProperty("nacos.home"); - if (StringUtils.isBlank(nacosDir)) { - configurator.setContext(lc); - try { - configurator.doConfigure(LogUtil.class.getResource("/nacos-config-logback.xml")); - } catch (JoranException e) { - System.err.println("init logger fail by nacos-config-logback.xml"); - } - } else { - configurator.setContext(lc); - try { - configurator.doConfigure(nacosDir + "/conf/nacos-logback.xml"); - } catch (JoranException e) { - System.err.println("init logger fail by " + nacosDir + "/conf/nacos-logback.xml"); - } - } - } /** * 默认的日志 @@ -61,40 +34,40 @@ public class LogUtil { * 致命错误,需要告警 */ static public final Logger fatalLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.fatal"); + .getLogger("com.alibaba.nacos.config.fatal"); /** * 客户端GET方法获取数据的日志 */ static public final Logger pullLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.pullLog"); + .getLogger("com.alibaba.nacos.config.pullLog"); static public final Logger pullCheckLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.pullCheckLog"); + .getLogger("com.alibaba.nacos.config.pullCheckLog"); /** * 从DB dump数据的日志 */ static public final Logger dumpLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.dumpLog"); + .getLogger("com.alibaba.nacos.config.dumpLog"); static public final Logger memoryLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.monitorLog"); + .getLogger("com.alibaba.nacos.config.monitorLog"); static public final Logger clientLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.clientLog"); + .getLogger("com.alibaba.nacos.config.clientLog"); static public final Logger sdkLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.sdkLog"); + .getLogger("com.alibaba.nacos.config.sdkLog"); static public final Logger traceLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.traceLog"); + .getLogger("com.alibaba.nacos.config.traceLog"); static public final Logger aclLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.aclLog"); + .getLogger("com.alibaba.nacos.config.aclLog"); static public final Logger notifyLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.notifyLog"); - - static public final Logger appCollectorLog = LoggerFactory - .getLogger("com.alibaba.nacos.config.appCollectorLog"); + .getLogger("com.alibaba.nacos.config.notifyLog"); + + static public final Logger appCollectorLog = LoggerFactory + .getLogger("com.alibaba.nacos.config.appCollectorLog"); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5.java index 8e41b3221..3f4c1f269 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5.java @@ -15,26 +15,27 @@ */ package com.alibaba.nacos.config.server.utils; +import com.alibaba.nacos.config.server.constant.Constants; + import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; -import com.alibaba.nacos.config.server.constant.Constants; - /** * md5 - * @author Nacos * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class MD5 { - private static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + private static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private final static int DIGITS_COUNT = 16; private final static int DIGITS_CHAR_SIZE = 32; - + private static Map rDigits = new HashMap(16); + static { for (int i = 0; i < digits.length; ++i) { rDigits.put(digits[i], i); @@ -45,41 +46,34 @@ public class MD5 { private MessageDigest mHasher; private ReentrantLock opLock = new ReentrantLock(); - private MD5() { try { mHasher = MessageDigest.getInstance("md5"); - } - catch (Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } - public static MD5 getInstance() { return me; } - public String getMD5String(String content) { return bytes2string(hash(content)); } - public String getMD5String(byte[] content) { return bytes2string(hash(content)); } - public byte[] getMD5Bytes(byte[] content) { return hash(content); } - /** * 对字符串进行md5 - * + * * @param str * @return md5 byte[16] */ @@ -91,19 +85,16 @@ public class MD5 { throw new IllegalArgumentException("md5 need"); } return bt; - } - catch (UnsupportedEncodingException e) { + } catch (UnsupportedEncodingException e) { throw new RuntimeException("unsupported utf-8 encoding", e); - } - finally { + } finally { opLock.unlock(); } } - /** * 对二进制数据进行md5 - * + * * @param str * @return md5 byte[16] */ @@ -115,16 +106,14 @@ public class MD5 { throw new IllegalArgumentException("md5 need"); } return bt; - } - finally { + } finally { opLock.unlock(); } } - /** * 将一个字节数组转化为可见的字符串 - * + * * @param bt * @return */ @@ -141,10 +130,9 @@ public class MD5 { return new String(out); } - /** * 将字符串转换为bytes - * + * * @param str * @return byte[] */ @@ -160,7 +148,7 @@ public class MD5 { for (int i = 0; i < DIGITS_COUNT; ++i) { int h = rDigits.get(chs[i * 2]).intValue(); int l = rDigits.get(chs[i * 2 + 1]).intValue(); - data[i] = (byte) ((h & 0x0F) << 4 | (l & 0x0F)); + data[i] = (byte)((h & 0x0F) << 4 | (l & 0x0F)); } return data; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5Util.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5Util.java index 8de5c9522..f30a5cb72 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5Util.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/MD5Util.java @@ -15,33 +15,26 @@ */ package com.alibaba.nacos.config.server.utils; -import static com.alibaba.nacos.config.server.constant.Constants.LINE_SEPARATOR; -import static com.alibaba.nacos.config.server.constant.Constants.WORD_SEPARATOR; +import com.alibaba.nacos.config.server.constant.Constants; +import com.alibaba.nacos.config.server.service.ConfigService; +import org.apache.commons.lang3.StringUtils; -import java.io.CharArrayWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.Writer; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; - -import com.alibaba.nacos.config.server.constant.Constants; -import com.alibaba.nacos.config.server.service.ConfigService; +import static com.alibaba.nacos.config.server.constant.Constants.LINE_SEPARATOR; +import static com.alibaba.nacos.config.server.constant.Constants.WORD_SEPARATOR; /** - * 轮询逻辑封装类 - * @author Nacos + * 轮询逻辑封装类 * + * @author Nacos */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class MD5Util { @@ -82,84 +75,82 @@ public class MD5Util { StringBuilder sb = new StringBuilder(); - for (String groupKey : changedGroupKeys) { - String[] dataIdGroupId = GroupKey2.parseKey(groupKey); - sb.append(dataIdGroupId[0]); - sb.append(WORD_SEPARATOR); - sb.append(dataIdGroupId[1]); - // if have tenant, then set it - if (dataIdGroupId.length == 3) { - if (StringUtils.isNotBlank(dataIdGroupId[2])) { - sb.append(WORD_SEPARATOR); - sb.append(dataIdGroupId[2]); - } - } - sb.append(LINE_SEPARATOR); - } + for (String groupKey : changedGroupKeys) { + String[] dataIdGroupId = GroupKey2.parseKey(groupKey); + sb.append(dataIdGroupId[0]); + sb.append(WORD_SEPARATOR); + sb.append(dataIdGroupId[1]); + // if have tenant, then set it + if (dataIdGroupId.length == 3) { + if (StringUtils.isNotBlank(dataIdGroupId[2])) { + sb.append(WORD_SEPARATOR); + sb.append(dataIdGroupId[2]); + } + } + sb.append(LINE_SEPARATOR); + } // 对WORD_SEPARATOR和LINE_SEPARATOR不可见字符进行编码, 编码后的值为%02和%01 return URLEncoder.encode(sb.toString(), "UTF-8"); } /** - * 解析传输协议 - * 传输协议有两种格式(w为字段分隔符,l为每条数据分隔符): - * 老报文:D w G w MD5 l - * 新报文:D w G w MD5 w T l + * 解析传输协议 传输协议有两种格式(w为字段分隔符,l为每条数据分隔符): 老报文:D w G w MD5 l 新报文:D w G w MD5 w T l + * * @param configKeysString 协议字符串 - * @return 协议报文 + * @return 协议报文 */ - static public Map getClientMd5Map(String configKeysString) { - - Map md5Map = new HashMap(5); - - if (null == configKeysString || "".equals(configKeysString)) { - return md5Map; - } - int start = 0; - List tmpList = new ArrayList(3); - for (int i = start; i < configKeysString.length(); i++) { - char c = configKeysString.charAt(i); - if (c == WORD_SEPARATOR_CHAR) { - tmpList.add(configKeysString.substring(start, i)); - start = i + 1; - if (tmpList.size() > 3) { - // 畸形报文。返回参数错误 - throw new IllegalArgumentException("invalid protocol,too much key"); - } - } else if (c == LINE_SEPARATOR_CHAR) { - String endValue = ""; - if (start + 1 <= i) { - endValue = configKeysString.substring(start, i); - } - start = i + 1; + static public Map getClientMd5Map(String configKeysString) { - // 如果老的报文,最后一位是md5。多租户后报文为tenant。 - if (tmpList.size() == 2) { - String groupKey = GroupKey2.getKey(tmpList.get(0), tmpList.get(1)); - groupKey = SingletonRepository.DataIdGroupIdCache.getSingleton(groupKey); - md5Map.put(groupKey, endValue); - } else { - String groupKey = GroupKey2.getKey(tmpList.get(0), tmpList.get(1), endValue); - groupKey = SingletonRepository.DataIdGroupIdCache.getSingleton(groupKey); - md5Map.put(groupKey, tmpList.get(2)); - } - tmpList.clear(); + Map md5Map = new HashMap(5); + + if (null == configKeysString || "".equals(configKeysString)) { + return md5Map; + } + int start = 0; + List tmpList = new ArrayList(3); + for (int i = start; i < configKeysString.length(); i++) { + char c = configKeysString.charAt(i); + if (c == WORD_SEPARATOR_CHAR) { + tmpList.add(configKeysString.substring(start, i)); + start = i + 1; + if (tmpList.size() > 3) { + // 畸形报文。返回参数错误 + throw new IllegalArgumentException("invalid protocol,too much key"); + } + } else if (c == LINE_SEPARATOR_CHAR) { + String endValue = ""; + if (start + 1 <= i) { + endValue = configKeysString.substring(start, i); + } + start = i + 1; + + // 如果老的报文,最后一位是md5。多租户后报文为tenant。 + if (tmpList.size() == 2) { + String groupKey = GroupKey2.getKey(tmpList.get(0), tmpList.get(1)); + groupKey = SingletonRepository.DataIdGroupIdCache.getSingleton(groupKey); + md5Map.put(groupKey, endValue); + } else { + String groupKey = GroupKey2.getKey(tmpList.get(0), tmpList.get(1), endValue); + groupKey = SingletonRepository.DataIdGroupIdCache.getSingleton(groupKey); + md5Map.put(groupKey, tmpList.get(2)); + } + tmpList.clear(); + + // 对畸形报文进行保护 + if (md5Map.size() > 10000) { + throw new IllegalArgumentException("invalid protocol, too much listener"); + } + } + } + return md5Map; + } - // 对畸形报文进行保护 - if (md5Map.size() > 10000) { - throw new IllegalArgumentException("invalid protocol, too much listener"); - } - } - } - return md5Map; - } - static public String toString(InputStream input, String encoding) throws IOException { return (null == encoding) ? toString(new InputStreamReader(input, Constants.ENCODE)) - : toString(new InputStreamReader(input, encoding)); + : toString(new InputStreamReader(input, encoding)); } - + static public String toString(Reader reader) throws IOException { CharArrayWriter sw = new CharArrayWriter(); copy(reader, sw); @@ -169,15 +160,15 @@ public class MD5Util { static public long copy(Reader input, Writer output) throws IOException { char[] buffer = new char[1024]; long count = 0; - for (int n = 0; (n = input.read(buffer)) >= 0;) { + for (int n = 0; (n = input.read(buffer)) >= 0; ) { output.write(buffer, 0, n); count += n; } return count; } - - static final char WORD_SEPARATOR_CHAR = (char) 2; - static final char LINE_SEPARATOR_CHAR = (char) 1; - + + static final char WORD_SEPARATOR_CHAR = (char)2; + static final char LINE_SEPARATOR_CHAR = (char)1; + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/PaginationHelper.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/PaginationHelper.java index 63b1a377a..394b1974f 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/PaginationHelper.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/PaginationHelper.java @@ -15,20 +15,20 @@ */ package com.alibaba.nacos.config.server.utils; -import java.util.List; - +import com.alibaba.nacos.config.server.model.Page; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; -import com.alibaba.nacos.config.server.model.Page; +import java.util.List; +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; /** * 分页辅助类 - * + * + * @param * @author boyan * @date 2010-5-6 - * @param */ public class PaginationHelper { @@ -36,18 +36,12 @@ public class PaginationHelper { /** * 取分页 * - * @param jt - * jdbcTemplate - * @param sqlCountRows - * 查询总数的SQL - * @param sqlFetchRows - * 查询数据的sql - * @param args - * 查询参数 - * @param pageNo - * 页数 - * @param pageSize - * 每页大小 + * @param jt jdbcTemplate + * @param sqlCountRows 查询总数的SQL + * @param sqlFetchRows 查询数据的sql + * @param args 查询参数 + * @param pageNo 页数 + * @param pageSize 每页大小 * @param rowMapper * @return */ @@ -66,10 +60,10 @@ public class PaginationHelper { // 查询当前记录总数 Integer rowCountInt = jt.queryForObject(sqlCountRows, Integer.class, args); if (rowCountInt == null) { - throw new IllegalArgumentException("fetchPageLimit error"); - } - final int rowCount = rowCountInt.intValue(); - + throw new IllegalArgumentException("fetchPageLimit error"); + } + final int rowCount = rowCountInt.intValue(); + // 计算页数 int pageCount = rowCount / pageSize; if (rowCount > pageSize * pageCount) { @@ -88,8 +82,8 @@ public class PaginationHelper { final int startRow = (pageNo - 1) * pageSize; String selectSQL = ""; - if (PropertyUtil.isStandaloneMode()) { - selectSQL = sqlFetchRows + " OFFSET "+startRow+" ROWS FETCH NEXT "+pageSize+" ROWS ONLY"; + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { + selectSQL = sqlFetchRows + " OFFSET " + startRow + " ROWS FETCH NEXT " + pageSize + " ROWS ONLY"; } else if (lastMaxId != null) { selectSQL = sqlFetchRows + " and id > " + lastMaxId + " order by id asc" + " limit " + 0 + "," + pageSize; } else { @@ -104,17 +98,18 @@ public class PaginationHelper { } public Page fetchPageLimit(final JdbcTemplate jt, final String sqlCountRows, final String sqlFetchRows, - final Object args[], final int pageNo, final int pageSize, final RowMapper rowMapper) { + final Object args[], final int pageNo, final int pageSize, + final RowMapper rowMapper) { if (pageNo <= 0 || pageSize <= 0) { throw new IllegalArgumentException("pageNo and pageSize must be greater than zero"); } // 查询当前记录总数 Integer rowCountInt = jt.queryForObject(sqlCountRows, Integer.class); if (rowCountInt == null) { - throw new IllegalArgumentException("fetchPageLimit error"); - } - final int rowCount = rowCountInt.intValue(); - + throw new IllegalArgumentException("fetchPageLimit error"); + } + final int rowCount = rowCountInt.intValue(); + // 计算页数 int pageCount = rowCount / pageSize; if (rowCount > pageSize * pageCount) { @@ -132,7 +127,7 @@ public class PaginationHelper { } String selectSQL = sqlFetchRows; - if (PropertyUtil.isStandaloneMode()) { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { selectSQL = selectSQL.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); } @@ -143,17 +138,19 @@ public class PaginationHelper { return page; } - public Page fetchPageLimit(final JdbcTemplate jt, final String sqlCountRows, final Object args1[], final String sqlFetchRows, - final Object args2[], final int pageNo, final int pageSize, final RowMapper rowMapper) { + public Page fetchPageLimit(final JdbcTemplate jt, final String sqlCountRows, final Object args1[], + final String sqlFetchRows, + final Object args2[], final int pageNo, final int pageSize, + final RowMapper rowMapper) { if (pageNo <= 0 || pageSize <= 0) { throw new IllegalArgumentException("pageNo and pageSize must be greater than zero"); } - // 查询当前记录总数 - Integer rowCountInt = jt.queryForObject(sqlCountRows, Integer.class, args1); - if (rowCountInt == null) { - throw new IllegalArgumentException("fetchPageLimit error"); - } - final int rowCount = rowCountInt.intValue(); + // 查询当前记录总数 + Integer rowCountInt = jt.queryForObject(sqlCountRows, Integer.class, args1); + if (rowCountInt == null) { + throw new IllegalArgumentException("fetchPageLimit error"); + } + final int rowCount = rowCountInt.intValue(); // 计算页数 int pageCount = rowCount / pageSize; @@ -172,7 +169,7 @@ public class PaginationHelper { } String selectSQL = sqlFetchRows; - if (PropertyUtil.isStandaloneMode()) { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { selectSQL = selectSQL.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); } @@ -184,7 +181,8 @@ public class PaginationHelper { } public Page fetchPageLimit(final JdbcTemplate jt, final String sqlFetchRows, - final Object args[], final int pageNo, final int pageSize, final RowMapper rowMapper) { + final Object args[], final int pageNo, final int pageSize, + final RowMapper rowMapper) { if (pageNo <= 0 || pageSize <= 0) { throw new IllegalArgumentException("pageNo and pageSize must be greater than zero"); } @@ -192,7 +190,7 @@ public class PaginationHelper { final Page page = new Page(); String selectSQL = sqlFetchRows; - if (PropertyUtil.isStandaloneMode()) { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { selectSQL = selectSQL.replaceAll("(?i)LIMIT \\?,\\?", "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); } @@ -206,7 +204,7 @@ public class PaginationHelper { public void updateLimit(final JdbcTemplate jt, final String sql, final Object args[]) { String sqlUpdate = sql; - if (PropertyUtil.isStandaloneMode()) { + if (STANDALONE_MODE && !PropertyUtil.isStandaloneUseMysql()) { sqlUpdate = sqlUpdate.replaceAll("limit \\?", "OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY"); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java index 24b734938..77c168168 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java @@ -15,29 +15,27 @@ */ package com.alibaba.nacos.config.server.utils; -import java.util.Map; - -import org.apache.commons.lang.StringUtils; - import com.alibaba.nacos.config.server.exception.NacosException; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; /** * 参数合法性检查工具类 - * + * * @author Nacos - * */ public class ParamUtils { - private static char[] validChars = new char[] { '_', '-', '.', ':' }; + private static char[] validChars = new char[] {'_', '-', '.', ':'}; private final static int TAG_MAX_LEN = 16; - + private final static int TANANT_MAX_LEN = 128; - + /** * 白名单的方式检查, 合法的参数只能包含字母、数字、以及validChars中的字符, 并且不能为空 - * + * * @param param * @return */ @@ -50,11 +48,9 @@ public class ParamUtils { char ch = param.charAt(i); if (Character.isLetterOrDigit(ch)) { continue; - } - else if (isValidChar(ch)) { + } else if (isValidChar(ch)) { continue; - } - else { + } else { return false; } } @@ -69,87 +65,87 @@ public class ParamUtils { } return false; } - - public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { - if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId.trim())) { - throw new NacosException(NacosException.INVALID_PARAM, "invalid dataId"); - } else if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { - throw new NacosException(NacosException.INVALID_PARAM, "invalid group"); - } else if (StringUtils.isBlank(datumId) || !ParamUtils.isValid(datumId)) { - throw new NacosException(NacosException.INVALID_PARAM, "invalid datumId"); - } else if (StringUtils.isBlank(content)) { - throw new NacosException(NacosException.INVALID_PARAM, "content is blank"); - } else if (content.length() > PropertyUtil.getMaxContent()) { - throw new NacosException(NacosException.INVALID_PARAM, - "invalid content, over " + PropertyUtil.getMaxContent()); - } - } - public static void checkParam(String tag) { - if (StringUtils.isNotBlank(tag)) { - if (!ParamUtils.isValid(tag.trim())) { - throw new IllegalArgumentException("invalid tag"); - } - if (tag.length() > TAG_MAX_LEN) { - throw new IllegalArgumentException("too long tag, over 16"); - } - } - } - - public static void checkTenant(String tenant) { - if (StringUtils.isNotBlank(tenant)) { - if (!ParamUtils.isValid(tenant.trim())) { - throw new IllegalArgumentException("invalid tenant"); - } - if (tenant.length() > TANANT_MAX_LEN) { - throw new IllegalArgumentException("too long tag, over 128"); - } - } - } + public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { + if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId.trim())) { + throw new NacosException(NacosException.INVALID_PARAM, "invalid dataId"); + } else if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { + throw new NacosException(NacosException.INVALID_PARAM, "invalid group"); + } else if (StringUtils.isBlank(datumId) || !ParamUtils.isValid(datumId)) { + throw new NacosException(NacosException.INVALID_PARAM, "invalid datumId"); + } else if (StringUtils.isBlank(content)) { + throw new NacosException(NacosException.INVALID_PARAM, "content is blank"); + } else if (content.length() > PropertyUtil.getMaxContent()) { + throw new NacosException(NacosException.INVALID_PARAM, + "invalid content, over " + PropertyUtil.getMaxContent()); + } + } + + public static void checkParam(String tag) { + if (StringUtils.isNotBlank(tag)) { + if (!ParamUtils.isValid(tag.trim())) { + throw new IllegalArgumentException("invalid tag"); + } + if (tag.length() > TAG_MAX_LEN) { + throw new IllegalArgumentException("too long tag, over 16"); + } + } + } + + public static void checkTenant(String tenant) { + if (StringUtils.isNotBlank(tenant)) { + if (!ParamUtils.isValid(tenant.trim())) { + throw new IllegalArgumentException("invalid tenant"); + } + if (tenant.length() > TANANT_MAX_LEN) { + throw new IllegalArgumentException("too long tag, over 128"); + } + } + } + + public static void checkParam(Map configAdvanceInfo) throws NacosException { + for (Map.Entry configAdvanceInfoTmp : configAdvanceInfo.entrySet()) { + if ("config_tags".equals(configAdvanceInfoTmp.getKey())) { + if (configAdvanceInfoTmp.getValue() != null) { + String[] tagArr = ((String)configAdvanceInfoTmp.getValue()).split(","); + if (tagArr.length > 5) { + throw new NacosException(NacosException.INVALID_PARAM, "too much config_tags, over 5"); + } + for (String tag : tagArr) { + if (tag.length() > 64) { + throw new NacosException(NacosException.INVALID_PARAM, "too long tag, over 64"); + } + } + } + } else if ("desc".equals(configAdvanceInfoTmp.getKey())) { + if (configAdvanceInfoTmp.getValue() != null + && ((String)configAdvanceInfoTmp.getValue()).length() > 128) { + throw new NacosException(NacosException.INVALID_PARAM, "too long desc, over 128"); + } + } else if ("use".equals(configAdvanceInfoTmp.getKey())) { + if (configAdvanceInfoTmp.getValue() != null + && ((String)configAdvanceInfoTmp.getValue()).length() > 32) { + throw new NacosException(NacosException.INVALID_PARAM, "too long use, over 32"); + } + } else if ("effect".equals(configAdvanceInfoTmp.getKey())) { + if (configAdvanceInfoTmp.getValue() != null + && ((String)configAdvanceInfoTmp.getValue()).length() > 32) { + throw new NacosException(NacosException.INVALID_PARAM, "too long effect, over 32"); + } + } else if ("type".equals(configAdvanceInfoTmp.getKey())) { + if (configAdvanceInfoTmp.getValue() != null + && ((String)configAdvanceInfoTmp.getValue()).length() > 32) { + throw new NacosException(NacosException.INVALID_PARAM, "too long type, over 32"); + } + } else if ("schema".equals(configAdvanceInfoTmp.getKey())) { + if (configAdvanceInfoTmp.getValue() != null + && ((String)configAdvanceInfoTmp.getValue()).length() > 32768) { + throw new NacosException(NacosException.INVALID_PARAM, "too long schema, over 32768"); + } + } else { + throw new NacosException(NacosException.INVALID_PARAM, "invalid param"); + } + } + } - public static void checkParam(Map configAdvanceInfo) throws NacosException { - for (Map.Entry configAdvanceInfoTmp : configAdvanceInfo.entrySet()) { - if ("config_tags".equals(configAdvanceInfoTmp.getKey())) { - if (configAdvanceInfoTmp.getValue() != null) { - String[] tagArr = ((String) configAdvanceInfoTmp.getValue()).split(","); - if (tagArr.length > 5) { - throw new NacosException(NacosException.INVALID_PARAM, "too much config_tags, over 5"); - } - for (String tag : tagArr) { - if (tag.length() > 64) { - throw new NacosException(NacosException.INVALID_PARAM, "too long tag, over 64"); - } - } - } - } else if ("desc".equals(configAdvanceInfoTmp.getKey())) { - if (configAdvanceInfoTmp.getValue() != null - && ((String) configAdvanceInfoTmp.getValue()).length() > 128) { - throw new NacosException(NacosException.INVALID_PARAM, "too long desc, over 128"); - } - } else if ("use".equals(configAdvanceInfoTmp.getKey())) { - if (configAdvanceInfoTmp.getValue() != null - && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { - throw new NacosException(NacosException.INVALID_PARAM, "too long use, over 32"); - } - } else if ("effect".equals(configAdvanceInfoTmp.getKey())) { - if (configAdvanceInfoTmp.getValue() != null - && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { - throw new NacosException(NacosException.INVALID_PARAM, "too long effect, over 32"); - } - } else if ("type".equals(configAdvanceInfoTmp.getKey())) { - if (configAdvanceInfoTmp.getValue() != null - && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { - throw new NacosException(NacosException.INVALID_PARAM, "too long type, over 32"); - } - } else if ("schema".equals(configAdvanceInfoTmp.getKey())) { - if (configAdvanceInfoTmp.getValue() != null - && ((String) configAdvanceInfoTmp.getValue()).length() > 32768) { - throw new NacosException(NacosException.INVALID_PARAM, "too long schema, over 32768"); - } - } else { - throw new NacosException(NacosException.INVALID_PARAM, "invalid param"); - } - } - } - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/PropertyUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/PropertyUtil.java index c33b837c0..727868744 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/PropertyUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/PropertyUtil.java @@ -15,262 +15,265 @@ */ package com.alibaba.nacos.config.server.utils; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; + +import static com.alibaba.nacos.common.util.SystemUtils.STANDALONE_MODE; + /** * properties utils - * - * @author Nacos * + * @author Nacos */ @Component public class PropertyUtil { - private final static Logger logger = LogUtil.defaultLog; - - private static int notifyConnectTimeout = 100; - private static int notifySocketTimeout = 200; - private static int maxHealthCheckFailCount = 12; - private static boolean isHealthCheck = true; - private static int maxContent = 10 * 1024 * 1024; + private final static Logger logger = LogUtil.defaultLog; - /** - * 是否开启容量管理 - */ - private static boolean isManageCapacity = true; - /** - * 是否开启容量管理的限制检验功能,包括配置个数上限、配置内容大小限制等 - */ - private static boolean isCapacityLimitCheck = false; - /** - * 集群默认容量上限 - */ - private static int defaultClusterQuota = 100000; - /** - * 每个Group默认容量上限 - */ - private static int defaultGroupQuota = 200; - /** - * 每个Tenant默认容量上限 - */ - private static int defaultTenantQuota = 200; - /** - * 单个配置中content的最大大小,单位为字节 - */ - private static int defaultMaxSize = 100 * 1024; - /** - * 聚合数据子配置最大个数 - */ - private static int defaultMaxAggrCount = 10000; - /** - * 聚合数据单个子配置中content的最大大小,单位为字节 - */ - private static int defaultMaxAggrSize = 1024; - /** - * 初始化容量信息记录时,发现已经到达限额时的扩容百分比 - */ - private static int initialExpansionPercent = 100; - /** - * 修正容量信息表使用量(usage)的时间间隔,单位为秒 - */ - private static int correctUsageDelay = 10 * 60; + private static int notifyConnectTimeout = 100; + private static int notifySocketTimeout = 200; + private static int maxHealthCheckFailCount = 12; + private static boolean isHealthCheck = true; + private static int maxContent = 10 * 1024 * 1024; - private static boolean standaloneMode = false; + /** + * 是否开启容量管理 + */ + private static boolean isManageCapacity = true; + /** + * 是否开启容量管理的限制检验功能,包括配置个数上限、配置内容大小限制等 + */ + private static boolean isCapacityLimitCheck = false; + /** + * 集群默认容量上限 + */ + private static int defaultClusterQuota = 100000; + /** + * 每个Group默认容量上限 + */ + private static int defaultGroupQuota = 200; + /** + * 每个Tenant默认容量上限 + */ + private static int defaultTenantQuota = 200; + /** + * 单个配置中content的最大大小,单位为字节 + */ + private static int defaultMaxSize = 100 * 1024; + /** + * 聚合数据子配置最大个数 + */ + private static int defaultMaxAggrCount = 10000; + /** + * 聚合数据单个子配置中content的最大大小,单位为字节 + */ + private static int defaultMaxAggrSize = 1024; + /** + * 初始化容量信息记录时,发现已经到达限额时的扩容百分比 + */ + private static int initialExpansionPercent = 100; + /** + * 修正容量信息表使用量(usage)的时间间隔,单位为秒 + */ + private static int correctUsageDelay = 10 * 60; + /** + * 单机模式使用db + */ + private static boolean standaloneUseMysql = false; - @Autowired - private Environment env; - static { - setStandaloneMode(Boolean.parseBoolean(System.getProperty("nacos.standalone", "false"))); - } + @Autowired + private Environment env; - @PostConstruct - public void init() { - try { + @PostConstruct + public void init() { + try { - setNotifyConnectTimeout(Integer.parseInt(env.getProperty("notifyConnectTimeout", "100"))); - logger.info("notifyConnectTimeout:{}", notifyConnectTimeout); - setNotifySocketTimeout(Integer.parseInt(env.getProperty("notifySocketTimeout", "200"))); - logger.info("notifySocketTimeout:{}", notifySocketTimeout); - setHealthCheck(Boolean.valueOf(env.getProperty("isHealthCheck", "true"))); - logger.info("isHealthCheck:{}", isHealthCheck); - setMaxHealthCheckFailCount(Integer.parseInt(env.getProperty("maxHealthCheckFailCount", "12"))); - logger.info("maxHealthCheckFailCount:{}", maxHealthCheckFailCount); - setMaxContent(Integer.parseInt(env.getProperty("maxContent", String.valueOf(maxContent)))); - logger.info("maxContent:{}", maxContent); - // 容量管理 - setManageCapacity(getBoolean("isManageCapacity", isManageCapacity)); - setCapacityLimitCheck(getBoolean("isCapacityLimitCheck", isCapacityLimitCheck)); - setDefaultClusterQuota(getInt("defaultClusterQuota", defaultClusterQuota)); - setDefaultGroupQuota(getInt("defaultGroupQuota", defaultGroupQuota)); - setDefaultTenantQuota(getInt("defaultTenantQuota", defaultTenantQuota)); - setDefaultMaxSize(getInt("defaultMaxSize", defaultMaxSize)); - setDefaultMaxAggrCount(getInt("defaultMaxAggrCount", defaultMaxAggrCount)); - setDefaultMaxAggrSize(getInt("defaultMaxAggrSize", defaultMaxAggrSize)); - setCorrectUsageDelay(getInt("correctUsageDelay", correctUsageDelay)); - setInitialExpansionPercent(getInt("initialExpansionPercent", initialExpansionPercent)); - - } catch (Exception e) { - logger.error("read application.properties failed", e); - } - } + setNotifyConnectTimeout(Integer.parseInt(env.getProperty("notifyConnectTimeout", "100"))); + logger.info("notifyConnectTimeout:{}", notifyConnectTimeout); + setNotifySocketTimeout(Integer.parseInt(env.getProperty("notifySocketTimeout", "200"))); + logger.info("notifySocketTimeout:{}", notifySocketTimeout); + setHealthCheck(Boolean.valueOf(env.getProperty("isHealthCheck", "true"))); + logger.info("isHealthCheck:{}", isHealthCheck); + setMaxHealthCheckFailCount(Integer.parseInt(env.getProperty("maxHealthCheckFailCount", "12"))); + logger.info("maxHealthCheckFailCount:{}", maxHealthCheckFailCount); + setMaxContent(Integer.parseInt(env.getProperty("maxContent", String.valueOf(maxContent)))); + logger.info("maxContent:{}", maxContent); + // 容量管理 + setManageCapacity(getBoolean("isManageCapacity", isManageCapacity)); + setCapacityLimitCheck(getBoolean("isCapacityLimitCheck", isCapacityLimitCheck)); + setDefaultClusterQuota(getInt("defaultClusterQuota", defaultClusterQuota)); + setDefaultGroupQuota(getInt("defaultGroupQuota", defaultGroupQuota)); + setDefaultTenantQuota(getInt("defaultTenantQuota", defaultTenantQuota)); + setDefaultMaxSize(getInt("defaultMaxSize", defaultMaxSize)); + setDefaultMaxAggrCount(getInt("defaultMaxAggrCount", defaultMaxAggrCount)); + setDefaultMaxAggrSize(getInt("defaultMaxAggrSize", defaultMaxAggrSize)); + setCorrectUsageDelay(getInt("correctUsageDelay", correctUsageDelay)); + setInitialExpansionPercent(getInt("initialExpansionPercent", initialExpansionPercent)); + setStandaloneUseMysql(getString("spring.datasource.platform", "").equals("mysql")); - public static int getNotifyConnectTimeout() { - return notifyConnectTimeout; - } + } catch (Exception e) { + logger.error("read application.properties failed", e); + } + } - public static int getNotifySocketTimeout() { - return notifySocketTimeout; - } + public static int getNotifyConnectTimeout() { + return notifyConnectTimeout; + } - public static int getMaxHealthCheckFailCount() { - return maxHealthCheckFailCount; - } + public static int getNotifySocketTimeout() { + return notifySocketTimeout; + } - public static boolean isHealthCheck() { - return isHealthCheck; - } + public static int getMaxHealthCheckFailCount() { + return maxHealthCheckFailCount; + } - private boolean getBoolean(String key, boolean defaultValue) { - return Boolean.valueOf(getString(key, String.valueOf(defaultValue))); - } + public static boolean isHealthCheck() { + return isHealthCheck; + } - private int getInt(String key, int defaultValue) { - return Integer.parseInt(getString(key, String.valueOf(defaultValue))); - } + private boolean getBoolean(String key, boolean defaultValue) { + return Boolean.valueOf(getString(key, String.valueOf(defaultValue))); + } - private String getString(String key, String defaultValue) { - String value = env.getProperty(key); - if (value == null) { - return defaultValue; - } - logger.info("{}:{}", key, value); - return value; - } + private int getInt(String key, int defaultValue) { + return Integer.parseInt(getString(key, String.valueOf(defaultValue))); + } - public String getProperty(String key) { - return env.getProperty(key); - } + private String getString(String key, String defaultValue) { + String value = env.getProperty(key); + if (value == null) { + return defaultValue; + } + logger.info("{}:{}", key, value); + return value; + } - public String getProperty(String key, String defaultValue) { - return env.getProperty(key, defaultValue); - } + public String getProperty(String key) { + return env.getProperty(key); + } - public static int getMaxContent() { - return maxContent; - } + public String getProperty(String key, String defaultValue) { + return env.getProperty(key, defaultValue); + } - public static boolean isManageCapacity() { - return isManageCapacity; - } + public static int getMaxContent() { + return maxContent; + } - public static int getDefaultClusterQuota() { - return defaultClusterQuota; - } + public static boolean isManageCapacity() { + return isManageCapacity; + } - public static boolean isCapacityLimitCheck() { - return isCapacityLimitCheck; - } + public static int getDefaultClusterQuota() { + return defaultClusterQuota; + } - public static int getDefaultGroupQuota() { - return defaultGroupQuota; - } + public static boolean isCapacityLimitCheck() { + return isCapacityLimitCheck; + } - public static int getDefaultTenantQuota() { - return defaultTenantQuota; - } + public static int getDefaultGroupQuota() { + return defaultGroupQuota; + } - public static int getInitialExpansionPercent() { - return initialExpansionPercent; - } + public static int getDefaultTenantQuota() { + return defaultTenantQuota; + } - public static int getDefaultMaxSize() { - return defaultMaxSize; - } + public static int getInitialExpansionPercent() { + return initialExpansionPercent; + } - public static int getDefaultMaxAggrCount() { - return defaultMaxAggrCount; - } + public static int getDefaultMaxSize() { + return defaultMaxSize; + } - public static int getDefaultMaxAggrSize() { - return defaultMaxAggrSize; - } + public static int getDefaultMaxAggrCount() { + return defaultMaxAggrCount; + } - public static int getCorrectUsageDelay() { - return correctUsageDelay; - } + public static int getDefaultMaxAggrSize() { + return defaultMaxAggrSize; + } - public static boolean isStandaloneMode() { - return standaloneMode; - } + public static int getCorrectUsageDelay() { + return correctUsageDelay; + } - public static void setStandaloneMode(boolean standaloneMode) { - PropertyUtil.standaloneMode = standaloneMode; - } + public static boolean isStandaloneMode() { + return STANDALONE_MODE; + } - public static void setNotifyConnectTimeout(int notifyConnectTimeout) { - PropertyUtil.notifyConnectTimeout = notifyConnectTimeout; - } + public static boolean isStandaloneUseMysql() { + return standaloneUseMysql; + } - public static void setNotifySocketTimeout(int notifySocketTimeout) { - PropertyUtil.notifySocketTimeout = notifySocketTimeout; - } + public static void setNotifyConnectTimeout(int notifyConnectTimeout) { + PropertyUtil.notifyConnectTimeout = notifyConnectTimeout; + } - public static void setMaxHealthCheckFailCount(int maxHealthCheckFailCount) { - PropertyUtil.maxHealthCheckFailCount = maxHealthCheckFailCount; - } + public static void setNotifySocketTimeout(int notifySocketTimeout) { + PropertyUtil.notifySocketTimeout = notifySocketTimeout; + } - public static void setHealthCheck(boolean isHealthCheck) { - PropertyUtil.isHealthCheck = isHealthCheck; - } + public static void setMaxHealthCheckFailCount(int maxHealthCheckFailCount) { + PropertyUtil.maxHealthCheckFailCount = maxHealthCheckFailCount; + } - public static void setMaxContent(int maxContent) { - PropertyUtil.maxContent = maxContent; - } + public static void setHealthCheck(boolean isHealthCheck) { + PropertyUtil.isHealthCheck = isHealthCheck; + } - public static void setManageCapacity(boolean isManageCapacity) { - PropertyUtil.isManageCapacity = isManageCapacity; - } + public static void setMaxContent(int maxContent) { + PropertyUtil.maxContent = maxContent; + } - public static void setCapacityLimitCheck(boolean isCapacityLimitCheck) { - PropertyUtil.isCapacityLimitCheck = isCapacityLimitCheck; - } + public static void setManageCapacity(boolean isManageCapacity) { + PropertyUtil.isManageCapacity = isManageCapacity; + } - public static void setDefaultClusterQuota(int defaultClusterQuota) { - PropertyUtil.defaultClusterQuota = defaultClusterQuota; - } + public static void setCapacityLimitCheck(boolean isCapacityLimitCheck) { + PropertyUtil.isCapacityLimitCheck = isCapacityLimitCheck; + } - public static void setDefaultGroupQuota(int defaultGroupQuota) { - PropertyUtil.defaultGroupQuota = defaultGroupQuota; - } + public static void setDefaultClusterQuota(int defaultClusterQuota) { + PropertyUtil.defaultClusterQuota = defaultClusterQuota; + } - public static void setDefaultTenantQuota(int defaultTenantQuota) { - PropertyUtil.defaultTenantQuota = defaultTenantQuota; - } + public static void setDefaultGroupQuota(int defaultGroupQuota) { + PropertyUtil.defaultGroupQuota = defaultGroupQuota; + } - public static void setDefaultMaxSize(int defaultMaxSize) { - PropertyUtil.defaultMaxSize = defaultMaxSize; - } + public static void setDefaultTenantQuota(int defaultTenantQuota) { + PropertyUtil.defaultTenantQuota = defaultTenantQuota; + } - public static void setDefaultMaxAggrCount(int defaultMaxAggrCount) { - PropertyUtil.defaultMaxAggrCount = defaultMaxAggrCount; - } + public static void setDefaultMaxSize(int defaultMaxSize) { + PropertyUtil.defaultMaxSize = defaultMaxSize; + } - public static void setDefaultMaxAggrSize(int defaultMaxAggrSize) { - PropertyUtil.defaultMaxAggrSize = defaultMaxAggrSize; - } + public static void setDefaultMaxAggrCount(int defaultMaxAggrCount) { + PropertyUtil.defaultMaxAggrCount = defaultMaxAggrCount; + } - public static void setInitialExpansionPercent(int initialExpansionPercent) { - PropertyUtil.initialExpansionPercent = initialExpansionPercent; - } + public static void setDefaultMaxAggrSize(int defaultMaxAggrSize) { + PropertyUtil.defaultMaxAggrSize = defaultMaxAggrSize; + } - public static void setCorrectUsageDelay(int correctUsageDelay) { - PropertyUtil.correctUsageDelay = correctUsageDelay; - } - + public static void setInitialExpansionPercent(int initialExpansionPercent) { + PropertyUtil.initialExpansionPercent = initialExpansionPercent; + } + + public static void setCorrectUsageDelay(int correctUsageDelay) { + PropertyUtil.correctUsageDelay = correctUsageDelay; + } + public static void setStandaloneUseMysql(boolean standaloneUseMysql) { + PropertyUtil.standaloneUseMysql = standaloneUseMysql; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/Protocol.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/Protocol.java index 25c4cbffc..02919b829 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/Protocol.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/Protocol.java @@ -17,15 +17,14 @@ package com.alibaba.nacos.config.server.utils; /** * 用来处理协议相关的操作 - * + * * @author zhidao * @version 1.0 2011/05/03 - * */ public class Protocol { /** * 解析类于2.0.4(major.minor.bug-fix这样的版本为数字) - * + * * @param version * @return */ @@ -38,8 +37,7 @@ public class Protocol { for (int i = 0; i < vs.length; i++) { try { sum = sum * 10 + Integer.parseInt(vs[i]); - } - catch (Exception e) { + } catch (Exception e) { // ignore } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/RegexParser.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/RegexParser.java index a8c321b00..f181d1652 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/RegexParser.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/RegexParser.java @@ -15,10 +15,7 @@ */ package com.alibaba.nacos.config.server.utils; -import org.apache.commons.lang.CharUtils; -import org.apache.commons.lang.NullArgumentException; - - +import org.apache.commons.lang3.CharUtils; /** * 用于ConfigCenter可支持的通配字符通配判定以及标准正则转换的通用类 @@ -28,19 +25,17 @@ import org.apache.commons.lang.NullArgumentException; */ public class RegexParser { - private final static char QUESTION_MARK = '?'; + private final static char QUESTION_MARK = '?'; /** - * 替换输入字符串中非正则特殊字符为标准正则表达式字符串;
- * '*'替换为 ‘.*’ '?'替换为'{n}',n为连续?的个数;
- * 其他非字母或数字的特殊字符前均添加'\'. + * 替换输入字符串中非正则特殊字符为标准正则表达式字符串;
'*'替换为 ‘.*’ '?'替换为'{n}',n为连续?的个数;
其他非字母或数字的特殊字符前均添加'\'. * * @param regex * @return */ static public String regexFormat(String regex) { if (regex == null) { - throw new NullArgumentException("regex string can't be null"); + throw new NullPointerException("regex string can't be null"); } StringBuffer result = new StringBuffer(); result.append("^"); @@ -75,8 +70,4 @@ public class RegexParser { return (regex.contains("?") || regex.contains("*")); } - public static void main(String[] args) { - String str = "com.taobao.uic.*"; - System.out.println(str + " -> " + regexFormat(str)); - } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/RequestUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/RequestUtil.java index c3248f84a..fb420c439 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/RequestUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/RequestUtil.java @@ -19,8 +19,8 @@ import javax.servlet.http.HttpServletRequest; /** * Request util - * @author Nacos * + * @author Nacos */ public class RequestUtil { @@ -30,9 +30,9 @@ public class RequestUtil { String nginxHeader = request.getHeader("X-Real-IP"); return (nginxHeader == null) ? request.getRemoteAddr() : nginxHeader; } - - public static String getAppName(HttpServletRequest request) { - return request.getHeader("Client-AppName"); - } - + + public static String getAppName(HttpServletRequest request) { + return request.getHeader("Client-AppName"); + } + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ResourceUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ResourceUtils.java index f00f95ba2..b991191a2 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ResourceUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ResourceUtils.java @@ -15,19 +15,15 @@ */ package com.alibaba.nacos.config.server.utils; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; +import com.alibaba.nacos.config.server.constant.Constants; + +import java.io.*; import java.net.URL; import java.util.Properties; -import com.alibaba.nacos.config.server.constant.Constants; - - /** * resource util + * * @author boyan * @date 2010-5-4 */ @@ -35,29 +31,23 @@ public class ResourceUtils { /** * Returns the URL of the resource on the classpath - * - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static URL getResourceURL(String resource) throws IOException { ClassLoader loader = ResourceUtils.class.getClassLoader(); return getResourceURL(loader, resource); } - /** * Returns the URL of the resource on the classpath - * - * @param loader - * The classloader used to load the resource - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param loader The classloader used to load the resource + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static URL getResourceURL(ClassLoader loader, String resource) throws IOException { URL url = null; @@ -73,32 +63,25 @@ public class ResourceUtils { return url; } - /** * Returns a resource on the classpath as a Stream object - * - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static InputStream getResourceAsStream(String resource) throws IOException { ClassLoader loader = ResourceUtils.class.getClassLoader(); return getResourceAsStream(loader, resource); } - /** * Returns a resource on the classpath as a Stream object - * - * @param loader - * The classloader used to load the resource - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param loader The classloader used to load the resource + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = null; @@ -114,32 +97,25 @@ public class ResourceUtils { return in; } - /** * Returns a resource on the classpath as a Properties object - * - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static Properties getResourceAsProperties(String resource) throws IOException { ClassLoader loader = ResourceUtils.class.getClassLoader(); return getResourceAsProperties(loader, resource); } - /** * Returns a resource on the classpath as a Properties object - * - * @param loader - * The classloader used to load the resource - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param loader The classloader used to load the resource + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException { Properties props = new Properties(); @@ -151,61 +127,47 @@ public class ResourceUtils { return props; } - /** * Returns a resource on the classpath as a Reader object - * - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static InputStreamReader getResourceAsReader(String resource) throws IOException { return new InputStreamReader(getResourceAsStream(resource), Constants.ENCODE); } - /** * Returns a resource on the classpath as a Reader object - * - * @param loader - * The classloader used to load the resource - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param loader The classloader used to load the resource + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ - public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException { - return new InputStreamReader(getResourceAsStream(loader, resource), Constants.ENCODE); - } - + public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException { + return new InputStreamReader(getResourceAsStream(loader, resource), Constants.ENCODE); + } /** * Returns a resource on the classpath as a File object - * - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static File getResourceAsFile(String resource) throws IOException { return new File(getResourceURL(resource).getFile()); } - /** * Returns a resource on the classpath as a File object - * - * @param loader - * The classloader used to load the resource - * @param resource - * The resource to find - * @throws IOException - * If the resource cannot be found or read + * + * @param loader The classloader used to load the resource + * @param resource The resource to find * @return The resource + * @throws IOException If the resource cannot be found or read */ public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException { return new File(getResourceURL(loader, resource).getFile()); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ResponseUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ResponseUtil.java index 2437ddcbb..8b4bce377 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ResponseUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ResponseUtil.java @@ -15,26 +15,25 @@ */ package com.alibaba.nacos.config.server.utils; -import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; - -import java.io.IOException; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog; /** * write response - * - * @author Nacos * + * @author Nacos */ public class ResponseUtil { - public static void writeErrMsg(HttpServletResponse response, int httpCode, - String msg) { - response.setStatus(httpCode); - try { - response.getWriter().println(msg); - } catch (IOException e) { - defaultLog.error("ResponseUtil:writeErrMsg wrong", e); - } - } + public static void writeErrMsg(HttpServletResponse response, int httpCode, + String msg) { + response.setStatus(httpCode); + try { + response.getWriter().println(msg); + } catch (IOException e) { + defaultLog.error("ResponseUtil:writeErrMsg wrong", e); + } + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/RunningConfigUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/RunningConfigUtils.java index a1dccf2b8..f16a1289c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/RunningConfigUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/RunningConfigUtils.java @@ -15,16 +15,16 @@ */ package com.alibaba.nacos.config.server.utils; -import javax.servlet.ServletContext; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; +import javax.servlet.ServletContext; + /** * Running config - * @author dungu.zpf + * @author nkorange */ @Component public class RunningConfigUtils implements ApplicationListener { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleCache.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleCache.java index 7f2182eca..6164e2b14 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleCache.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleCache.java @@ -18,13 +18,11 @@ package com.alibaba.nacos.config.server.utils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - /** * 一个带TTL的简单Cache,对于过期的entry没有清理 - * - * @author fengHan, jiuRen - * + * * @param + * @author fengHan, jiuRen */ public class SimpleCache { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleFlowData.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleFlowData.java index e7dbc1768..d795a7876 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleFlowData.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleFlowData.java @@ -23,15 +23,15 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Simple Flow data - * @author Nacos * + * @author Nacos */ public class SimpleFlowData { private int index = 0; private AtomicInteger[] data; private int average; private int slotCount; - + @SuppressWarnings("PMD.ThreadPoolCreationRule") private ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @@ -44,7 +44,6 @@ public class SimpleFlowData { }); - public SimpleFlowData(int slotCount, int interval) { this.slotCount = slotCount; data = new AtomicInteger[slotCount]; @@ -60,17 +59,14 @@ public class SimpleFlowData { }, interval, interval, TimeUnit.MILLISECONDS); } - public int addAndGet(int count) { return data[index].addAndGet(count); } - public int incrementAndGet() { return data[index].incrementAndGet(); } - public void rotateSlot() { int total = 0; @@ -84,22 +80,18 @@ public class SimpleFlowData { data[index].set(0); } - public int getCurrentCount() { return data[index].get(); } - public int getAverageCount() { return this.average; } - public int getSlotCount() { return this.slotCount; } - public String getSlotInfo() { StringBuilder sb = new StringBuilder(); @@ -114,7 +106,6 @@ public class SimpleFlowData { return sb.toString(); } - public int getCount(int prevStep) { prevStep = prevStep % this.slotCount; int index = (this.index + this.slotCount - prevStep) % this.slotCount; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleIPFlowData.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleIPFlowData.java index 7647d42af..88e0491f1 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleIPFlowData.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleIPFlowData.java @@ -21,12 +21,10 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; - /** * 根据IP进行流控, 控制单个IP的数量以及IP总量 - * + * * @author leiwen.zh - * */ @SuppressWarnings("PMD.ClassNamingShouldBeCamelRule") public class SimpleIPFlowData { @@ -57,12 +55,10 @@ public class SimpleIPFlowData { } - public SimpleIPFlowData(int slotCount, int interval) { if (slotCount <= 0) { this.slotCount = 1; - } - else { + } else { this.slotCount = slotCount; } data = new AtomicInteger[slotCount]; @@ -72,7 +68,6 @@ public class SimpleIPFlowData { timer.scheduleAtFixedRate(new DefaultIPFlowDataManagerTask(), interval, interval, TimeUnit.MILLISECONDS); } - public int incrementAndGet(String ip) { int index = 0; if (ip != null) { @@ -84,7 +79,6 @@ public class SimpleIPFlowData { return data[index].incrementAndGet(); } - public void rotateSlot() { int totalCount = 0; for (int i = 0; i < slotCount; i++) { @@ -94,7 +88,6 @@ public class SimpleIPFlowData { this.averageCount = totalCount / this.slotCount; } - public int getCurrentCount(String ip) { int index = 0; if (ip != null) { @@ -106,7 +99,6 @@ public class SimpleIPFlowData { return data[index].get(); } - public int getAverageCount() { return this.averageCount; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLock.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLock.java index 255152ba8..2a9730b2d 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLock.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLock.java @@ -15,14 +15,13 @@ */ package com.alibaba.nacos.config.server.utils; - /** * 最简单的读写锁实现。要求加锁和解锁必须成对调用。 - * @author Nacos * + * @author Nacos */ public class SimpleReadWriteLock { - + public synchronized boolean tryReadLock() { if (isWriteLocked()) { return false; @@ -31,11 +30,11 @@ public class SimpleReadWriteLock { return true; } } - + public synchronized void releaseReadLock() { status--; } - + public synchronized boolean tryWriteLock() { if (!isFree()) { return false; @@ -44,20 +43,21 @@ public class SimpleReadWriteLock { return true; } } - + public synchronized void releaseWriteLock() { status = 0; } - + private boolean isWriteLocked() { return status < 0; } + private boolean isFree() { return status == 0; } - + /** - * 零表示没有锁;负数表示加写锁;正数表示加读锁,数值表示读锁的个数。 + * 零表示没有锁;负数表示加写锁;正数表示加读锁,数值表示读锁的个数。 */ private int status = 0; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/SingletonRepository.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/SingletonRepository.java index e09fb8103..5e6581001 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/SingletonRepository.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/SingletonRepository.java @@ -17,9 +17,9 @@ package com.alibaba.nacos.config.server.utils; import java.util.concurrent.ConcurrentHashMap; - /** * 避免多个相同内容的实例的工具类。比如,可以用来缓存客户端IP。 + * * @author Nacos */ public class SingletonRepository { @@ -28,7 +28,7 @@ public class SingletonRepository { // 初始化大小2^16, 这个容器本身大概占用50k的内存,避免不停扩容 shared = new ConcurrentHashMap(1 << 16); } - + public T getSingleton(T obj) { T previous = shared.putIfAbsent(obj, obj); return (null == previous) ? obj : previous; @@ -37,17 +37,18 @@ public class SingletonRepository { public int size() { return shared.size(); } - + /** - * 必须小心使用。 + * 必须小心使用。 + * * @param obj obj */ public void remove(Object obj) { shared.remove(obj); } - + private final ConcurrentHashMap shared; - + /** * DataId和Group的缓存。 */ @@ -55,7 +56,7 @@ public class SingletonRepository { static public String getSingleton(String str) { return cache.getSingleton(str); } - + static SingletonRepository cache = new SingletonRepository(); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/StatConstants.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/StatConstants.java index 8bf22a3a1..ca219ab03 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/StatConstants.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/StatConstants.java @@ -17,8 +17,8 @@ package com.alibaba.nacos.config.server.utils; /** * Stat constant - * @author Nacos * + * @author Nacos */ public class StatConstants { private StatConstants() { @@ -33,5 +33,5 @@ public class StatConstants { public static final String STAT_AVERAGE_HTTP_GET_OTHER = "AverageHttpGet_Other_Status"; public static final String STAT_AVERAGE_HTTP_POST_CHECK = "AverageHttpPost_Check"; - + } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/StringUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/StringUtils.java index 8b0f63336..7c491ca80 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/StringUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/StringUtils.java @@ -17,13 +17,13 @@ package com.alibaba.nacos.config.server.utils; /** * 替代common-lang中类,减少依赖 - * @author Nacos * + * @author Nacos */ public class StringUtils { public static final int INDEX_NOT_FOUND = -1; - + public static boolean isBlank(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { @@ -36,34 +36,34 @@ public class StringUtils { } return true; } - + public static boolean isNotEmpty(String str) { return !StringUtils.isEmpty(str); } - + public static boolean isEmpty(String str) { return str == null || str.length() == 0; } - + public static String defaultIfEmpty(String str, String defaultStr) { return StringUtils.isEmpty(str) ? defaultStr : str; } - + public static boolean equals(String str1, String str2) { return str1 == null ? str2 == null : str1.equals(str2); } - - public static String substringBetween(String str, String open, String close) { - if (str == null || open == null || close == null) { - return null; - } - int start = str.indexOf(open); - if (start != INDEX_NOT_FOUND) { - int end = str.indexOf(close, start + open.length()); - if (end != INDEX_NOT_FOUND) { - return str.substring(start + open.length(), end); - } - } - return null; - } + + public static String substringBetween(String str, String open, String close) { + if (str == null || open == null || close == null) { + return null; + } + int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) { + int end = str.indexOf(close, start + open.length()); + if (end != INDEX_NOT_FOUND) { + return str.substring(start + open.length(), end); + } + } + return null; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/SystemConfig.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/SystemConfig.java index cc70258d0..a1bc2f297 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/SystemConfig.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/SystemConfig.java @@ -15,18 +15,18 @@ */ package com.alibaba.nacos.config.server.utils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * System config - * @author Nacos * + * @author Nacos */ public class SystemConfig { @@ -35,26 +35,26 @@ public class SystemConfig { private static final Logger log = LoggerFactory.getLogger(SystemConfig.class); private static String getHostAddress() { - String address = System.getProperty("nacos.server.ip"); - if (StringUtils.isNotEmpty(address)) { - return address; - } else { - address = "127.0.0.1"; - } + String address = System.getProperty("nacos.server.ip"); + if (StringUtils.isNotEmpty(address)) { + return address; + } else { + address = "127.0.0.1"; + } try { Enumeration en = NetworkInterface.getNetworkInterfaces(); while (en.hasMoreElements()) { NetworkInterface ni = en.nextElement(); Enumeration ads = ni.getInetAddresses(); - while (ads.hasMoreElements()) { - InetAddress ip = ads.nextElement(); - // 兼容集团不规范11网段 - if (!ip.isLoopbackAddress() - && ip.getHostAddress().indexOf(":") == -1 - /* && ip.isSiteLocalAddress() */) { - return ip.getHostAddress(); - } - } + while (ads.hasMoreElements()) { + InetAddress ip = ads.nextElement(); + // 兼容集团不规范11网段 + if (!ip.isLoopbackAddress() + && ip.getHostAddress().indexOf(":") == -1 + /* && ip.isSiteLocalAddress() */) { + return ip.getHostAddress(); + } + } } } catch (Exception e) { log.error("get local host address error", e); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ThreadUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ThreadUtil.java index 0b7780681..eb017c3e9 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ThreadUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ThreadUtil.java @@ -17,23 +17,24 @@ package com.alibaba.nacos.config.server.utils; /** * Thread util - * @author Nacos * + * @author Nacos */ public class ThreadUtil { - /** - * 通过内核数,算出合适的线程数;1.5-2倍cpu内核数 - * @return thread count - */ - public static int getSuitableThreadCount() { - final int coreCount = Runtime.getRuntime().availableProcessors(); - int workerCount = 1; - while (workerCount < coreCount * THREAD_MULTIPLER) { - workerCount <<= 1; - } - return workerCount; - } - - private final static int THREAD_MULTIPLER = 2; + /** + * 通过内核数,算出合适的线程数;1.5-2倍cpu内核数 + * + * @return thread count + */ + public static int getSuitableThreadCount() { + final int coreCount = Runtime.getRuntime().availableProcessors(); + int workerCount = 1; + while (workerCount < coreCount * THREAD_MULTIPLER) { + workerCount <<= 1; + } + return workerCount; + } + + private final static int THREAD_MULTIPLER = 2; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeUtils.java index 644af4811..6990c4cdf 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeUtils.java @@ -15,30 +15,27 @@ */ package com.alibaba.nacos.config.server.utils; +import org.apache.commons.lang3.time.FastDateFormat; + import java.sql.Timestamp; import java.util.Calendar; import java.util.Date; -import org.apache.commons.lang.time.FastDateFormat; /** * Time util - * @author Nacos * + * @author Nacos */ public class TimeUtils { - public static Timestamp getCurrentTime() { + public static Timestamp getCurrentTime() { Date date = new Date(); return new Timestamp(date.getTime()); } - public static void main(String[] args) { - System.out.println(getCurrentTime().toString()); - } - - static public String getCurrentTimeStr() { + + static public String getCurrentTimeStr() { Calendar c = Calendar.getInstance(); c.setTime(new Date()); - c.get(Calendar.HOUR); FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); return format.format(c.getTime()); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeoutUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeoutUtils.java index d3a41b47a..62f9253a3 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeoutUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/TimeoutUtils.java @@ -17,19 +17,16 @@ package com.alibaba.nacos.config.server.utils; import java.util.concurrent.atomic.AtomicLong; - /** - * 处理超时的工具类, 用于客户端获取数据的总体超时。 每次从网络获取完数据后, 累计totalTime, 每次从网络获取数据前, - * 检查totalTime是否大于totalTimeout, 是则说明总体超时, totalTime有失效时间, 每次从网络获取数据前, 检查是否失效, - * 失效则重置totalTime, 重新开始累计 - * + * 处理超时的工具类, 用于客户端获取数据的总体超时。 每次从网络获取完数据后, 累计totalTime, 每次从网络获取数据前, 检查totalTime是否大于totalTimeout, 是则说明总体超时, + * totalTime有失效时间, 每次从网络获取数据前, 检查是否失效, 失效则重置totalTime, 重新开始累计 + * * @author leiwen.zh - * */ public class TimeoutUtils { /** - * 累计的获取数据消耗的时间, 单位ms + * 累计的获取数据消耗的时间, 单位ms */ private final AtomicLong totalTime = new AtomicLong(0L); @@ -38,21 +35,19 @@ public class TimeoutUtils { private volatile boolean initialized = false; /** - * 获取数据的总体超时, 单位ms + * 获取数据的总体超时, 单位ms */ private long totalTimeout; /** - * 累计的获取数据消耗的时间的过期时间, 单位ms + * 累计的获取数据消耗的时间的过期时间, 单位ms */ private long invalidThreshold; - public TimeoutUtils(long totalTimeout, long invalidThreshold) { this.totalTimeout = totalTimeout; this.invalidThreshold = invalidThreshold; } - public synchronized void initLastResetTime() { if (initialized) { return; @@ -61,27 +56,24 @@ public class TimeoutUtils { initialized = true; } - /** * 累计总的时间 - * + * * @param timeout */ public void addTotalTime(long time) { totalTime.addAndGet(time); } - /** * 判断是否超时 - * + * * @return */ public boolean isTimeout() { return totalTime.get() > this.totalTimeout; } - /** * 总的时间清零 */ @@ -92,12 +84,10 @@ public class TimeoutUtils { } } - public AtomicLong getTotalTime() { return totalTime; } - private boolean isTotalTimeExpired() { return System.currentTimeMillis() - lastResetTime > this.invalidThreshold; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/TraceLogUtil.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/TraceLogUtil.java index c44af306a..239efee38 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/TraceLogUtil.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/TraceLogUtil.java @@ -20,19 +20,18 @@ import org.slf4j.LoggerFactory; /** * Trace Util - * @author Nacos * + * @author Nacos */ public class TraceLogUtil { /** - * 记录server各个接口的请求记录 + * 记录server各个接口的请求记录 */ public static Logger requestLog = LoggerFactory.getLogger("com.alibaba.nacos.config.request"); /** - * 记录各个client的轮询请求记录 + * 记录各个client的轮询请求记录 */ public static Logger pollingLog = LoggerFactory.getLogger("com.alibaba.nacos.config.polling"); - } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/UrlAnalysisUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/UrlAnalysisUtils.java index 261b3250f..f02eb994a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/UrlAnalysisUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/UrlAnalysisUtils.java @@ -15,23 +15,20 @@ */ package com.alibaba.nacos.config.server.utils; +import com.alibaba.nacos.config.server.constant.Constants; + import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.alibaba.nacos.config.server.constant.Constants; - - /** * 分析url的工具类 - * + * * @author leiwen.zh - * */ public class UrlAnalysisUtils { private static Pattern urlPattern = Pattern.compile("^(\\w+://)?([\\w\\.]+:)(\\d*)?(\\??.*)"); - public static String getContentIdentity(String content) { if (!verifyIncrementPubContent(content)) { @@ -55,7 +52,6 @@ public class UrlAnalysisUtils { return buf.toString(); } - private static boolean verifyIncrementPubContent(String content) { if (content == null || content.length() == 0) { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/event/EventDispatcher.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/event/EventDispatcher.java index a6c8dd19d..15abf3008 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/event/EventDispatcher.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/event/EventDispatcher.java @@ -15,18 +15,16 @@ */ package com.alibaba.nacos.config.server.utils.event; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * Event dispatcher - * - * @author Nacos * + * @author Nacos */ public class EventDispatcher { @@ -57,17 +55,17 @@ public class EventDispatcher { } /** - * For only test purpose + * For only test purpose */ static public void clear() { LISTENER_HUB.clear(); } - + /** * get event listener for eventType. Add Entry if not exist. */ static Entry getEntry(Class eventType) { - for (;;) { + for (; ; ) { for (Entry entry : LISTENER_HUB) { if (entry.eventType == eventType) { return entry; @@ -101,47 +99,44 @@ public class EventDispatcher { if (this == obj) { return true; } - return eventType == ((Entry) obj).eventType; + return eventType == ((Entry)obj).eventType; } - @Override - public int hashCode() { - return super.hashCode(); - } - - } + @Override + public int hashCode() { + return super.hashCode(); + } + } static private final Logger log = LoggerFactory.getLogger(EventDispatcher.class); static final CopyOnWriteArrayList LISTENER_HUB = new CopyOnWriteArrayList(); - static public interface Event { } static public abstract class AbstractEventListener { public AbstractEventListener() { - /** - * automatic register - */ - EventDispatcher.addEventListener(this); + /** + * automatic register + */ + EventDispatcher.addEventListener(this); } - /** - * 感兴趣的事件列表 - * - * @return event list - */ - abstract public List> interest(); + /** + * 感兴趣的事件列表 + * + * @return event list + */ + abstract public List> interest(); - /** - * 处理事件 - * - * @param event - * event - */ + /** + * 处理事件 + * + * @param event event + */ abstract public void onEvent(Event event); } diff --git a/config/src/main/resources/nacos-config-logback.xml b/config/src/main/resources/META-INF/logback/nacos-included.xml similarity index 53% rename from config/src/main/resources/nacos-config-logback.xml rename to config/src/main/resources/META-INF/logback/nacos-included.xml index 207b7bd66..515a5fb47 100755 --- a/config/src/main/resources/nacos-config-logback.xml +++ b/config/src/main/resources/META-INF/logback/nacos-included.xml @@ -1,283 +1,262 @@ - - - - - - ${user.home}/nacos/logs/config-dump.log - true - - ${user.home}/nacos/logs/config-dump.log.%d{yyyy-MM-dd}.%i - 2GB - 15 - 7GB - true - - - %date %level %msg%n%n - GBK - - - - ${user.home}/nacos/logs/config-pull.log - true - - ${user.home}/nacos/logs/config-pull.log.%d{yyyy-MM-dd}.%i - 20MB - 15 - 128MB - true - - - %date %level %msg%n%n - GBK - - - - ${user.home}/nacos/logs/config-fatal.log - true - - ${user.home}/nacos/logs/config-fatal.log.%d{yyyy-MM-dd}.%i - 20MB - 15 - 128MB - true - - - %date %level %msg%n%n - GBK - - - - ${user.home}/nacos/logs/config-memory.log - true - - ${user.home}/nacos/logs/config-memory.log.%d{yyyy-MM-dd}.%i - 20MB - 15 - 128MB - true - - - %date %level %msg%n%n - GBK - - - - ${user.home}/nacos/logs/config-pull-check.log - true - - ${user.home}/nacos/logs/config-pull-check.log.%d{yyyy-MM-dd}.%i - 1GB - 15 - 3GB - true - - - %msg%n - GBK - - - - - ${user.home}/nacos/logs/config-acl.log - true - - ${user.home}/nacos/logs/config-acl.log.%d{yyyy-MM-dd}.%i - 50MB - 15 - 512MB - true - - - %date %level %msg%n%n - GBK - - - - - ${user.home}/nacos/logs/config-client-request.log - true - - ${user.home}/nacos/logs/config-client-request.log.%d{yyyy-MM-dd}.%i - 2GB - 15 - 7GB - true - - - %date|%msg%n - GBK - - - - - ${user.home}/nacos/logs/config-sdk-request.log - true - - ${user.home}/nacos/logs/config-sdk-request.log.%d{yyyy-MM-dd}.%i - 1GB - 15 - 3GB - true - - - %date|%msg%n - GBK - - - - - ${user.home}/nacos/logs/config-trace.log - true - - ${user.home}/nacos/logs/config-trace.log.%d{yyyy-MM-dd}.%i - 2GB - 15 - 7GB - true - - - %date|%msg%n - GBK - - - - - ${user.home}/nacos/logs/config-notify.log - true - - ${user.home}/nacos/logs/config-notify.log.%d{yyyy-MM-dd}.%i - 1GB - 15 - 3GB - true - - - %date %level %msg%n%n - GBK - - - - - ${user.home}/nacos/logs/config-app.log - true - - ${user.home}/nacos/logs/config-app.log.%d{yyyy-MM-dd}.%i - 20MB - 15 - 128MB - true - - - %date %level %msg%n%n - GBK - - - - - ${user.home}/nacos/logs/config-server.log - true - - ${user.home}/nacos/logs/config-server.log.%d{yyyy-MM-dd}.%i - 50MB - 15 - 512MB - true - - - %date %level %msg%n%n - GBK - - - - - ${user.home}/nacos/logs/nacos.log - true - - ${user.home}/nacos/logs/nacos.log.%d{yyyy-MM-dd}.%i - 50MB - 15 - 512MB - true - - - %date %level %msg%n%n - GBK - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + ${LOG_HOME}/config-dump.log + true + + ${LOG_HOME}/config-dump.log.%d{yyyy-MM-dd}.%i + 2GB + 15 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-pull.log + true + + ${LOG_HOME}/config-pull.log.%d{yyyy-MM-dd}.%i + 20MB + 15 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-fatal.log + true + + ${LOG_HOME}/config-fatal.log.%d{yyyy-MM-dd}.%i + 20MB + 15 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-memory.log + true + + ${LOG_HOME}/config-memory.log.%d{yyyy-MM-dd}.%i + 20MB + 15 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-pull-check.log + true + + ${LOG_HOME}/config-pull-check.log.%d{yyyy-MM-dd}.%i + 1GB + 15 + 3GB + true + + + %msg%n + UTF-8 + + + + + ${LOG_HOME}/config-acl.log + true + + ${LOG_HOME}/config-acl.log.%d{yyyy-MM-dd}.%i + 50MB + 15 + 512MB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/config-client-request.log + true + + ${LOG_HOME}/config-client-request.log.%d{yyyy-MM-dd}.%i + 2GB + 15 + 7GB + true + + + %date|%msg%n + UTF-8 + + + + + ${LOG_HOME}/config-sdk-request.log + true + + ${LOG_HOME}/config-sdk-request.log.%d{yyyy-MM-dd}.%i + 1GB + 15 + 3GB + true + + + %date|%msg%n + UTF-8 + + + + + ${LOG_HOME}/config-trace.log + true + + ${LOG_HOME}/config-trace.log.%d{yyyy-MM-dd}.%i + 2GB + 15 + 7GB + true + + + %date|%msg%n + UTF-8 + + + + + ${LOG_HOME}/config-notify.log + true + + ${LOG_HOME}/config-notify.log.%d{yyyy-MM-dd}.%i + 1GB + 15 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/config-app.log + true + + ${LOG_HOME}/config-app.log.%d{yyyy-MM-dd}.%i + 20MB + 15 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/config-server.log + true + + ${LOG_HOME}/config-server.log.%d{yyyy-MM-dd}.%i + 50MB + 15 + 512MB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/src/main/resources/nacos-db.sql b/config/src/main/resources/META-INF/nacos-db.sql similarity index 60% rename from config/src/main/resources/nacos-db.sql rename to config/src/main/resources/META-INF/nacos-db.sql index 08494d054..0ccf886c0 100644 --- a/config/src/main/resources/nacos-db.sql +++ b/config/src/main/resources/META-INF/nacos-db.sql @@ -4,25 +4,24 @@ /******************************************/ CREATE TABLE `config_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', - `data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id', - `group_id` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `content` longtext CHARACTER SET utf8 NOT NULL COMMENT 'content', - `md5` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT 'md5', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(255) DEFAULT NULL, + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', - `src_user` text CHARACTER SET utf8 COMMENT '不用了', - `src_ip` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '不用了', - `app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL, - `tenant_id` varchar(128) CHARACTER SET gbk COLLATE gbk_bin DEFAULT '' COMMENT '租户字段', - `c_desc` varchar(256) COLLATE utf8_bin DEFAULT NULL, - `c_use` varchar(64) COLLATE utf8_bin DEFAULT NULL, - `effect` varchar(64) COLLATE utf8_bin DEFAULT NULL, - `type` varchar(64) COLLATE utf8_bin DEFAULT NULL, - `c_schema` text COLLATE utf8_bin, + `src_user` text COMMENT 'source user', + `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', + `app_name` varchar(128) DEFAULT NULL, + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `c_desc` varchar(256) DEFAULT NULL, + `c_use` varchar(64) DEFAULT NULL, + `effect` varchar(64) DEFAULT NULL, + `type` varchar(64) DEFAULT NULL, + `c_schema` text, PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) -) ENGINE=InnoDB AUTO_INCREMENT=2042668 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; /******************************************/ /* 数据库全名 = nacos_config */ @@ -30,17 +29,16 @@ CREATE TABLE `config_info` ( /******************************************/ CREATE TABLE `config_info_aggr` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', - `data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id', - `group_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'group_id', - `datum_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'datum_id', - `content` longtext COLLATE utf8_bin NOT NULL COMMENT '内容', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(255) NOT NULL COMMENT 'group_id', + `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', + `content` longtext NOT NULL COMMENT '内容', `gmt_modified` datetime NOT NULL COMMENT '修改时间', - `app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL, - `tenant_id` varchar(128) CHARACTER SET gbk COLLATE gbk_bin DEFAULT '' COMMENT '租户字段', + `app_name` varchar(128) DEFAULT NULL, + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) -) ENGINE=InnoDB AUTO_INCREMENT=17423 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段'; /******************************************/ @@ -49,21 +47,20 @@ CREATE TABLE `config_info_aggr` ( /******************************************/ CREATE TABLE `config_info_beta` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', - `data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id', - `group_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'group_id', - `app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT 'app_name', - `content` longtext CHARACTER SET utf8 NOT NULL COMMENT 'content', - `beta_ips` varchar(1024) COLLATE utf8_bin DEFAULT NULL COMMENT 'betaIps', - `md5` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT 'md5', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', - `src_user` text CHARACTER SET utf8 COMMENT '不用了', - `src_ip` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '不用了', - `tenant_id` varchar(128) CHARACTER SET gbk COLLATE gbk_bin DEFAULT '' COMMENT '租户字段', + `src_user` text COMMENT 'source user', + `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) -) ENGINE=InnoDB AUTO_INCREMENT=437 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta'; /******************************************/ /* 数据库全名 = nacos_config */ @@ -80,12 +77,11 @@ CREATE TABLE `config_info_tag` ( `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', - `src_user` text COMMENT '不用了', - `src_ip` varchar(20) DEFAULT NULL COMMENT '不用了', + `src_user` text COMMENT 'source user', + `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) -) ENGINE=InnoDB AUTO_INCREMENT=609 DEFAULT CHARSET=utf8 COMMENT='config_info_tag' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; /******************************************/ /* 数据库全名 = nacos_config */ @@ -93,17 +89,16 @@ CREATE TABLE `config_info_tag` ( /******************************************/ CREATE TABLE `config_tags_relation` ( `id` bigint(20) NOT NULL COMMENT 'id', - `tag_name` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'tag_name', - `tag_type` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT 'tag_type', - `data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id', - `group_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'group_id', - `tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT 'tenant_id', + `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', + `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `nid` bigint(20) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`nid`), UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), KEY `idx_tenant_id` (`tenant_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; /******************************************/ /* 数据库全名 = nacos_config */ @@ -111,7 +106,7 @@ CREATE TABLE `config_tags_relation` ( /******************************************/ CREATE TABLE `group_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `group_id` varchar(128) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', + `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', @@ -122,8 +117,7 @@ CREATE TABLE `group_capacity` ( `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_group_id` (`group_id`) -) ENGINE=InnoDB AUTO_INCREMENT=1362 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表'; /******************************************/ /* 数据库全名 = nacos_config */ @@ -132,23 +126,22 @@ CREATE TABLE `group_capacity` ( CREATE TABLE `his_config_info` ( `id` bigint(64) unsigned NOT NULL, `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `data_id` varchar(255) COLLATE utf8_bin NOT NULL, - `group_id` varchar(128) COLLATE utf8_bin NOT NULL, - `app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT 'app_name', - `content` longtext CHARACTER SET utf8 NOT NULL, - `md5` varchar(32) CHARACTER SET utf8 DEFAULT NULL, + `data_id` varchar(255) NOT NULL, + `group_id` varchar(128) NOT NULL, + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL, + `md5` varchar(32) DEFAULT NULL, `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00', - `src_user` text CHARACTER SET utf8, - `src_ip` varchar(20) COLLATE utf8_bin DEFAULT NULL, - `op_type` char(10) COLLATE utf8_bin DEFAULT NULL, - `tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '租户字段', + `src_user` text, + `src_ip` varchar(20) DEFAULT NULL, + `op_type` char(10) DEFAULT NULL, + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`nid`), KEY `idx_gmt_create` (`gmt_create`), KEY `idx_gmt_modified` (`gmt_modified`), KEY `idx_did` (`data_id`) -) ENGINE=InnoDB AUTO_INCREMENT=444359 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造'; /******************************************/ @@ -157,7 +150,7 @@ CREATE TABLE `his_config_info` ( /******************************************/ CREATE TABLE `tenant_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `tenant_id` varchar(128) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'Tenant ID', + `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', @@ -168,5 +161,19 @@ CREATE TABLE `tenant_capacity` ( `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_id` (`tenant_id`) -) ENGINE=InnoDB AUTO_INCREMENT=525 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表' -; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表'; + + +CREATE TABLE `tenant_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `kp` varchar(128) NOT NULL COMMENT 'kp', + `tenant_id` varchar(128) default '' COMMENT 'tenant_id', + `tenant_name` varchar(128) default '' COMMENT 'tenant_name', + `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', + `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', + `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', + `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; \ No newline at end of file diff --git a/config/src/main/resources/schema.sql b/config/src/main/resources/META-INF/schema.sql similarity index 92% rename from config/src/main/resources/schema.sql rename to config/src/main/resources/META-INF/schema.sql index e5722e896..ceabf32e4 100644 --- a/config/src/main/resources/schema.sql +++ b/config/src/main/resources/META-INF/schema.sql @@ -158,4 +158,18 @@ CREATE TABLE tenant_capacity ( gmt_modified timestamp DEFAULT '2010-05-05 00:00:00', constraint tenant_capacity_id_key PRIMARY KEY (id), constraint uk_tenant_id UNIQUE (tenant_id)); + +CREATE TABLE tenant_info ( + id bigint NOT NULL generated by default as identity, + kp varchar(128) NOT NULL, + tenant_id varchar(128) DEFAULT '', + tenant_name varchar(128) DEFAULT '', + tenant_desc varchar(256) DEFAULT NULL, + create_source varchar(32) DEFAULT NULL, + gmt_create bigint NOT NULL, + gmt_modified bigint NOT NULL, + constraint tenant_info_id_key PRIMARY KEY (id), + constraint uk_tenant_info_kptenantid UNIQUE (kp,tenant_id)); +CREATE INDEX tenant_info_tenant_id_idx ON tenant_info(tenant_id); + diff --git a/config/src/main/resources/application.properties b/config/src/main/resources/application.properties deleted file mode 100644 index 1bf0abb4d..000000000 --- a/config/src/main/resources/application.properties +++ /dev/null @@ -1,17 +0,0 @@ -# spring -management.security.enabled=false -server.servlet.context-path=/nacos -server.port=8848 - -db.num=2 -db.url.0=url1 -db.url.1=url2 -db.user=user -db.password=pwd - -spring.http.encoding.force=true -spring.http.encoding.charset=UTF-8 -spring.http.encoding.enabled=true -server.tomcat.uri-encoding=UTF-8 -spring.messages.encoding=UTF-8 -security.headers.content-type=application/json;charset=UTF-8 \ No newline at end of file diff --git a/config/src/main/resources/banner.txt b/config/src/main/resources/banner.txt deleted file mode 100644 index 4ebabe6fa..000000000 --- a/config/src/main/resources/banner.txt +++ /dev/null @@ -1,15 +0,0 @@ - - ,--. - ,--.'| - ,--,: : | -,`--.'`| ' : ,---. -| : : | | ' ,'\ .--.--. -: | \ | : ,--.--. ,---. / / | / / ' -| : ' '; | / \ / \. ; ,. :| : /`./ -' ' ;. ;.--. .-. | / / '' | |: :| : ;_ -| | | \ | \__\/: . .. ' / ' | .; : \ \ `. -' : | ; .' ," .--.; |' ; :__| : | `----. \ -| | '`--' / / ,. |' | '.'|\ \ / / /`--' / -' : | ; : .' \ : : `----' '--'. / -; |.' | , .-./\ \ / `--'---' -'---' `--`---' `----' diff --git a/config/src/test/java/com/alibaba/nacos/config/mock/FilterConfigMock.java b/config/src/test/java/com/alibaba/nacos/config/mock/FilterConfigMock.java deleted file mode 100644 index 02095a9b9..000000000 --- a/config/src/test/java/com/alibaba/nacos/config/mock/FilterConfigMock.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.config.mock; - -import java.util.Enumeration; - -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; - - - -public class FilterConfigMock implements FilterConfig { - - public FilterConfigMock(ServletContext context) { - this.context = context; - } - - - @Override - public String getFilterName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public ServletContext getServletContext() { - return context; - } - - @Override - public String getInitParameter(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Enumeration getInitParameterNames() { - // TODO Auto-generated method stub - return null; - } - - final ServletContext context; -} diff --git a/config/src/test/java/com/alibaba/nacos/config/mock/ServletContextMock.java b/config/src/test/java/com/alibaba/nacos/config/mock/ServletContextMock.java deleted file mode 100644 index 736543b65..000000000 --- a/config/src/test/java/com/alibaba/nacos/config/mock/ServletContextMock.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright 1999-2018 Alibaba Group Holding Ltd. - * - * 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 com.alibaba.nacos.config.mock; - -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Enumeration; -import java.util.EventListener; -import java.util.Map; -import java.util.Set; - -import javax.servlet.Filter; -import javax.servlet.FilterRegistration; -import javax.servlet.FilterRegistration.Dynamic; -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; -import javax.servlet.SessionCookieConfig; -import javax.servlet.SessionTrackingMode; -import javax.servlet.descriptor.JspConfigDescriptor; - - - - -public class ServletContextMock implements ServletContext { - - @Override - public String getContextPath() { - // TODO Auto-generated method stub - return null; - } - - @Override - public ServletContext getContext(String uripath) { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getMajorVersion() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int getMinorVersion() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public String getMimeType(String file) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set getResourcePaths(String path) { - // TODO Auto-generated method stub - return null; - } - - @Override - public URL getResource(String path) throws MalformedURLException { - // TODO Auto-generated method stub - return null; - } - - @Override - public InputStream getResourceAsStream(String path) { - // TODO Auto-generated method stub - return null; - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - // TODO Auto-generated method stub - return null; - } - - @Override - public RequestDispatcher getNamedDispatcher(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Servlet getServlet(String name) throws ServletException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Enumeration getServlets() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Enumeration getServletNames() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void log(String msg) { - // TODO Auto-generated method stub - - } - - @Override - public void log(Exception exception, String msg) { - // TODO Auto-generated method stub - - } - - @Override - public void log(String message, Throwable throwable) { - // TODO Auto-generated method stub - - } - - @Override - public String getRealPath(String path) { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getServerInfo() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getInitParameter(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Enumeration getInitParameterNames() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object getAttribute(String name) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Enumeration getAttributeNames() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setAttribute(String name, Object object) { - // TODO Auto-generated method stub - - } - - @Override - public void removeAttribute(String name) { - // TODO Auto-generated method stub - - } - - @Override - public String getServletContextName() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Dynamic addFilter(String arg0, String arg1) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Dynamic addFilter(String arg0, Filter arg1) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Dynamic addFilter(String arg0, Class arg1) { - // TODO Auto-generated method stub - return null; - } - - @Override - public void addListener(Class arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void addListener(String arg0) { - // TODO Auto-generated method stub - - } - - @Override - public void addListener(T arg0) { - // TODO Auto-generated method stub - - } - - @Override - public javax.servlet.ServletRegistration.Dynamic addServlet(String arg0, String arg1) { - // TODO Auto-generated method stub - return null; - } - - @Override - public javax.servlet.ServletRegistration.Dynamic addServlet(String arg0, Servlet arg1) { - // TODO Auto-generated method stub - return null; - } - - @Override - public javax.servlet.ServletRegistration.Dynamic addServlet(String arg0, - Class arg1) { - // TODO Auto-generated method stub - return null; - } - - @Override - public T createFilter(Class arg0) throws ServletException { - // TODO Auto-generated method stub - return null; - } - - @Override - public T createListener(Class arg0) throws ServletException { - // TODO Auto-generated method stub - return null; - } - - @Override - public T createServlet(Class arg0) throws ServletException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void declareRoles(String... arg0) { - // TODO Auto-generated method stub - - } - - @Override - public ClassLoader getClassLoader() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set getDefaultSessionTrackingModes() { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getEffectiveMajorVersion() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int getEffectiveMinorVersion() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public Set getEffectiveSessionTrackingModes() { - // TODO Auto-generated method stub - return null; - } - - @Override - public FilterRegistration getFilterRegistration(String arg0) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map getFilterRegistrations() { - // TODO Auto-generated method stub - return null; - } - - @Override - public JspConfigDescriptor getJspConfigDescriptor() { - // TODO Auto-generated method stub - return null; - } - - @Override - public ServletRegistration getServletRegistration(String arg0) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map getServletRegistrations() { - // TODO Auto-generated method stub - return null; - } - - @Override - public SessionCookieConfig getSessionCookieConfig() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean setInitParameter(String arg0, String arg1) { - // TODO Auto-generated method stub - return false; - } - - @Override - public void setSessionTrackingModes(Set arg0) - throws IllegalStateException, IllegalArgumentException { - // TODO Auto-generated method stub - } - - @Override - public String getVirtualServerName() { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/configuration/LocalDataSourceConfigurationTest.java b/config/src/test/java/com/alibaba/nacos/config/server/configuration/LocalDataSourceConfigurationTest.java new file mode 100644 index 000000000..065238790 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/configuration/LocalDataSourceConfigurationTest.java @@ -0,0 +1,91 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package com.alibaba.nacos.config.server.configuration; +// +//import org.apache.commons.dbcp.BasicDataSource; +//import org.junit.Assert; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.beans.factory.annotation.Qualifier; +//import org.springframework.jdbc.core.JdbcTemplate; +//import org.springframework.jdbc.datasource.DataSourceTransactionManager; +//import org.springframework.test.context.ActiveProfiles; +//import org.springframework.test.context.ContextConfiguration; +//import org.springframework.test.context.TestPropertySource; +//import org.springframework.test.context.junit4.SpringRunner; +//import org.springframework.transaction.PlatformTransactionManager; +// +//import javax.sql.DataSource; +// +//import static com.alibaba.nacos.config.server.configuration.DataBaseConfiguration.DATA_SOURCE_BEAN_NAME; +// +///** +// * {@link LocalDataSourceConfiguration} Test +// * +// * @author Mercy +// * @since 0.2.2 +// */ +//@RunWith(SpringRunner.class) +//@ContextConfiguration(classes = { +// LocalDataSourceConfiguration.class, +// DataBaseConfiguration.class, +// LocalDataSourceConfigurationTest.class +//}) +//@TestPropertySource(properties = { +// "db.initialSize=1", +// "db.maxActive=2", +// "db.maxIdle=3", +// "db.maxWait=4", +//}) +//@ActiveProfiles("standalone") +//public class LocalDataSourceConfigurationTest { +// +// @Autowired +// @Qualifier(DATA_SOURCE_BEAN_NAME) +// private DataSource nacosConfigDataSource; +// +// @Autowired +// private JdbcTemplate jdbcTemplate; +// +// @Autowired +// private PlatformTransactionManager platformTransactionManager; +// +// @Test +// public void testDataSource() { +// Assert.assertNotNull(nacosConfigDataSource); +// Assert.assertTrue(nacosConfigDataSource instanceof BasicDataSource); +// BasicDataSource dataSource = BasicDataSource.class.cast(nacosConfigDataSource); +// Assert.assertEquals(1, dataSource.getInitialSize()); +// Assert.assertEquals(2, dataSource.getMaxActive()); +// Assert.assertEquals(3, dataSource.getMaxIdle()); +// Assert.assertEquals(4, dataSource.getMaxWait()); +// } +// +// @Test +// public void testJdbcTemplate() { +// Assert.assertEquals(jdbcTemplate.getDataSource(), nacosConfigDataSource); +// } +// +// @Test +// public void testPlatformTransactionManager() { +// Assert.assertTrue(platformTransactionManager instanceof DataSourceTransactionManager); +// DataSourceTransactionManager transactionManager = (DataSourceTransactionManager) platformTransactionManager; +// Assert.assertEquals(transactionManager.getDataSource(), nacosConfigDataSource); +// } +// +//} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/constant/ConstantsTest.java b/config/src/test/java/com/alibaba/nacos/config/server/constant/ConstantsTest.java new file mode 100644 index 000000000..c789f9c8f --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/constant/ConstantsTest.java @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.config.server.constant; + +import org.junit.Assert; +import org.junit.Test; + +import static com.alibaba.nacos.config.server.constant.Constants.CAPACITY_CONTROLLER_PATH; +import static com.alibaba.nacos.config.server.constant.Constants.COMMUNICATION_CONTROLLER_PATH; +import static com.alibaba.nacos.config.server.constant.Constants.CONFIG_CONTROLLER_PATH; +import static com.alibaba.nacos.config.server.constant.Constants.HEALTH_CONTROLLER_PATH; +import static com.alibaba.nacos.config.server.constant.Constants.HISTORY_CONTROLLER_PATH; +import static com.alibaba.nacos.config.server.constant.Constants.LISTENER_CONTROLLER_PATH; +import static com.alibaba.nacos.config.server.constant.Constants.NAMESPACE_CONTROLLER_PATH; + +/** + * {@link Constants} Test + * + * @author Mercy + * @since 0.2.2 + */ +public class ConstantsTest { + + @Test + public void testControllerPathsDefaultValues() { + + Assert.assertEquals("/v1/cs/capacity", CAPACITY_CONTROLLER_PATH); + Assert.assertEquals("/v1/cs/communication", COMMUNICATION_CONTROLLER_PATH); + Assert.assertEquals("/v1/cs/configs", CONFIG_CONTROLLER_PATH); + Assert.assertEquals("/v1/cs/health", HEALTH_CONTROLLER_PATH); + Assert.assertEquals("/v1/cs/history", HISTORY_CONTROLLER_PATH); + Assert.assertEquals("/v1/cs/listener", LISTENER_CONTROLLER_PATH); + Assert.assertEquals("/v1/cs/namespaces", NAMESPACE_CONTROLLER_PATH); + } +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/controller/HealthControllerUnitTest.java b/config/src/test/java/com/alibaba/nacos/config/server/controller/HealthControllerUnitTest.java index a31fb5310..c8cf6930b 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/controller/HealthControllerUnitTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/controller/HealthControllerUnitTest.java @@ -41,27 +41,27 @@ import com.alibaba.nacos.config.server.service.DataSourceService; @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class HealthControllerUnitTest { - - @InjectMocks - HealthController healthController; + @InjectMocks + HealthController healthController; - @Mock - DataSourceService dataSourceService; + @Mock + DataSourceService dataSourceService; + + private MockMvc mockmvc; - private MockMvc mockmvc; @Before public void setUp() throws Exception { - mockmvc = MockMvcBuilders.standaloneSetup(healthController).build(); + mockmvc = MockMvcBuilders.standaloneSetup(healthController).build(); } @Test - public void testGetHealth() throws Exception{ + public void testGetHealth() throws Exception { + + Mockito.when(dataSourceService.getHealth()).thenReturn("UP"); + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.HEALTH_CONTROLLER_PATH); + String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); + Assert.assertEquals("UP", actualValue); - Mockito.when(dataSourceService.getHealth()).thenReturn("UP"); - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.HEALTH_CONTROLLER_PATH); - String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); - Assert.assertEquals("UP", actualValue); - } } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/AggrWhitelistTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/AggrWhitelistTest.java index fa1420be5..6853998ab 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/AggrWhitelistTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/AggrWhitelistTest.java @@ -15,30 +15,28 @@ */ package com.alibaba.nacos.config.server.service; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; -import com.alibaba.nacos.config.server.service.AggrWhitelist; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class AggrWhitelistTest { AggrWhitelist service; - + @Before public void before() throws Exception { service = new AggrWhitelist(); } - + @Test public void testIsAggrDataId() { List list = new ArrayList(); @@ -46,12 +44,12 @@ public class AggrWhitelistTest { list.add("NS_NACOS_SUBSCRIPTION_TOPIC_*"); list.add("com.taobao.tae.AppListOnGrid-*"); service.compile(list); - + assertEquals(false, service.isAggrDataId("com.abc")); assertEquals(false, service.isAggrDataId("com.taobao.jiuren")); assertEquals(false, service.isAggrDataId("com.taobao.jiurenABC")); assertEquals(true, service.isAggrDataId("com.taobao.jiuren.abc")); assertEquals(true, service.isAggrDataId("NS_NACOS_SUBSCRIPTION_TOPIC_abc")); assertEquals(true, service.isAggrDataId("com.taobao.tae.AppListOnGrid-abc")); - } + } } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/ClientTrackServiceTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/ClientTrackServiceTest.java index e792df201..c5ec39dac 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/ClientTrackServiceTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/ClientTrackServiceTest.java @@ -15,19 +15,14 @@ */ package com.alibaba.nacos.config.server.service; +import com.alibaba.nacos.config.server.utils.GroupKey2; import org.junit.Assert; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; -import com.alibaba.nacos.config.server.service.ClientTrackService; -import com.alibaba.nacos.config.server.service.ConfigService; -import com.alibaba.nacos.config.server.utils.GroupKey2; - - @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class ClientTrackServiceTest { @@ -44,7 +39,7 @@ public class ClientTrackServiceTest { String group = "online"; String groupKey = GroupKey2.getKey(dataId, group); String md5 = "xxxxxxxxxxxxx"; - + ConfigService.updateMd5(groupKey, md5, System.currentTimeMillis()); ClientTrackService.trackClientMd5(clientIp, groupKey, md5); @@ -53,7 +48,7 @@ public class ClientTrackServiceTest { Assert.assertEquals(true, ClientTrackService.isClientUptodate(clientIp).get(groupKey)); Assert.assertEquals(1, ClientTrackService.subscribeClientCount()); Assert.assertEquals(1, ClientTrackService.subscriberCount()); - + //服务端数据更新 ConfigService.updateMd5(groupKey, md5 + "111", System.currentTimeMillis()); Assert.assertEquals(false, ClientTrackService.isClientUptodate(clientIp).get(groupKey)); diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/DiskServiceUnitTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/DiskServiceUnitTest.java index 93382ec8f..6863ed5e9 100755 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/DiskServiceUnitTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/DiskServiceUnitTest.java @@ -17,50 +17,46 @@ package com.alibaba.nacos.config.server.service; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; -import com.alibaba.nacos.config.server.service.DiskUtil; - import javax.servlet.ServletContext; - -import static org.junit.Assert.assertEquals; - import java.io.File; import java.io.IOException; +import static org.junit.Assert.assertEquals; + @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class DiskServiceUnitTest { - private DiskUtil diskService; + private DiskUtil diskService; - private ServletContext servletContext; + private ServletContext servletContext; - private File tempFile; + private File tempFile; - private String path; + private String path; - @Before - public void setUp() throws IOException { - this.tempFile = File.createTempFile("diskServiceTest", "tmp"); - this.path = tempFile.getParent(); - this.diskService = new DiskUtil(); - } + @Before + public void setUp() throws IOException { + this.tempFile = File.createTempFile("diskServiceTest", "tmp"); + this.path = tempFile.getParent(); + this.diskService = new DiskUtil(); + } - @Test - public void testCreateConfig() throws IOException { - diskService.saveToDisk("testDataId", "testGroup", "testTenant", "testContent"); - String content = diskService.getConfig("testDataId", "testGroup", "testTenant"); - assertEquals(content, "testContent"); + @Test + public void testCreateConfig() throws IOException { + diskService.saveToDisk("testDataId", "testGroup", "testTenant", "testContent"); + String content = diskService.getConfig("testDataId", "testGroup", "testTenant"); + assertEquals(content, "testContent"); - } + } - @After - public void tearDown() throws IOException { - tempFile.delete(); - } + @After + public void tearDown() throws IOException { + tempFile.delete(); + } } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java b/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java index 412d1074a..56077cc2e 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java @@ -50,7 +50,7 @@ public class GroupKeyTest { } catch (IllegalArgumentException e) { System.out.println(e.toString()); } - + key = "11111%2b+222"; try { GroupKey2.parseKey(key); @@ -58,7 +58,6 @@ public class GroupKeyTest { } catch (IllegalArgumentException e) { System.out.println(e.toString()); } - key = "11111%25+222"; String[] pair = GroupKey2.parseKey(key); diff --git a/config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLockTest.java b/config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLockTest.java index 9c15a2c72..591c2ba1d 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLockTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLockTest.java @@ -24,7 +24,6 @@ import org.springframework.test.context.web.WebAppConfiguration; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; - @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class SimpleReadWriteLockTest { @@ -34,44 +33,44 @@ public class SimpleReadWriteLockTest { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertEquals(true, lock.tryReadLock()); assertEquals(true, lock.tryReadLock()); - + lock.releaseReadLock(); lock.releaseReadLock(); - + assertEquals(true, lock.tryWriteLock()); } - + @Test public void test_加写锁() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertEquals(true, lock.tryWriteLock()); lock.releaseWriteLock(); } - + @Test public void test_双重写锁() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); - + assertEquals(true, lock.tryWriteLock()); assertEquals(false, lock.tryWriteLock()); } - + @Test public void test_先读锁后写锁() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); - + assertEquals(true, lock.tryReadLock()); assertEquals(false, lock.tryWriteLock()); } - + @Test public void test_双重读锁_释放一个_加写锁失败() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertEquals(true, lock.tryReadLock()); assertEquals(true, lock.tryReadLock()); - + lock.releaseReadLock(); - + assertEquals(false, lock.tryWriteLock()); } } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/utils/event/EventDispatcherTest.java b/config/src/test/java/com/alibaba/nacos/config/server/utils/event/EventDispatcherTest.java index c451e33fc..660f002e7 100755 --- a/config/src/test/java/com/alibaba/nacos/config/server/utils/event/EventDispatcherTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/utils/event/EventDispatcherTest.java @@ -32,7 +32,6 @@ import java.util.concurrent.CountDownLatch; import static org.junit.Assert.assertEquals; - @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration public class EventDispatcherTest { @@ -46,10 +45,10 @@ public class EventDispatcherTest { @Test public void testAddListener() throws Exception { final AbstractEventListener listener = new MockListener(); - + int vusers = 1000; final CountDownLatch latch = new CountDownLatch(vusers); - + for (int i = 0; i < vusers; ++i) { new Thread(new Runnable() { public void run() { @@ -58,33 +57,32 @@ public class EventDispatcherTest { } }).start(); } - + latch.await(); assertEquals(1, EventDispatcher.LISTENER_HUB.size()); } - + @Test public void testFireEvent() { EventDispatcher.fireEvent(new MockEvent()); assertEquals(0, MockListener.count); - + EventDispatcher.addEventListener(new MockListener()); - + EventDispatcher.fireEvent(new MockEvent()); assertEquals(1, MockListener.count); - + EventDispatcher.fireEvent(new MockEvent()); assertEquals(2, MockListener.count); } } - -class MockEvent implements Event { +class MockEvent implements Event { } class MockListener extends AbstractEventListener { static int count = 0; - + @Override public List> interest() { List> types = new ArrayList>(); diff --git a/console/pom.xml b/console/pom.xml index 3b96e6e77..b3451649a 100644 --- a/console/pom.xml +++ b/console/pom.xml @@ -12,37 +12,37 @@ limitations under the License. --> - 4.0.0 - - com.alibaba.nacos - nacos-all - 0.2.1 - - nacos-console - - jar - nacos-console ${project.version} - http://maven.apache.org - - UTF-8 - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + com.alibaba.nacos + nacos-all + 0.7.0 + + nacos-console + + jar + nacos-console ${project.version} + http://maven.apache.org + + UTF-8 + + - - ${project.groupId} - nacos-config - - - org.apache.tomcat.embed - tomcat-embed-jasper - 7.0.59 - - - ${project.groupId} - nacos-naming - + + ${project.groupId} + nacos-config + + + org.apache.tomcat.embed + tomcat-embed-jasper + 7.0.59 + + + ${project.groupId} + nacos-naming + @@ -59,38 +59,30 @@ org.slf4j jul-to-slf4j - - - - nacos-server - - - org.springframework.boot - spring-boot-maven-plugin - - com.alibaba.nacos.Nacos - - - - - repackage - - - - - - - - true - src/main/resources - - application.properties - banner.txt - diamond-server-logback.xml - schema.sql - - - - + + org.mockito + mockito-core + test + + + + nacos-server + + + org.springframework.boot + spring-boot-maven-plugin + + com.alibaba.nacos.Nacos + + + + + repackage + + + + + + diff --git a/console/src/main/java/com/alibaba/nacos/Nacos.java b/console/src/main/java/com/alibaba/nacos/Nacos.java index 4142ed54c..6dc4be5b7 100644 --- a/console/src/main/java/com/alibaba/nacos/Nacos.java +++ b/console/src/main/java/com/alibaba/nacos/Nacos.java @@ -16,7 +16,6 @@ package com.alibaba.nacos; -import java.net.UnknownHostException; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @@ -28,7 +27,7 @@ import org.springframework.boot.web.servlet.ServletComponentScan; @ServletComponentScan public class Nacos { - public static void main(String[] args) throws UnknownHostException { + public static void main(String[] args) { SpringApplication.run(Nacos.class, args); } -} +} \ No newline at end of file diff --git a/console/src/main/java/com/alibaba/nacos/console/controller/HealthController.java b/console/src/main/java/com/alibaba/nacos/console/controller/HealthController.java new file mode 100644 index 000000000..0e59b188d --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/controller/HealthController.java @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.console.controller; + +import com.alibaba.nacos.config.server.service.PersistService; +import com.alibaba.nacos.naming.web.ApiCommands; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author hxy1991 + */ +@RestController("consoleHealth") +@RequestMapping("/v1/console/health") +public class HealthController { + + private static final Logger logger = LoggerFactory.getLogger(HealthController.class); + + private final PersistService persistService; + private final ApiCommands apiCommands; + + @Autowired + public HealthController(PersistService persistService, ApiCommands apiCommands) { + this.persistService = persistService; + this.apiCommands = apiCommands; + } + + /** + * Whether the Nacos is in broken states or not, and cannot recover except by being restarted + * + * @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that + * Nacos is in broken states. + */ + @ResponseBody + @RequestMapping(value = "liveness", method = RequestMethod.GET) + public ResponseEntity liveness() { + return ResponseEntity.ok().body("OK"); + } + + /** + * Ready to receive the request or not + * + * @return HTTP code equal to 200 indicates that Nacos is ready. HTTP code equal to 500 indicates that Nacos is not + * ready. + */ + @ResponseBody + @RequestMapping(value = "readiness", method = RequestMethod.GET) + public ResponseEntity readiness(HttpServletRequest request) { + boolean isConfigReadiness = isConfigReadiness(); + boolean isNamingReadiness = isNamingReadiness(request); + + if (isConfigReadiness && isNamingReadiness) { + return ResponseEntity.ok().body("OK"); + } + + if (!isConfigReadiness && !isNamingReadiness) { + return ResponseEntity.status(500).body("Config and Naming are not in readiness"); + } + + if (!isConfigReadiness) { + return ResponseEntity.status(500).body("Config is not in readiness"); + } + + return ResponseEntity.status(500).body("Naming is not in readiness"); + } + + private boolean isConfigReadiness() { + // check db + try { + persistService.configInfoCount(""); + return true; + } catch (Exception e) { + logger.error("Config health check fail.", e); + } + return false; + } + + private boolean isNamingReadiness(HttpServletRequest request) { + try { + apiCommands.hello(request); + return true; + } catch (Exception e) { + logger.error("Naming health check fail.", e); + } + return false; + } +} diff --git a/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java b/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java new file mode 100644 index 000000000..64a19d329 --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java @@ -0,0 +1,168 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.console.controller; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import com.alibaba.nacos.config.server.exception.NacosException; +import com.alibaba.nacos.config.server.model.RestResult; +import com.alibaba.nacos.config.server.model.TenantInfo; +import com.alibaba.nacos.config.server.service.PersistService; +import com.alibaba.nacos.config.server.utils.StringUtils; +import com.alibaba.nacos.console.model.Namespace; +import com.alibaba.nacos.console.model.NamespaceAllInfo; + +/** + * namespace service + * + * @author Nacos + */ +@Controller +@RequestMapping("/v1/console/namespaces") +public class NamespaceController { + + @Autowired + private transient PersistService persistService; + + /** + * Get namespace list + * + * @param request request + * @param response response + * @return namespace list + */ + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public RestResult> getNamespaces(HttpServletRequest request, HttpServletResponse response) { + RestResult> rr = new RestResult>(); + rr.setCode(200); + // TODO 获取用kp + List tenantInfos = persistService.findTenantByKp("1"); + Namespace namespace0 = new Namespace("", "Public", 200, persistService.configInfoCount(""), 0); + List namespaces = new ArrayList(); + namespaces.add(namespace0); + for (TenantInfo tenantInfo : tenantInfos) { + int configCount = persistService.configInfoCount(tenantInfo.getTenantId()); + Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(), 200, + configCount, 2); + namespaces.add(namespaceTmp); + } + rr.setData(namespaces); + return rr; + } + + /** + * get namespace all info by namespace id + * + * @param request request + * @param response response + * @param namespaceId namespaceId + * @return namespace all info + */ + @ResponseBody + @RequestMapping(params = "show=all", method = RequestMethod.GET) + public NamespaceAllInfo getNamespace(HttpServletRequest request, HttpServletResponse response, + @RequestParam("namespaceId") String namespaceId) { + // TODO 获取用kp + if (StringUtils.isBlank(namespaceId)) { + int configCount = persistService.configInfoCount(); + NamespaceAllInfo namespaceTmp = new NamespaceAllInfo(namespaceId, "Public", 200, configCount, 0, + "Public Namespace"); + return namespaceTmp; + } else { + TenantInfo tenantInfo = persistService.findTenantByKp("1", namespaceId); + int configCount = persistService.configInfoCount(namespaceId); + NamespaceAllInfo namespaceTmp = new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), 200, + configCount, 2, tenantInfo.getTenantDesc()); + return namespaceTmp; + } + } + + /** + * create namespace + * + * @param request request + * @param response response + * @param namespaceName namespace Name + * @param namespaceDesc namespace Desc + * @return whether create ok + * @throws NacosException + */ + @RequestMapping(method = RequestMethod.POST) + @ResponseBody + public Boolean createNamespace(HttpServletRequest request, HttpServletResponse response, + @RequestParam("namespaceName") String namespaceName, + @RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) + throws NacosException { + // TODO 获取用kp + String namespaceId = UUID.randomUUID().toString(); + persistService.insertTenantInfoAtomic("1", namespaceId, namespaceName, namespaceDesc, "nacos", + System.currentTimeMillis()); + return true; + } + + /** + * edit namespace + * + * @param request request + * @param response response + * @param namespace namespace + * @param namespaceShowName namespace ShowName + * @param namespaceDesc namespace Desc + * @return whether edit ok + * @throws NacosException NacosException + */ + @RequestMapping(method = RequestMethod.PUT) + @ResponseBody + public Boolean editNamespace(HttpServletRequest request, HttpServletResponse response, + @RequestParam("namespace") String namespace, + @RequestParam("namespaceShowName") String namespaceShowName, + @RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) + throws NacosException { + // TODO 获取用kp + persistService.updateTenantNameAtomic("1", namespace, namespaceShowName, namespaceDesc); + return true; + } + + /** + * del namespace by id + * + * @param request request + * @param response response + * @param namespaceId namespace Id + * @return whether del ok + * @throws NacosException NacosException + */ + @RequestMapping(method = RequestMethod.DELETE) + @ResponseBody + public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam("namespaceId") String namespaceId) throws NacosException { + persistService.removeTenantInfoAtomic("1", namespaceId); + return true; + } + +} diff --git a/console/src/main/java/com/alibaba/nacos/console/model/Namespace.java b/console/src/main/java/com/alibaba/nacos/console/model/Namespace.java new file mode 100644 index 000000000..c617f10c0 --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/model/Namespace.java @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.console.model; + +/** + * Namespace + * + * @author diamond + */ +public class Namespace { + + private String namespace; + + private String namespaceShowName; + + private int quota; + + private int configCount; + /** + * 0 : Global configuration, 1 : Default private namespace ,2 : Custom namespace + */ + private int type; + + public String getNamespaceShowName() { + return namespaceShowName; + } + + public void setNamespaceShowName(String namespaceShowName) { + this.namespaceShowName = namespaceShowName; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public Namespace() { + } + + public Namespace(String namespace, String namespaceShowName) { + this.namespace = namespace; + this.namespaceShowName = namespaceShowName; + } + + public Namespace(String namespace, String namespaceShowName, int quota, int configCount, int type) { + this.namespace = namespace; + this.namespaceShowName = namespaceShowName; + this.quota = quota; + this.configCount = configCount; + this.type = type; + } + + public int getQuota() { + return quota; + } + + public void setQuota(int quota) { + this.quota = quota; + } + + public int getConfigCount() { + return configCount; + } + + public void setConfigCount(int configCount) { + this.configCount = configCount; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + +} diff --git a/console/src/main/java/com/alibaba/nacos/console/model/NamespaceAllInfo.java b/console/src/main/java/com/alibaba/nacos/console/model/NamespaceAllInfo.java new file mode 100644 index 000000000..ebd36bdf0 --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/model/NamespaceAllInfo.java @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.nacos.console.model; + +/** + * all namespace info + * + * @author Nacos + */ +public class NamespaceAllInfo extends Namespace { + + private String namespaceDesc; + + public String getNamespaceDesc() { + return namespaceDesc; + } + + public void setNamespaceDesc(String namespaceDesc) { + this.namespaceDesc = namespaceDesc; + } + + public NamespaceAllInfo() { + } + + ; + + public NamespaceAllInfo(String namespace, String namespaceShowName, int quota, int configCount, int type, + String namespaceDesc) { + super(namespace, namespaceShowName, quota, configCount, type); + this.namespaceDesc = namespaceDesc; + } + +} diff --git a/console/src/main/resources/application.properties b/console/src/main/resources/META-INF/nacos-default.properties similarity index 88% rename from console/src/main/resources/application.properties rename to console/src/main/resources/META-INF/nacos-default.properties index 34a749946..019d315ac 100644 --- a/console/src/main/resources/application.properties +++ b/console/src/main/resources/META-INF/nacos-default.properties @@ -1,8 +1,5 @@ -# spring -management.security.enabled=false -server.contextPath=/nacos -server.servlet.contextPath=/nacos -server.port=8848 +# Console Default Properties + spring.mvc.view.prefix=/jsp/ # 响应页面默认后缀 spring.mvc.view.suffix=.jsp @@ -33,6 +30,10 @@ maxHealthCheckFailCount=12 # whether open spas; true:open; false:close OPEN_SPAS=true +nacos.cmdb.dumpTaskInterval=3600 +nacos.cmdb.eventTaskInterval=10 +nacos.cmdb.labelTaskInterval=300 +nacos.cmdb.loadDataAtStart=false db.num=2 db.url.0=jdbc:mysql://11.162.196.161:3306/diamond_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true diff --git a/console/src/main/resources/schema.sql b/console/src/main/resources/META-INF/schema.sql similarity index 92% rename from console/src/main/resources/schema.sql rename to console/src/main/resources/META-INF/schema.sql index ae4ae291b..d766aeab3 100644 --- a/console/src/main/resources/schema.sql +++ b/console/src/main/resources/META-INF/schema.sql @@ -159,3 +159,15 @@ CREATE TABLE tenant_capacity ( constraint tenant_capacity_id_key PRIMARY KEY (id), constraint uk_tenant_id UNIQUE (tenant_id)); +CREATE TABLE tenant_info ( + id bigint NOT NULL generated by default as identity, + kp varchar(128) NOT NULL, + tenant_id varchar(128) DEFAULT '', + tenant_name varchar(128) DEFAULT '', + tenant_desc varchar(256) DEFAULT NULL, + create_source varchar(32) DEFAULT NULL, + gmt_create bigint NOT NULL, + gmt_modified bigint NOT NULL, + constraint tenant_info_id_key PRIMARY KEY (id), + constraint uk_tenant_info_kptenantid UNIQUE (kp,tenant_id)); +CREATE INDEX tenant_info_tenant_id_idx ON tenant_info(tenant_id); diff --git a/console/src/main/resources/banner.txt b/console/src/main/resources/banner.txt deleted file mode 100644 index 4ebabe6fa..000000000 --- a/console/src/main/resources/banner.txt +++ /dev/null @@ -1,15 +0,0 @@ - - ,--. - ,--.'| - ,--,: : | -,`--.'`| ' : ,---. -| : : | | ' ,'\ .--.--. -: | \ | : ,--.--. ,---. / / | / / ' -| : ' '; | / \ / \. ; ,. :| : /`./ -' ' ;. ;.--. .-. | / / '' | |: :| : ;_ -| | | \ | \__\/: . .. ' / ' | .; : \ \ `. -' : | ; .' ," .--.; |' ; :__| : | `----. \ -| | '`--' / / ,. |' | '.'|\ \ / / /`--' / -' : | ; : .' \ : : `----' '--'. / -; |.' | , .-./\ \ / `--'---' -'---' `--`---' `----' diff --git a/console/src/main/resources/diamond-server-logback.xml b/console/src/main/resources/diamond-server-logback.xml index 060b7fb2a..3e356b30c 100755 --- a/console/src/main/resources/diamond-server-logback.xml +++ b/console/src/main/resources/diamond-server-logback.xml @@ -1,204 +1,208 @@ - + + + - ${user.home}/diamond/logs/dump.log + ${LOG_HOME}/dump.log true - ${user.home}/diamond/logs/dump.log.%d{yyyy-MM-dd}.%i - 2GB + ${LOG_HOME}/dump.log.%d{yyyy-MM-dd}.%i + 2GB 15 7GB true %date %level %msg%n%n - GBK + UTF-8 + - ${user.home}/diamond/logs/pull.log + ${LOG_HOME}/pull.log true - ${user.home}/diamond/logs/pull.log.%d{yyyy-MM-dd}.%i - 20MB + ${LOG_HOME}/pull.log.%d{yyyy-MM-dd}.%i + 20MB 15 128MB true %date %level %msg%n%n - GBK + UTF-8 + - ${user.home}/diamond/logs/fatal.log + ${LOG_HOME}/fatal.log true - ${user.home}/diamond/logs/fatal.log.%d{yyyy-MM-dd}.%i - 20MB + ${LOG_HOME}/fatal.log.%d{yyyy-MM-dd}.%i + 20MB 15 128MB true %date %level %msg%n%n - GBK + UTF-8 - ${user.home}/diamond/logs/memory.log + ${LOG_HOME}/memory.log true - ${user.home}/diamond/logs/memory.log.%d{yyyy-MM-dd}.%i - 20MB + ${LOG_HOME}/memory.log.%d{yyyy-MM-dd}.%i + 20MB 15 128MB true %date %level %msg%n%n - GBK + UTF-8 - ${user.home}/diamond/logs/pull-check.log + ${LOG_HOME}/pull-check.log true - ${user.home}/diamond/logs/pull-check.log.%d{yyyy-MM-dd}.%i - 1GB + ${LOG_HOME}/pull-check.log.%d{yyyy-MM-dd}.%i + 1GB 15 3GB true %msg%n - GBK + UTF-8 - ${user.home}/diamond/logs/acl.log + ${LOG_HOME}/acl.log true - ${user.home}/diamond/logs/acl.log.%d{yyyy-MM-dd}.%i - 50MB + ${LOG_HOME}/acl.log.%d{yyyy-MM-dd}.%i + 50MB 15 512MB true %date %level %msg%n%n - GBK + UTF-8 - ${user.home}/diamond/logs/client-request.log + ${LOG_HOME}/client-request.log true - ${user.home}/diamond/logs/client-request.log.%d{yyyy-MM-dd}.%i - 2GB + ${LOG_HOME}/client-request.log.%d{yyyy-MM-dd}.%i + 2GB 15 7GB true %date|%msg%n - GBK + UTF-8 - ${user.home}/diamond/logs/sdk-request.log + ${LOG_HOME}/sdk-request.log true - ${user.home}/diamond/logs/sdk-request.log.%d{yyyy-MM-dd}.%i - 1GB + ${LOG_HOME}/sdk-request.log.%d{yyyy-MM-dd}.%i + 1GB 15 3GB true %date|%msg%n - GBK + UTF-8 - ${user.home}/diamond/logs/trace.log + ${LOG_HOME}/trace.log true - ${user.home}/diamond/logs/trace.log.%d{yyyy-MM-dd}.%i - 2GB + ${LOG_HOME}/trace.log.%d{yyyy-MM-dd}.%i + 2GB 15 7GB true %date|%msg%n - GBK + UTF-8 - ${user.home}/diamond/logs/notify.log + ${LOG_HOME}/notify.log true - ${user.home}/diamond/logs/notify.log.%d{yyyy-MM-dd}.%i - 1GB + ${LOG_HOME}/notify.log.%d{yyyy-MM-dd}.%i + 1GB 15 3GB true %date %level %msg%n%n - GBK + UTF-8 - ${user.home}/diamond/logs/app.log + ${LOG_HOME}/app.log true - ${user.home}/diamond/logs/app.log.%d{yyyy-MM-dd}.%i - 20MB + ${LOG_HOME}/app.log.%d{yyyy-MM-dd}.%i + 20MB 15 128MB true %date %level %msg%n%n - GBK + UTF-8 - + - ${user.home}/diamond/logs/diamondServer.log + ${LOG_HOME}/diamondServer.log true - ${user.home}/diamond/logs/diamondServer.log.%d{yyyy-MM-dd}.%i - 50MB + ${LOG_HOME}/diamondServer.log.%d{yyyy-MM-dd}.%i + 50MB 15 512MB true %date %level %msg%n%n - GBK + UTF-8 @@ -252,7 +256,7 @@ - + diff --git a/console/src/main/resources/static/console-fe/.babelrc b/console/src/main/resources/static/console-fe/.babelrc new file mode 100644 index 000000000..0bad3f503 --- /dev/null +++ b/console/src/main/resources/static/console-fe/.babelrc @@ -0,0 +1,15 @@ +{ + "presets": [ + "react-app" + ], + "plugins": [ + "transform-decorators-legacy", + [ + "babel-plugin-import", + { + "libraryName": "@alifd/next", + "style": true + } + ] + ] +} diff --git a/console/src/main/resources/static/console-fe/.editorconfig b/console/src/main/resources/static/console-fe/.editorconfig new file mode 100644 index 000000000..3331d7049 --- /dev/null +++ b/console/src/main/resources/static/console-fe/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/.eslintignore b/console/src/main/resources/static/console-fe/.eslintignore new file mode 100644 index 000000000..2a606840e --- /dev/null +++ b/console/src/main/resources/static/console-fe/.eslintignore @@ -0,0 +1,6 @@ +*.svg +*.ejs +.DS_Store +build +node_modules +public \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/.eslintrc b/console/src/main/resources/static/console-fe/.eslintrc new file mode 100644 index 000000000..1f3d55b5e --- /dev/null +++ b/console/src/main/resources/static/console-fe/.eslintrc @@ -0,0 +1,34 @@ +{ + "extends": "eslint-config-ali/react", + "parser": "babel-eslint", + "env": {}, + "globals": { + "window": true + }, + "rules": { + "no-shadow": "off", + "no-empty": "off", + "no-useless-escape": "off", + "no-template-curly-in-string": "off", + "no-unused-vars": "off", + "no-tabs": "off", + "react/no-string-refs": "off", + "react/no-unused-state": "off", + "no-return-assign": "off", + "no-plusplus": "off", + "no-script-url": "off", + "no-mixed-operators": "off", + "react/jsx-indent": "off", + "react/jsx-no-bind": "off", + "react/forbid-prop-types": "off", + "react/no-array-index-key": "off", + "react/sort-comp": "off", + "implicit-arrow-linebreak": "off", + "prefer-const": "off", + "space-before-function-paren": "off", + "generator-star-spacing": "off", + "wrap-iife": "off", + "arrow-parens": "off", + "indent": "off", + } +} diff --git a/console/src/main/resources/static/console-fe/.gitignore b/console/src/main/resources/static/console-fe/.gitignore new file mode 100644 index 000000000..aa6e4c4e6 --- /dev/null +++ b/console/src/main/resources/static/console-fe/.gitignore @@ -0,0 +1,11 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# production +/dist + +# misc +.DS_Store +npm-debug.log* diff --git a/console/src/main/resources/static/console-fe/.prettierignore b/console/src/main/resources/static/console-fe/.prettierignore new file mode 100644 index 000000000..2a606840e --- /dev/null +++ b/console/src/main/resources/static/console-fe/.prettierignore @@ -0,0 +1,6 @@ +*.svg +*.ejs +.DS_Store +build +node_modules +public \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/.prettierrc b/console/src/main/resources/static/console-fe/.prettierrc new file mode 100644 index 000000000..0f6921f61 --- /dev/null +++ b/console/src/main/resources/static/console-fe/.prettierrc @@ -0,0 +1,9 @@ +{ + "tabWidth": 2, + "printWidth": 100, + "semi": true, + "useTabs": false, + "bracketSpacing": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/console/src/main/resources/static/console-fe/.vscode/settings.json b/console/src/main/resources/static/console-fe/.vscode/settings.json new file mode 100644 index 000000000..6779945fa --- /dev/null +++ b/console/src/main/resources/static/console-fe/.vscode/settings.json @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +{ + "editor.tabSize": 2, + "editor.formatOnSave": true, + "[javascript]": { + "editor.formatOnSave": true, + "editor.formatOnPaste": false + }, + "[javascriptreact]": { + "editor.formatOnSave": true, + "editor.formatOnPaste": false + }, + "[typescript]": { + "editor.formatOnSave": true, + "editor.formatOnPaste": false + }, + "[typescriptreact]": { + "editor.formatOnSave": true, + "editor.formatOnPaste": false + }, + "[html]": { + "editor.formatOnSave": false + } +} diff --git a/console/src/main/resources/static/console-fe/README.md b/console/src/main/resources/static/console-fe/README.md new file mode 100644 index 000000000..4fda681bc --- /dev/null +++ b/console/src/main/resources/static/console-fe/README.md @@ -0,0 +1,60 @@ +# 开始项目 +## cnpm 安装(可忽略) +```sh +npm install -g cnpm --registry=https://registry.npm.taobao.org + +# 设置匿名 +alias cnpm="npm --registry=https://registry.npm.taobao.org \ +--cache=$HOME/.npm/.cache/cnpm \ +--disturl=https://npm.taobao.org/dist \ +--userconfig=$HOME/.cnpmrc" + +# Or alias it in .bashrc or .zshrc +$ echo '\n#alias for cnpm\nalias cnpm="npm --registry=https://registry.npm.taobao.org \ + --cache=$HOME/.npm/.cache/cnpm \ + --disturl=https://npm.taobao.org/dist \ + --userconfig=$HOME/.cnpmrc"' >> ~/.zshrc && source ~/.zshrc + +``` +[详情地址: http://npm.taobao.org/](http://npm.taobao.org/) + +## 安装依赖 +```sh +yarn +``` +或 +``` +cnpm install +``` + +## 启动 +```sh +yarn start +``` +或 +``` +npm start +``` + +## 构建打包 +```sh +yarn build +``` +或 +``` +npm run build +``` +## + +# 代理配置 +`build/webpack.dev.conf.js` +修改proxy属性 + +``` +proxy: [{ + context: ['/'], + changeOrigin: true, + secure: false, + target: 'http://ip:port', +}], +``` diff --git a/console/src/main/resources/static/console-fe/build/copy-dist.js b/console/src/main/resources/static/console-fe/build/copy-dist.js new file mode 100644 index 000000000..daf957ef0 --- /dev/null +++ b/console/src/main/resources/static/console-fe/build/copy-dist.js @@ -0,0 +1,79 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +const path = require('path'); +const fs = require('fs'); + +const styles = { + 'red': ['\x1B[31m', '\x1B[39m'], + 'green': ['\x1B[32m', '\x1B[39m'], + 'yellow': ['\x1B[33m', '\x1B[39m'], +}; + +const distPath = path.join(__dirname, '../dist/'); +const rootPath = path.join(__dirname, '../../'); + +console.log('\n\n> Start copying the dist directory...\n'); + +function delDir(dest) { + let paths = fs.readdirSync(dest); + paths.forEach(function(p) { + const target = path.join(dest, p); + const st = fs.statSync(target); + if (st.isFile()) { + console.log(`\r${styles.red[0]}Delete File${styles.red[1]}: ${target}`); + fs.unlinkSync(target); + } + if (st.isDirectory()) { + console.log(`\r${styles.red[0]}Delete Directory${styles.red[1]}: ${target}`); + delDir(target); + } + }); + paths = fs.readdirSync(dest); + if (!paths.length) { + fs.rmdirSync(dest); + } +} + +function copyDir(source, dest) { + const paths = fs.readdirSync(source); + paths.forEach(function(p) { + const src = path.join(source, p); + const target = path.join(dest, p); + const st = fs.statSync(src); + if (st.isFile()) { + if (fs.existsSync(target)) { + console.log(`\r${styles.red[0]}Delete File${styles.red[1]}: ${target}`); + fs.unlinkSync(target); + } + console.log(`\r${styles.yellow[0]}Copy File${styles.yellow[1]}: ${target}`); + const readStream = fs.createReadStream(src); + const writeStream = fs.createWriteStream(target); + readStream.pipe(writeStream); + } + if (st.isDirectory()) { + if (fs.existsSync(target)) { + console.log(`\r${styles.red[0]}Delete Directory${styles.red[1]}: ${target}`); + delDir(target); + } + console.log(`\r${styles.yellow[0]}Create Directory${styles.yellow[1]}: ${target}`); + fs.mkdirSync(target); + copyDir(src, target); + } + }); +} + + +copyDir(distPath, rootPath); + +console.log(`\n>${styles.green[0]} Copy complete!${styles.green[0]}\n`); diff --git a/console/src/main/resources/static/console-fe/build/copyFile.js b/console/src/main/resources/static/console-fe/build/copyFile.js new file mode 100644 index 000000000..e078ed5aa --- /dev/null +++ b/console/src/main/resources/static/console-fe/build/copyFile.js @@ -0,0 +1,42 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +const fs = require('fs'); +const path = require('path'); +// 默认打包存放地址 +const srcDir = path.join(__dirname, '../dist'); +// 打包后文件存放地址 +const destDir = path.join(__dirname, '../../'); + +const mkdir = dir => { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + }; + +const copyList = ['js/main.js', 'css/main.css']; + +copyList.forEach(_fileName => { + const srcFileName = path.join(srcDir, _fileName); + const destFileName = path.join(destDir, _fileName); + + if (!fs.existsSync(srcFileName)) { + return; + } + + mkdir(path.dirname(destFileName)); + + const readStream = fs.createReadStream(srcFileName); + const writeStream = fs.createWriteStream(destFileName); + readStream.pipe(writeStream); +}); \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/build/webpack.base.conf.js b/console/src/main/resources/static/console-fe/build/webpack.base.conf.js new file mode 100644 index 000000000..60e5eb6cf --- /dev/null +++ b/console/src/main/resources/static/console-fe/build/webpack.base.conf.js @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +const isDev = process.env.NODE_ENV !== 'production'; + +function resolve(dir) { + return path.join(__dirname, '..', dir); +} + +module.exports = { + entry: { + main: './src/index.js', + }, + output: { + filename: './js/[name].js', + path: path.resolve(__dirname, '../dist'), + }, + resolve: { + extensions: ['.js', '.jsx', '.json'], + alias: { + '@': resolve('src'), + utils: resolve('src/utils'), + components: resolve('src/components'), + }, + }, + module: { + rules: [ + { + test: /\.(css|scss)$/, + use: [isDev ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], + }, + { + test: /\.(js|jsx)$/, + loader: 'eslint-loader', + enforce: 'pre', + include: [resolve('src')], + }, + { + test: /\.(js|jsx)$/, + include: [resolve('src')], + use: ['babel-loader'], + }, + { + test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], + loader: 'url-loader', + options: { + limit: 10000, + name: '/img/[name].[hash:8].[ext]', + }, + }, + { + test: /\.(ttf|woff|svg)$/, + use: [ + { + loader: 'url-loader', + options: { + name: '/fonts/[name].[hash:8].[ext]', + }, + }, + ], + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + filename: 'index.html', + template: './public/index.html', + minify: !isDev, + }), + new CopyWebpackPlugin([ + { + from: resolve('public'), + to: './', + ignore: ['index.html'], + }, + ]), + ], +}; diff --git a/console/src/main/resources/static/console-fe/build/webpack.dev.conf.js b/console/src/main/resources/static/console-fe/build/webpack.dev.conf.js new file mode 100644 index 000000000..8a5275fc7 --- /dev/null +++ b/console/src/main/resources/static/console-fe/build/webpack.dev.conf.js @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +const path = require('path'); +const webpack = require('webpack'); +const base = require('./webpack.base.conf'); + +module.exports = Object.assign({}, base, { + output: { + filename: './js/[name].js', + path: path.resolve(__dirname, '../dist'), + }, + devServer: { + port: process.env.PORT || 8000, + proxy: [{ + context: ['/'], + changeOrigin: true, + secure: false, + target: 'http://console.nacos.io', + pathRewrite: {'^/v1' : '/nacos/v1'} + }], + disableHostCheck: true, + open: true, + hot: true, + overlay: true + }, + mode: 'development', + devtool: 'eval-source-map', + plugins: [ + ...base.plugins, + new webpack.HotModuleReplacementPlugin() + ] +}); diff --git a/console/src/main/resources/static/console-fe/build/webpack.prod.conf.js b/console/src/main/resources/static/console-fe/build/webpack.prod.conf.js new file mode 100644 index 000000000..698f92b67 --- /dev/null +++ b/console/src/main/resources/static/console-fe/build/webpack.prod.conf.js @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +const path = require('path'); +const base = require('./webpack.base.conf'); +const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); + +module.exports = Object.assign({}, base, { + optimization: { + minimizer: [ + new UglifyJsPlugin({ + cache: true, + parallel: true, + sourceMap: true, + }), + new OptimizeCSSAssetsPlugin({}), + ], + }, + plugins: [ + new CleanWebpackPlugin(path.resolve(__dirname, '../dist'), { + root: path.resolve(__dirname, '../'), + }), + ...base.plugins, + new MiniCssExtractPlugin({ + filename: './css/[name].css', + chunkFilename: '[id].css', + }), + ], + mode: 'production', +}); diff --git a/console/src/main/resources/static/console-fe/package.json b/console/src/main/resources/static/console-fe/package.json new file mode 100644 index 000000000..382d6c1d8 --- /dev/null +++ b/console/src/main/resources/static/console-fe/package.json @@ -0,0 +1,82 @@ +{ + "name": "console-fe", + "version": "1.0.0", + "description": "console fe", + "main": "index.js", + "scripts": { + "start": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.conf.js", + "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js && node build/copyFile.js", + "eslint": "eslint --ext .js src/", + "eslint-fix": "eslint --ext .js --fix src/" + }, + "private": true, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{js,css,less}": [ + "prettier --write", + "git add" + ] + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/alibaba/nacos.git" + }, + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-core": "^6.26.3", + "babel-eslint": "^10.0.1", + "babel-loader": "^7.1.5", + "babel-plugin-import": "^1.10.0", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-plugin-transform-decorators-legacy": "^1.3.5", + "babel-preset-env": "^1.7.0", + "babel-preset-react-app": "^3.1.1", + "babel-runtime": "^6.23.0", + "clean-webpack-plugin": "^0.1.19", + "copy-webpack-plugin": "^4.6.0", + "cross-env": "^5.2.0", + "css-loader": "^1.0.0", + "eslint": "^5.9.0", + "eslint-config-ali": "^4.0.0", + "eslint-config-prettier": "^3.3.0", + "eslint-loader": "^2.1.1", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-prettier": "^3.0.0", + "eslint-plugin-react": "^7.11.1", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "husky": "^1.1.4", + "lint-staged": "^8.0.4", + "mini-css-extract-plugin": "^0.4.3", + "node-sass": "^4.1.0", + "optimize-css-assets-webpack-plugin": "^5.0.1", + "prettier": "1.15.2", + "sass-loader": "^7.1.0", + "style-loader": "^0.23.0", + "uglifyjs-webpack-plugin": "^2.0.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "@alifd/next": "^1.9.19", + "axios": "^0.18.0", + "jquery": "^3.3.1", + "moment": "^2.22.2", + "prop-types": "^15.6.2", + "react": "^16.6.0", + "react-dom": "^16.6.0", + "react-redux": "^5.1.0", + "react-router": "^4.3.1", + "react-router-dom": "^4.3.1", + "react-router-redux": "^4.0.8", + "redux": "^4.0.1", + "redux-thunk": "^2.3.0" + } +} diff --git a/console/src/main/resources/static/console-fe/public/css/bootstrap.css b/console/src/main/resources/static/console-fe/public/css/bootstrap.css new file mode 100644 index 000000000..3bcd6346c --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/css/bootstrap.css @@ -0,0 +1,7125 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +/*! normalize.css v2.1.3 | MIT License | git.io/normalize */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden], +template { + display: none; +} + +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; +} + +a { + background: transparent; +} + +a:focus { + outline: thin dotted; +} + +a:active, +a:hover { + outline: 0; +} + +h1 { + margin: 0.67em 0; + font-size: 2em; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +b, +strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +hr { + height: 0; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +mark { + color: #000; + background: #ff0; +} + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +pre { + white-space: pre-wrap; +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + border: 0; +} + +svg:not(:root) { + overflow: hidden; +} + +figure { + margin: 0; +} + +fieldset { + padding: 0.35em 0.625em 0.75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} + +legend { + padding: 0; + border: 0; +} + +button, +input, +select, +textarea { + margin: 0; + font-family: inherit; + font-size: 100%; +} + +button, +input { + line-height: normal; +} + +button, +select { + text-transform: none; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +button[disabled], +html input[disabled] { + cursor: default; +} + +input[type="checkbox"], +input[type="radio"] { + padding: 0; + box-sizing: border-box; +} + +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; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +@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; + } + @page { + margin: 2cm .5cm; + } + 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; + } +} + +*, +*: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.428571429; + color: #333333; + background-color: #ffffff; +} + +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; +} + +img { + vertical-align: middle; +} + +.img-responsive { + display: block; + height: auto; + max-width: 100%; +} + +.img-rounded { + border-radius: 6px; +} + +.img-thumbnail { + display: inline-block; + height: auto; + max-width: 100%; + padding: 4px; + line-height: 1.428571429; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.img-circle { + border-radius: 50%; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} + +.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: "Helvetica Neue", Helvetica, Arial, sans-serif; + 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: #999999; +} + +h1, +h2, +h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +h1 small, +h2 small, +h3 small, +h1 .small, +h2 .small, +h3 .small { + font-size: 65%; +} + +h4, +h5, +h6 { + margin-top: 10px; + margin-bottom: 10px; +} + +h4 small, +h5 small, +h6 small, +h4 .small, +h5 .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-muted { + color: #999999; +} + +.text-primary { + color: #428bca; +} + +.text-primary:hover { + color: #3071a9; +} + +.text-warning { + color: #8a6d3b; +} + +.text-warning:hover { + color: #66512c; +} + +.text-danger { + color: #a94442; +} + +.text-danger:hover { + color: #843534; +} + +.text-success { + color: #3c763d; +} + +.text-success:hover { + color: #2b542c; +} + +.text-info { + color: #31708f; +} + +.text-info:hover { + color: #245269; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eeeeee; +} + +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; +} + +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +.list-inline > li:first-child { + padding-left: 0; +} + +dl { + margin-top: 0; + margin-bottom: 20px; +} + +dt, +dd { + line-height: 1.428571429; +} + +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; + } + .dl-horizontal dd:before, + .dl-horizontal dd:after { + display: table; + content: " "; + } + .dl-horizontal dd:after { + clear: both; + } + .dl-horizontal dd:before, + .dl-horizontal dd:after { + display: table; + content: " "; + } + .dl-horizontal dd:after { + clear: both; + } +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + font-size: 17.5px; + font-weight: 300; + line-height: 1.25; +} + +blockquote p:last-child { + margin-bottom: 0; +} + +blockquote small, +blockquote .small { + display: block; + line-height: 1.428571429; + color: #999999; +} + +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small, +blockquote.pull-right .small { + text-align: right; +} + +blockquote.pull-right small:before, +blockquote.pull-right .small:before { + content: ''; +} + +blockquote.pull-right 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.428571429; +} + +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; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.428571429; + color: #333333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #cccccc; + 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; +} + +.container:before, +.container:after { + display: table; + content: " "; +} + +.container:after { + clear: both; +} + +.container:before, +.container:after { + display: table; + content: " "; +} + +.container:after { + clear: both; +} + +@media (min-width: 768px) { + .container { + width: 750px; + } +} + +@media (min-width: 992px) { + .container { + width: 970px; + } +} + +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} + +.row { + margin-right: -15px; + margin-left: -15px; +} + +.row:before, +.row:after { + display: table; + content: " "; +} + +.row:after { + clear: both; +} + +.row:before, +.row:after { + display: table; + content: " "; +} + +.row:after { + clear: both; +} + +.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.66666666666666%; +} + +.col-xs-10 { + width: 83.33333333333334%; +} + +.col-xs-9 { + width: 75%; +} + +.col-xs-8 { + width: 66.66666666666666%; +} + +.col-xs-7 { + width: 58.333333333333336%; +} + +.col-xs-6 { + width: 50%; +} + +.col-xs-5 { + width: 41.66666666666667%; +} + +.col-xs-4 { + width: 33.33333333333333%; +} + +.col-xs-3 { + width: 25%; +} + +.col-xs-2 { + width: 16.666666666666664%; +} + +.col-xs-1 { + width: 8.333333333333332%; +} + +.col-xs-pull-12 { + right: 100%; +} + +.col-xs-pull-11 { + right: 91.66666666666666%; +} + +.col-xs-pull-10 { + right: 83.33333333333334%; +} + +.col-xs-pull-9 { + right: 75%; +} + +.col-xs-pull-8 { + right: 66.66666666666666%; +} + +.col-xs-pull-7 { + right: 58.333333333333336%; +} + +.col-xs-pull-6 { + right: 50%; +} + +.col-xs-pull-5 { + right: 41.66666666666667%; +} + +.col-xs-pull-4 { + right: 33.33333333333333%; +} + +.col-xs-pull-3 { + right: 25%; +} + +.col-xs-pull-2 { + right: 16.666666666666664%; +} + +.col-xs-pull-1 { + right: 8.333333333333332%; +} + +.col-xs-pull-0 { + right: 0; +} + +.col-xs-push-12 { + left: 100%; +} + +.col-xs-push-11 { + left: 91.66666666666666%; +} + +.col-xs-push-10 { + left: 83.33333333333334%; +} + +.col-xs-push-9 { + left: 75%; +} + +.col-xs-push-8 { + left: 66.66666666666666%; +} + +.col-xs-push-7 { + left: 58.333333333333336%; +} + +.col-xs-push-6 { + left: 50%; +} + +.col-xs-push-5 { + left: 41.66666666666667%; +} + +.col-xs-push-4 { + left: 33.33333333333333%; +} + +.col-xs-push-3 { + left: 25%; +} + +.col-xs-push-2 { + left: 16.666666666666664%; +} + +.col-xs-push-1 { + left: 8.333333333333332%; +} + +.col-xs-push-0 { + left: 0; +} + +.col-xs-offset-12 { + margin-left: 100%; +} + +.col-xs-offset-11 { + margin-left: 91.66666666666666%; +} + +.col-xs-offset-10 { + margin-left: 83.33333333333334%; +} + +.col-xs-offset-9 { + margin-left: 75%; +} + +.col-xs-offset-8 { + margin-left: 66.66666666666666%; +} + +.col-xs-offset-7 { + margin-left: 58.333333333333336%; +} + +.col-xs-offset-6 { + margin-left: 50%; +} + +.col-xs-offset-5 { + margin-left: 41.66666666666667%; +} + +.col-xs-offset-4 { + margin-left: 33.33333333333333%; +} + +.col-xs-offset-3 { + margin-left: 25%; +} + +.col-xs-offset-2 { + margin-left: 16.666666666666664%; +} + +.col-xs-offset-1 { + margin-left: 8.333333333333332%; +} + +.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.66666666666666%; + } + .col-sm-10 { + width: 83.33333333333334%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666666666666%; + } + .col-sm-7 { + width: 58.333333333333336%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666666666667%; + } + .col-sm-4 { + width: 33.33333333333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.666666666666664%; + } + .col-sm-1 { + width: 8.333333333333332%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666666666666%; + } + .col-sm-pull-10 { + right: 83.33333333333334%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666666666666%; + } + .col-sm-pull-7 { + right: 58.333333333333336%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666666666667%; + } + .col-sm-pull-4 { + right: 33.33333333333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.666666666666664%; + } + .col-sm-pull-1 { + right: 8.333333333333332%; + } + .col-sm-pull-0 { + right: 0; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666666666666%; + } + .col-sm-push-10 { + left: 83.33333333333334%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666666666666%; + } + .col-sm-push-7 { + left: 58.333333333333336%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666666666667%; + } + .col-sm-push-4 { + left: 33.33333333333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.666666666666664%; + } + .col-sm-push-1 { + left: 8.333333333333332%; + } + .col-sm-push-0 { + left: 0; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666666666666%; + } + .col-sm-offset-10 { + margin-left: 83.33333333333334%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666666666666%; + } + .col-sm-offset-7 { + margin-left: 58.333333333333336%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666666666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.666666666666664%; + } + .col-sm-offset-1 { + margin-left: 8.333333333333332%; + } + .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.66666666666666%; + } + .col-md-10 { + width: 83.33333333333334%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666666666666%; + } + .col-md-7 { + width: 58.333333333333336%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666666666667%; + } + .col-md-4 { + width: 33.33333333333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.666666666666664%; + } + .col-md-1 { + width: 8.333333333333332%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666666666666%; + } + .col-md-pull-10 { + right: 83.33333333333334%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666666666666%; + } + .col-md-pull-7 { + right: 58.333333333333336%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666666666667%; + } + .col-md-pull-4 { + right: 33.33333333333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.666666666666664%; + } + .col-md-pull-1 { + right: 8.333333333333332%; + } + .col-md-pull-0 { + right: 0; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666666666666%; + } + .col-md-push-10 { + left: 83.33333333333334%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666666666666%; + } + .col-md-push-7 { + left: 58.333333333333336%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666666666667%; + } + .col-md-push-4 { + left: 33.33333333333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.666666666666664%; + } + .col-md-push-1 { + left: 8.333333333333332%; + } + .col-md-push-0 { + left: 0; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666666666666%; + } + .col-md-offset-10 { + margin-left: 83.33333333333334%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666666666666%; + } + .col-md-offset-7 { + margin-left: 58.333333333333336%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666666666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.666666666666664%; + } + .col-md-offset-1 { + margin-left: 8.333333333333332%; + } + .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.66666666666666%; + } + .col-lg-10 { + width: 83.33333333333334%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666666666666%; + } + .col-lg-7 { + width: 58.333333333333336%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666666666667%; + } + .col-lg-4 { + width: 33.33333333333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.666666666666664%; + } + .col-lg-1 { + width: 8.333333333333332%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666666666666%; + } + .col-lg-pull-10 { + right: 83.33333333333334%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666666666666%; + } + .col-lg-pull-7 { + right: 58.333333333333336%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666666666667%; + } + .col-lg-pull-4 { + right: 33.33333333333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.666666666666664%; + } + .col-lg-pull-1 { + right: 8.333333333333332%; + } + .col-lg-pull-0 { + right: 0; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666666666666%; + } + .col-lg-push-10 { + left: 83.33333333333334%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666666666666%; + } + .col-lg-push-7 { + left: 58.333333333333336%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666666666667%; + } + .col-lg-push-4 { + left: 33.33333333333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.666666666666664%; + } + .col-lg-push-1 { + left: 8.333333333333332%; + } + .col-lg-push-0 { + left: 0; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666666666666%; + } + .col-lg-offset-10 { + margin-left: 83.33333333333334%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666666666666%; + } + .col-lg-offset-7 { + margin-left: 58.333333333333336%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666666666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.666666666666664%; + } + .col-lg-offset-1 { + margin-left: 8.333333333333332%; + } + .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.428571429; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #dddddd; +} + +.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 #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.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 #dddddd; +} + +.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 #dddddd; +} + +.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; + display: table-column; + float: none; +} + +table td[class*="col-"], +table th[class*="col-"] { + display: table-cell; + float: none; +} + +.table > thead > tr > .active, +.table > tbody > tr > .active, +.table > tfoot > tr > .active, +.table > thead > .active > td, +.table > tbody > .active > td, +.table > tfoot > .active > td, +.table > thead > .active > th, +.table > tbody > .active > th, +.table > tfoot > .active > th { + background-color: #f5f5f5; +} + +.table-hover > tbody > tr > .active:hover, +.table-hover > tbody > .active:hover > td, +.table-hover > tbody > .active:hover > th { + background-color: #e8e8e8; +} + +.table > thead > tr > .success, +.table > tbody > tr > .success, +.table > tfoot > tr > .success, +.table > thead > .success > td, +.table > tbody > .success > td, +.table > tfoot > .success > td, +.table > thead > .success > th, +.table > tbody > .success > th, +.table > tfoot > .success > th { + background-color: #dff0d8; +} + +.table-hover > tbody > tr > .success:hover, +.table-hover > tbody > .success:hover > td, +.table-hover > tbody > .success:hover > th { + background-color: #d0e9c6; +} + +.table > thead > tr > .danger, +.table > tbody > tr > .danger, +.table > tfoot > tr > .danger, +.table > thead > .danger > td, +.table > tbody > .danger > td, +.table > tfoot > .danger > td, +.table > thead > .danger > th, +.table > tbody > .danger > th, +.table > tfoot > .danger > th { + background-color: #f2dede; +} + +.table-hover > tbody > tr > .danger:hover, +.table-hover > tbody > .danger:hover > td, +.table-hover > tbody > .danger:hover > th { + background-color: #ebcccc; +} + +.table > thead > tr > .warning, +.table > tbody > tr > .warning, +.table > tfoot > tr > .warning, +.table > thead > .warning > td, +.table > tbody > .warning > td, +.table > tfoot > .warning > td, +.table > thead > .warning > th, +.table > tbody > .warning > th, +.table > tfoot > .warning > th { + background-color: #fcf8e3; +} + +.table-hover > tbody > tr > .warning:hover, +.table-hover > tbody > .warning:hover > td, +.table-hover > tbody > .warning:hover > th { + background-color: #faf2cc; +} + +@media (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: scroll; + overflow-y: hidden; + border: 1px solid #dddddd; + -ms-overflow-style: -ms-autohiding-scrollbar; + -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; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333333; + 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; +} + +select[multiple], +select[size] { + height: auto; +} + +select optgroup { + font-family: inherit; + font-size: inherit; + font-style: inherit; +} + +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; +} + +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { + height: auto; +} + +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; + vertical-align: middle; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; + vertical-align: middle; + background-color: #ffffff; + background-image: none; + border: 1px solid #cccccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +} + +.form-control:-moz-placeholder { + color: #999999; +} + +.form-control::-moz-placeholder { + color: #999999; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #999999; +} + +.form-control::-webkit-input-placeholder { + color: #999999; +} + +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eeeeee; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 15px; +} + +.radio, +.checkbox { + display: block; + min-height: 20px; + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; + vertical-align: middle; +} + +.radio label, +.checkbox label { + display: inline; + margin-bottom: 0; + 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 { + 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 { + height: auto; +} + +.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, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} + +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-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, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} + +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} + +.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, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} + +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} + +.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; + } + .form-inline select.form-control { + width: auto; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 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-group:before, +.form-horizontal .form-group:after { + display: table; + content: " "; +} + +.form-horizontal .form-group:after { + clear: both; +} + +.form-horizontal .form-group:before, +.form-horizontal .form-group:after { + display: table; + content: " "; +} + +.form-horizontal .form-group:after { + clear: both; +} + +.form-horizontal .form-control-static { + padding-top: 7px; +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} + +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.428571429; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; +} + +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-default { + color: #333333; + background-color: #ffffff; + border-color: #cccccc; +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333333; + 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: #ffffff; + border-color: #cccccc; +} + +.btn-default .badge { + color: #ffffff; + background-color: #fff; +} + +.btn-primary { + color: #ffffff; + background-color: #428bca; + border-color: #357ebd; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #ffffff; + 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-warning { + color: #ffffff; + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #ffffff; + 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: #ffffff; + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #ffffff; + 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-success { + color: #ffffff; + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #ffffff; + 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: #ffffff; + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #ffffff; + 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-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: #999999; + text-decoration: none; +} + +.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-xs { + 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 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + display: none; +} + +.collapse.in { + display: block; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + transition: height 0.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'; + -webkit-font-smoothing: antialiased; + font-style: normal; + font-weight: normal; + line-height: 1; + -moz-osx-font-smoothing: grayscale; +} + +.glyphicon:empty { + width: 1em; +} + +.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: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 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: normal; + line-height: 1.428571429; + color: #333333; + 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: #ffffff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} + +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} + +.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-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.428571429; + color: #999999; +} + +.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 { + 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 { + right: 0; + left: 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: 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:before, +.btn-toolbar:after { + display: table; + content: " "; +} + +.btn-toolbar:after { + clear: both; +} + +.btn-toolbar:before, +.btn-toolbar:after { + display: table; + content: " "; +} + +.btn-toolbar:after { + clear: both; +} + +.btn-toolbar .btn-group { + float: left; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group, +.btn-toolbar > .btn-group + .btn-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-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-top-right-radius: 0; + border-bottom-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-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +.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, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 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:before, +.btn-group-vertical > .btn-group:after { + display: table; + content: " "; +} + +.btn-group-vertical > .btn-group:after { + clear: both; +} + +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after { + display: table; + content: " "; +} + +.btn-group-vertical > .btn-group:after { + clear: both; +} + +.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-right-radius: 0; + border-bottom-left-radius: 4px; + 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 > .btn:last-child, +.btn-group-vertical > .btn-group:first-child > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn-group:last-child > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.btn-group-justified { + display: table; + width: 100%; + border-collapse: separate; + table-layout: fixed; +} + +.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 { + 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 { + 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 { + 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: #555555; + text-align: center; + background-color: #eeeeee; + border: 1px solid #cccccc; + 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 > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { + 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 > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.input-group-addon:last-child { + border-left: 0; +} + +.input-group-btn { + position: relative; + white-space: nowrap; +} + +.input-group-btn:first-child > .btn { + margin-right: -1px; +} + +.input-group-btn:last-child > .btn { + margin-left: -1px; +} + +.input-group-btn > .btn { + position: relative; +} + +.input-group-btn > .btn + .btn { + margin-left: -4px; +} + +.input-group-btn > .btn:hover, +.input-group-btn > .btn:active { + z-index: 2; +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav:before, +.nav:after { + display: table; + content: " "; +} + +.nav:after { + clear: both; +} + +.nav:before, +.nav:after { + display: table; + content: " "; +} + +.nav:after { + clear: both; +} + +.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: #eeeeee; +} + +.nav > li.disabled > a { + color: #999999; +} + +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} + +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + 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 #dddddd; +} + +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} + +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.428571429; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #dddddd; + 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 #dddddd; +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #dddddd; + 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: #ffffff; + } +} + +.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: #ffffff; + 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 #dddddd; +} + +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #dddddd; + 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: #ffffff; + } +} + +.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; +} + +.navbar:before, +.navbar:after { + display: table; + content: " "; +} + +.navbar:after { + clear: both; +} + +.navbar:before, +.navbar:after { + display: table; + content: " "; +} + +.navbar:after { + clear: both; +} + +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} + +.navbar-header:before, +.navbar-header:after { + display: table; + content: " "; +} + +.navbar-header:after { + clear: both; +} + +.navbar-header:before, +.navbar-header:after { + display: table; + content: " "; +} + +.navbar-header:after { + clear: both; +} + +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} + +.navbar-collapse { + max-height: 340px; + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} + +.navbar-collapse:before, +.navbar-collapse:after { + display: table; + content: " "; +} + +.navbar-collapse:after { + clear: both; +} + +.navbar-collapse:before, +.navbar-collapse:after { + display: table; + content: " "; +} + +.navbar-collapse:after { + clear: both; +} + +.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 > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} + +@media (min-width: 768px) { + .container > .navbar-header, + .container > .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 15px; + font-size: 18px; + line-height: 20px; +} + +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} + +@media (min-width: 768px) { + .navbar > .container .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 .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, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.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; + } + .navbar-form select.form-control { + width: auto; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 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-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-nav.pull-right > li > .dropdown-menu, +.navbar-nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.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: #777777; +} + +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} + +.navbar-default .navbar-text { + color: #777777; +} + +.navbar-default .navbar-nav > li > a { + color: #777777; +} + +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333333; + background-color: transparent; +} + +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555555; + background-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} + +.navbar-default .navbar-toggle { + border-color: #dddddd; +} + +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #dddddd; +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #cccccc; +} + +.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: #555555; + background-color: #e7e7e7; +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333333; + 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: #555555; + 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: #cccccc; + background-color: transparent; + } +} + +.navbar-default .navbar-link { + color: #777777; +} + +.navbar-default .navbar-link:hover { + color: #333333; +} + +.navbar-inverse { + background-color: #222222; + border-color: #080808; +} + +.navbar-inverse .navbar-brand { + color: #999999; +} + +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .navbar-nav > li > a { + color: #999999; +} + +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #080808; +} + +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444444; + background-color: transparent; +} + +.navbar-inverse .navbar-toggle { + border-color: #333333; +} + +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333333; +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #ffffff; +} + +.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: #ffffff; + 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: #999999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + 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: #ffffff; + 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: #444444; + background-color: transparent; + } +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.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: #cccccc; + content: "/\00a0"; +} + +.breadcrumb > .active { + color: #999999; +} + +.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.428571429; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; +} + +.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-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 { + background-color: #eeeeee; +} + +.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: #ffffff; + 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: #999999; + cursor: not-allowed; + background-color: #ffffff; + border-color: #dddddd; +} + +.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-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-bottom-left-radius: 3px; + border-top-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:before, +.pager:after { + display: table; + content: " "; +} + +.pager:after { + clear: both; +} + +.pager:before, +.pager:after { + display: table; + content: " "; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 15px; +} + +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.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: #999999; + cursor: not-allowed; + background-color: #ffffff; +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} + +.label[href]:hover, +.label[href]:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label:empty { + display: none; +} + +.btn .label { + position: relative; + top: -1px; +} + +.label-default { + background-color: #999999; +} + +.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: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; + border-radius: 10px; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #ffffff; +} + +.nav-pills > li > a > .badge { + margin-left: 3px; +} + +.jumbotron { + padding: 30px; + margin-bottom: 30px; + font-size: 21px; + font-weight: 200; + line-height: 2.1428571435; + color: inherit; + background-color: #eeeeee; +} + +.jumbotron h1, +.jumbotron .h1 { + line-height: 1; + color: inherit; +} + +.jumbotron p { + line-height: 1.4; +} + +.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.428571429; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.thumbnail > img, +.thumbnail a > img { + display: block; + height: auto; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #428bca; +} + +.thumbnail .caption { + padding: 9px; + color: #333333; +} + +.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, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #ffffff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.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, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.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, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.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, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.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, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.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: #ffffff; + border: 1px solid #dddddd; +} + +.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: #555555; +} + +a.list-group-item .list-group-item-heading { + color: #333333; +} + +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: #ffffff; + 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-heading { + margin-top: 0; + margin-bottom: 5px; +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} + +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.panel-body { + padding: 15px; +} + +.panel-body:before, +.panel-body:after { + display: table; + content: " "; +} + +.panel-body:after { + clear: both; +} + +.panel-body:before, +.panel-body:after { + display: table; + content: " "; +} + +.panel-body:after { + clear: both; +} + +.panel > .list-group { + margin-bottom: 0; +} + +.panel > .list-group .list-group-item { + border-width: 1px 0; +} + +.panel > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.panel > .list-group .list-group-item:last-child { + border-bottom: 0; +} + +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} + +.panel > .table, +.panel > .table-responsive > .table { + margin-bottom: 0; +} + +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #dddddd; +} + +.panel > .table > tbody:first-child th, +.panel > .table > tbody: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:last-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > th, +.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, +.panel > .table-bordered > thead > tr:last-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > td, +.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 { + border-bottom: 0; +} + +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} + +.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 #dddddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.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 #dddddd; +} + +.panel-group .panel-footer { + border-top: 0; +} + +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd; +} + +.panel-default { + border-color: #dddddd; +} + +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #dddddd; +} + +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #dddddd; +} + +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #dddddd; +} + +.panel-primary { + border-color: #428bca; +} + +.panel-primary > .panel-heading { + color: #ffffff; + 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-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; +} + +.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; +} + +.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, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 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: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.modal-open { + overflow: hidden; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + display: none; + overflow: auto; + overflow-y: scroll; +} + +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -moz-transition: -moz-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.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; + z-index: 1050; + width: auto; + margin: 10px; +} + +.modal-content { + position: relative; + background-color: #ffffff; + border: 1px solid #999999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + background-clip: padding-box; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} + +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} + +.modal-header { + min-height: 16.428571429px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} + +.modal-header .close { + margin-top: -2px; +} + +.modal-title { + margin: 0; + line-height: 1.428571429; +} + +.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:before, +.modal-footer:after { + display: table; + content: " "; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} + +.modal-footer:after { + clear: both; +} + +.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 screen and (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 12px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} + +.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: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + 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-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.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: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + background-clip: padding-box; +} + +.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 { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + border-bottom-width: 0; + content: " "; +} + +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; + content: " "; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; + content: " "; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; + content: " "; +} + +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + height: auto; + max-width: 100%; + 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: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.left { + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} + +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 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 { + color: #ffffff; + text-decoration: none; + outline: none; + opacity: 0.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%; + 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 #ffffff; + border-radius: 10px; +} + +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #ffffff; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} + +.carousel-caption .btn { + text-shadow: none; +} + +@media screen and (min-width: 768px) { + .carousel-control .glyphicons-chevron-left, + .carousel-control .glyphicons-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 { + display: table; + content: " "; +} + +.clearfix: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, +tr.visible-xs, +th.visible-xs, +td.visible-xs { + 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-xs.visible-sm { + display: block !important; + } + table.visible-xs.visible-sm { + display: table; + } + tr.visible-xs.visible-sm { + display: table-row !important; + } + th.visible-xs.visible-sm, + td.visible-xs.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-xs.visible-md { + display: block !important; + } + table.visible-xs.visible-md { + display: table; + } + tr.visible-xs.visible-md { + display: table-row !important; + } + th.visible-xs.visible-md, + td.visible-xs.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-xs.visible-lg { + display: block !important; + } + table.visible-xs.visible-lg { + display: table; + } + tr.visible-xs.visible-lg { + display: table-row !important; + } + th.visible-xs.visible-lg, + td.visible-xs.visible-lg { + display: table-cell !important; + } +} + +.visible-sm, +tr.visible-sm, +th.visible-sm, +td.visible-sm { + display: none !important; +} + +@media (max-width: 767px) { + .visible-sm.visible-xs { + display: block !important; + } + table.visible-sm.visible-xs { + display: table; + } + tr.visible-sm.visible-xs { + display: table-row !important; + } + th.visible-sm.visible-xs, + td.visible-sm.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-sm.visible-md { + display: block !important; + } + table.visible-sm.visible-md { + display: table; + } + tr.visible-sm.visible-md { + display: table-row !important; + } + th.visible-sm.visible-md, + td.visible-sm.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-sm.visible-lg { + display: block !important; + } + table.visible-sm.visible-lg { + display: table; + } + tr.visible-sm.visible-lg { + display: table-row !important; + } + th.visible-sm.visible-lg, + td.visible-sm.visible-lg { + display: table-cell !important; + } +} + +.visible-md, +tr.visible-md, +th.visible-md, +td.visible-md { + display: none !important; +} + +@media (max-width: 767px) { + .visible-md.visible-xs { + display: block !important; + } + table.visible-md.visible-xs { + display: table; + } + tr.visible-md.visible-xs { + display: table-row !important; + } + th.visible-md.visible-xs, + td.visible-md.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-md.visible-sm { + display: block !important; + } + table.visible-md.visible-sm { + display: table; + } + tr.visible-md.visible-sm { + display: table-row !important; + } + th.visible-md.visible-sm, + td.visible-md.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-md.visible-lg { + display: block !important; + } + table.visible-md.visible-lg { + display: table; + } + tr.visible-md.visible-lg { + display: table-row !important; + } + th.visible-md.visible-lg, + td.visible-md.visible-lg { + display: table-cell !important; + } +} + +.visible-lg, +tr.visible-lg, +th.visible-lg, +td.visible-lg { + display: none !important; +} + +@media (max-width: 767px) { + .visible-lg.visible-xs { + display: block !important; + } + table.visible-lg.visible-xs { + display: table; + } + tr.visible-lg.visible-xs { + display: table-row !important; + } + th.visible-lg.visible-xs, + td.visible-lg.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-lg.visible-sm { + display: block !important; + } + table.visible-lg.visible-sm { + display: table; + } + tr.visible-lg.visible-sm { + display: table-row !important; + } + th.visible-lg.visible-sm, + td.visible-lg.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-lg.visible-md { + display: block !important; + } + table.visible-lg.visible-md { + display: table; + } + tr.visible-lg.visible-md { + display: table-row !important; + } + th.visible-lg.visible-md, + td.visible-lg.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; + } +} + +.hidden-xs { + display: block !important; +} + +table.hidden-xs { + display: table; +} + +tr.hidden-xs { + display: table-row !important; +} + +th.hidden-xs, +td.hidden-xs { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-xs, + tr.hidden-xs, + th.hidden-xs, + td.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-xs.hidden-sm, + tr.hidden-xs.hidden-sm, + th.hidden-xs.hidden-sm, + td.hidden-xs.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-xs.hidden-md, + tr.hidden-xs.hidden-md, + th.hidden-xs.hidden-md, + td.hidden-xs.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-xs.hidden-lg, + tr.hidden-xs.hidden-lg, + th.hidden-xs.hidden-lg, + td.hidden-xs.hidden-lg { + display: none !important; + } +} + +.hidden-sm { + display: block !important; +} + +table.hidden-sm { + display: table; +} + +tr.hidden-sm { + display: table-row !important; +} + +th.hidden-sm, +td.hidden-sm { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-sm.hidden-xs, + tr.hidden-sm.hidden-xs, + th.hidden-sm.hidden-xs, + td.hidden-sm.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm, + tr.hidden-sm, + th.hidden-sm, + td.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-sm.hidden-md, + tr.hidden-sm.hidden-md, + th.hidden-sm.hidden-md, + td.hidden-sm.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-sm.hidden-lg, + tr.hidden-sm.hidden-lg, + th.hidden-sm.hidden-lg, + td.hidden-sm.hidden-lg { + display: none !important; + } +} + +.hidden-md { + display: block !important; +} + +table.hidden-md { + display: table; +} + +tr.hidden-md { + display: table-row !important; +} + +th.hidden-md, +td.hidden-md { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-md.hidden-xs, + tr.hidden-md.hidden-xs, + th.hidden-md.hidden-xs, + td.hidden-md.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-md.hidden-sm, + tr.hidden-md.hidden-sm, + th.hidden-md.hidden-sm, + td.hidden-md.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md, + tr.hidden-md, + th.hidden-md, + td.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-md.hidden-lg, + tr.hidden-md.hidden-lg, + th.hidden-md.hidden-lg, + td.hidden-md.hidden-lg { + display: none !important; + } +} + +.hidden-lg { + display: block !important; +} + +table.hidden-lg { + display: table; +} + +tr.hidden-lg { + display: table-row !important; +} + +th.hidden-lg, +td.hidden-lg { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-lg.hidden-xs, + tr.hidden-lg.hidden-xs, + th.hidden-lg.hidden-xs, + td.hidden-lg.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-lg.hidden-sm, + tr.hidden-lg.hidden-sm, + th.hidden-lg.hidden-sm, + td.hidden-lg.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-lg.hidden-md, + tr.hidden-lg.hidden-md, + th.hidden-lg.hidden-md, + td.hidden-lg.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-lg, + tr.hidden-lg, + th.hidden-lg, + td.hidden-lg { + display: none !important; + } +} + +.visible-print, +tr.visible-print, +th.visible-print, +td.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; + } + .hidden-print, + tr.hidden-print, + th.hidden-print, + td.hidden-print { + display: none !important; + } +} \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/css/codemirror.css b/console/src/main/resources/static/console-fe/public/css/codemirror.css new file mode 100644 index 000000000..b54156be5 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/css/codemirror.css @@ -0,0 +1,353 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: 300px; + color: black; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} + +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: -20px; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 30px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -30px; margin-right: -30px; + padding-bottom: 30px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 30px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -30px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + overflow: auto; +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background: #ffa; + background: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } diff --git a/console/src/main/resources/static/console-fe/public/css/console1412.css b/console/src/main/resources/static/console-fe/public/css/console1412.css new file mode 100644 index 000000000..838132db0 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/css/console1412.css @@ -0,0 +1 @@ +@charset "UTF-8";.viewFramework-topbar{position:fixed;width:100%;height:50px;background:#09C;z-index:101}.viewFramework-body{position:absolute;width:100%;top:50px;bottom:0px;background-color:#000;z-index:100}.viewFramework-body .console-global-notice .console-global-notice-nav{top:14px}.viewFramework-body .console-global-notice .console-global-notice-list{margin:0;height:40px}.viewFramework-body .console-global-notice .console-global-notice-list .console-global-notice-item{padding:11px 12px;border:none}.viewFramework-body .console-global-notice .console-global-notice-list .console-global-notice-item .console-global-notice-nomore{top:10px}.viewFramework-body.viewFramework-topbar-hide{top:0px}.viewFramework-body.viewFramework-topbar-hide .viewFramework-sidebar{top:0px}.viewFramework-sidebar{width:0px;display:none;position:fixed;top:50px;bottom:0px;background-color:#293038;z-index:102;overflow-x:hidden}.viewFramework-sidebar .sidebar-content{width:200px;height:100%;overflow:auto;overflow-x:hidden}.viewFramework-sidebar .sidebar-trans{-o-transition:all 0.12s ease,0.12s ease;-ms-transition:all 0.12s ease,0.12s ease;-moz-transition:all 0.12s ease,0.12s ease;-webkit-transition:all 0.12s ease,0.12s ease}.viewFramework-sidebar .sidebar-fold{height:30px;width:180px;background:#394555;color:#aeb9c2;text-align:center;line-height:30px !important;font-size:12px;user-select:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none}.viewFramework-sidebar .sidebar-fold:hover{background:#37424f}.viewFramework-sidebar .sidebar-nav{width:180px}.viewFramework-sidebar .sidebar-nav ul{margin:0px;padding:0px;list-style:none;overflow:hidden}.viewFramework-sidebar .sidebar-nav li a{display:block;width:100%;height:40px;line-height:40px;overflow:hidden}.viewFramework-sidebar .sidebar-nav li a:hover{background:#37424f}.viewFramework-sidebar .sidebar-nav li a:hover .nav-icon,.viewFramework-sidebar .sidebar-nav li a:hover .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav .nav-item{position:relative}.viewFramework-sidebar .sidebar-nav .nav-comment{background:#2d3945;color:#cccccc;height:26px;margin-left:8px;line-height:26px;padding:0 7px;vertical-align:middle;position:relative;display:none}.viewFramework-sidebar .sidebar-nav .nav-comment .icon-arrow-left{position:absolute;left:-14px;line-height:26px;font-size:24px;color:#2d3945}.viewFramework-sidebar .sidebar-nav .nav-tooltip-comment{color:#ccc}.viewFramework-sidebar .sidebar-nav .sidebar-title{height:40px;background:#22282e;color:#fff;line-height:40px;position:relative;cursor:pointer;-webkit-user-select:none;-moz-user-select:none}.viewFramework-sidebar .sidebar-nav .sidebar-title:hover{background:#414d5c}.viewFramework-sidebar .sidebar-nav .sidebar-title-icon{display:inline-block;margin:0 8px 0 20px;vertical-align:middle;transition:transform 0.12s;-o-transition:-o-transform 0.12s;-ms-transition:-ms-transform 0.12s;-moz-transition:-moz-transform 0.12s;-webkit-transition:-webkit-transform 0.12s}.viewFramework-sidebar .sidebar-manage{vertical-align:middle;position:absolute;height:40px;width:40px;right:0}.viewFramework-sidebar .sidebar-manage a{display:block;width:100%;height:100%;text-align:center;line-height:40px;font-size:14px;color:#a0abb3;text-decoration:none}.viewFramework-sidebar .sidebar-nav-fold ul{height:0 !important;overflow:hidden}.viewFramework-sidebar .sidebar-nav-fold .sidebar-title-icon{transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg)}.viewFramework-sidebar .sidebar-nav-fold .sidebar-title{background:#37424f;border-bottom:1px solid #414d5c}.viewFramework-sidebar .sidebar-nav .nav-item:hover .nav-comment{display:inline-block}.viewFramework-sidebar .entrance-nav .nav-comment{margin-left:10px}.viewFramework-sidebar .sidebar-nav .nav-icon{width:50px;text-align:center;font-size:16px;float:left;color:#aeb9c2}.viewFramework-sidebar .sidebar-nav .nav-title{float:left;overflow:hidden;color:#fff;white-space:nowrap;text-overflow:ellipsis;display:block;width:130px}.viewFramework-sidebar .entrance-nav .nav-title{width:auto}.viewFramework-sidebar .sidebar-nav li.consolehome .nav-tooltip{top:15px;line-height:40px}.viewFramework-sidebar .sidebar-nav li.consolehome a{height:70px;line-height:70px;background:#293038}.viewFramework-sidebar .sidebar-nav li.consolehome a .nav-icon{font-size:20px}.viewFramework-sidebar .sidebar-nav li.consolehome.active a{background:#293038}.viewFramework-sidebar .sidebar-nav li.active a{background:#0099cc}.viewFramework-sidebar .sidebar-nav li.active a .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav li.active a .nav-icon{color:#fff}.viewFramework-sidebar .sidebar-nav .manage-nav{height:30px;overflow:hidden}.viewFramework-sidebar .sidebar-nav .manage-nav:hover .nav-icon{color:#fff}.viewFramework-sidebar .sidebar-nav .manage-nav a{display:block;height:100%}.viewFramework-sidebar .sidebar-nav .manage-nav .nav-icon{height:100%;line-height:30px;font-size:16px}.viewFramework-sidebar .sidebar-nav .manage-nav .nav-title{margin-top:14px;background:#293038;height:1px;width:120px}.viewFramework-sidebar .sidebar-nav .more-nav{display:block;width:100%;height:40px;line-height:40px;position:relative}.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch{background:#09c}.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch .nav-icon,.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch:hover{background:#09c}.viewFramework-sidebar .sidebar-nav .more-nav.open .icon-up{display:none}.viewFramework-sidebar .sidebar-nav .more-nav.open .icon-down{display:inline-block}.viewFramework-sidebar .sidebar-nav .more-nav .icon-up{display:inline-block}.viewFramework-sidebar .sidebar-nav .more-nav .icon-down{display:none}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch{display:block;width:100%;height:40px;line-height:40px}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover{background:#425160}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover .nav-icon,.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container{background:#425160;position:absolute;bottom:40px;top:auto;border:none;border-radius:0 0;box-shadow:none;margin:0;width:100%}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container a{color:#fff;text-decoration:none}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item{display:block;height:40px;line-height:40px}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item:hover{background:#3a4856}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item:hover .more-nav-item-icon{color:#aeb9c2}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item-icon{width:50px;display:inline-block;vertical-align:text-top;text-align:center;color:#546478}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item.active{background:#2d3945}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item.active .more-nav-item-icon{color:#0099cc}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-close{height:20px;background:#09c;text-align:right;line-height:20px;cursor:pointer}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-close .icon-down{text-align:left;width:32px;display:inline-block;color:#80cce6;vertical-align:middle}.viewFramework-sidebar-mini .viewFramework-sidebar,.viewFramework-sidebar.sidebar-mini{width:50px;display:block}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-content,.viewFramework-sidebar.sidebar-mini .sidebar-content{width:70px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-fold,.viewFramework-sidebar.sidebar-mini .sidebar-fold{width:50px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav,.viewFramework-sidebar.sidebar-mini .sidebar-nav{width:50px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .nav-item a:hover+.nav-tooltip,.viewFramework-sidebar.sidebar-mini .sidebar-nav .nav-item a:hover+.nav-tooltip{display:block}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .nav-title,.viewFramework-sidebar.sidebar-mini .sidebar-nav .nav-title{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-switch:hover{background:#425160 !important}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav.open .more-nav-switch{background:#425160 !important}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-container{bottom:0px;left:50px;width:180px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-container .more-nav-item{display:block;height:40px;line-height:40px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item-icon,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-container .more-nav-item-icon{width:50px;padding-left:0}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-close,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-close{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav li.consolehome a :hover,.viewFramework-sidebar.sidebar-mini .sidebar-nav li.consolehome a :hover{background:#425160}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-title .sidebar-title-text,.viewFramework-sidebar.sidebar-mini .sidebar-title .sidebar-title-text{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-title-inner:hover+.nav-tooltip,.viewFramework-sidebar.sidebar-mini .sidebar-title-inner:hover+.nav-tooltip{display:block}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-manage,.viewFramework-sidebar.sidebar-mini .sidebar-manage{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .entrance-nav .nav-item:hover .nav-comment,.viewFramework-sidebar.sidebar-mini .entrance-nav .nav-item:hover .nav-comment{display:none}.viewFramework-sidebar-full .viewFramework-sidebar,.viewFramework-sidebar.sidebar-full{width:180px;display:block}.viewFramework-sidebar-full .viewFramework-sidebar .sidebar-nav .nav-icon,.viewFramework-sidebar.sidebar-full .sidebar-nav .nav-icon{width:50px}.viewFramework-sidebar-mini .viewFramework-product{left:50px}.viewFramework-sidebar-full .viewFramework-product{left:180px}.viewFramework-sidebar-dialog .modal-dialog{width:730px}.viewFramework-sidebar-dialog .modal-dialog .modal-title{user-select:none;-webkit-user-select:none}.viewFramework-sidebar-manage .sidebar-item-list{padding:4px 0 0 0;height:auto}.viewFramework-sidebar-manage .sidebar-item-list-picked .sidebar-item{border:1px solid #37a9d5}.viewFramework-sidebar-manage .sidebar-item-list-picked .sidebar-item .sidebar-item-opt-icon{display:block}.viewFramework-sidebar-manage .sidebar-item-list-picked .sidebar-item .sidebar-item-icon{color:#516176}.viewFramework-sidebar-manage .sidebar-config-title{padding-left:6px;user-select:none;-webkit-user-select:none}.viewFramework-sidebar-manage .sidebar-item-wrap{padding:6px;width:33.3%;float:left;user-select:none;-webkit-user-select:none}.viewFramework-sidebar-manage .sidebar-item-wrap.on-drag-hover .sidebar-item{border:1px dashed #ddd}.viewFramework-sidebar-manage .sidebar-item{height:32px;padding:4px;line-height:24px;background:#fff;border:1px solid #d3dce3;position:relative;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-o-transition:all 0.1s, 0.1s;-ms-transition:all 0.1s, 0.1s;-moz-transition:all 0.1s, 0.1s;-webkit-transition:all 0.1s, 0.1s}.viewFramework-sidebar-manage .sidebar-item:hover{border:1px solid #37a9d5}.viewFramework-sidebar-manage .sidebar-item:hover .sidebar-item-opt-icon{display:block}.viewFramework-sidebar-manage .sidebar-item .sidebar-item-icon{color:#aeb9c2;font-size:14px;margin:0 2px;position:relative;top:1px}.viewFramework-sidebar-manage .sidebar-item .sidebar-item-opt-icon{display:none;position:absolute;height:30px;width:30px;right:0;top:0;line-height:30px;text-align:center;border-left:1px solid #37a9d5;color:#37a9d5;font-size:14px}.viewFramework-sidebar-manage .sidebar-config-gap{border:1px dashed #e8ecf0;margin:16px 5px;user-select:none;-webkit-user-select:none}.aliyun-console-sidebar-tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.aliyun-console-sidebar-tooltip .tooltip-inner{max-width:200px;padding:12px 8px;color:#ffffff;text-align:center;text-decoration:none;border-radius:0 0;background-color:#425160}.aliyun-console-sidebar-tooltip .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.aliyun-console-sidebar-tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.aliyun-console-sidebar-tooltip.right{padding:0 5px;margin-left:3px}.aliyun-console-sidebar-tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#425160;border-width:5px 5px 5px 0}.viewFramework-product{width:auto;position:absolute;top:0px;left:0px;bottom:0px;right:0px;overflow:hidden;background:#FFF}.viewFramework-product-navbar{width:0px;float:left;background-color:#EAEDF1;position:absolute;top:0px;bottom:0px;z-index:2;overflow:hidden;-o-transition:all 0.2s ease;-ms-transition:all 0.2s ease;-moz-transition:all 0.2s ease;-webkit-transition:all 0.2s ease}.viewFramework-product-navbar .product-nav-stage{width:180px;overflow:hidden;position:absolute;top:0px;bottom:0px;right:0px}.viewFramework-product-navbar .product-nav-stage .product-nav-scene{width:180px;position:absolute;top:0px;bottom:0px;-webkit-transition:position,.2s,linear;-moz-transition:position,.2s,linear}.viewFramework-product-navbar .product-nav-stage .product-nav-main-scene{left:0px}.viewFramework-product-navbar .product-nav-stage .product-nav-sub-scene{left:180px}.viewFramework-product-navbar .product-nav-stage-main .product-nav-main-scene{left:0px}.viewFramework-product-navbar .product-nav-stage-main .product-nav-sub-scene{left:180px}.viewFramework-product-navbar .product-nav-stage-sub .product-nav-main-scene{left:-180px}.viewFramework-product-navbar .product-nav-stage-sub .product-nav-sub-scene{left:0px}.viewFramework-product-navbar .product-nav-scene .product-nav-title{width:180px;height:70px;line-height:70px;background:#D9DEE4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.viewFramework-product-navbar .product-nav-main-scene .product-nav-title{font-weight:bold;text-indent:20px}.viewFramework-product-navbar .product-nav-sub-scene .product-nav-title{text-align:center}.viewFramework-product-navbar .product-nav-sub-scene .product-nav-title a{font-size:20px;color:#546478;text-decoration:none}.viewFramework-product-navbar .product-nav-sub-scene .product-nav-title a:hover{color:#09C}.viewFramework-product-navbar .product-nav-list{position:absolute;top:70px;left:0px;right:0px;bottom:0px;overflow-y:auto;overflow-x:hidden}.viewFramework-product-navbar .product-nav-list .nav-icon{width:30px;height:40px;float:left;text-align:center;font-size:16px;color:#333}.viewFramework-product-navbar .product-nav-list .nav-title{width:138px;float:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.viewFramework-product-navbar .product-nav-list .nav-extend{height:40px;line-height:40px;float:right;margin-top:-40px}.viewFramework-product-navbar .product-nav-list ul{list-style:none;padding:0px;margin:0px}.viewFramework-product-navbar .product-nav-list li a{width:180px;height:40px;line-height:40px;display:block;color:#333}.viewFramework-product-navbar .product-nav-list ul ul li a{color:#666}.viewFramework-product-navbar .product-nav-list ul ul li a .nav-title{text-indent:8px}.viewFramework-product-navbar .product-nav-list li a:hover{background-color:#F4F6F8}.viewFramework-product-navbar .product-nav-list li.active a{background-color:#FFF}.viewFramework-product-navbar-collapse{position:absolute;left:0;top:50%;width:20px;height:50px;z-index:3;-o-transition:all 0.2s ease;-ms-transition:all 0.2s ease;-moz-transition:all 0.2s ease;-webkit-transition:all 0.2s ease}.viewFramework-product-navbar-collapse:hover .product-navbar-collapse{left:0}.viewFramework-product-navbar-collapse:hover .product-navbar-collapse-bg{border-bottom:8px solid transparent;border-left:20px solid #D9DEE4;border-top:8px solid transparent}.viewFramework-product-navbar-collapse .product-navbar-collapse-inner{top:-50%;position:relative;overflow:hidden}.viewFramework-product-navbar-collapse .product-navbar-collapse{height:50px;position:relative;left:-7px;text-align:center;cursor:pointer;-o-transition:all 0.1s ease,0.1s ease;-ms-transition:all 0.1s ease,0.1s ease;-moz-transition:all 0.1s ease,0.1s ease;-webkit-transition:all 0.1s ease,0.1s ease}.viewFramework-product-navbar-collapse .product-navbar-collapse>span{font-size:15px;line-height:50px;vertical-align:text-top}.viewFramework-product-navbar-collapse .product-navbar-collapse-bg{width:0;height:50px;position:absolute;top:0;left:0;border-bottom:9px solid transparent;border-left:13px solid #D9DEE4;border-top:9px solid transparent;-o-transition:all 0.1s ease,0.1s ease;-ms-transition:all 0.1s ease,0.1s ease;-moz-transition:all 0.1s ease,0.1s ease;-webkit-transition:all 0.1s ease,0.1s ease}.viewFramework-product-navbar-collapse .icon-collapse-left{display:none}.viewFramework-product-navbar-collapse .icon-collapse-right{display:inline}.viewFramework-product-body{position:absolute;width:auto;top:0px;bottom:0px;left:0px;right:0px;overflow:hidden;overflow-y:auto;-o-transition:all 0.2s ease;-ms-transition:all 0.2s ease;-moz-transition:all 0.2s ease;-webkit-transition:all 0.2s ease}.viewFramework-product-col-1 .viewFramework-product-navbar-bg,.viewFramework-product-col-1 .viewFramework-product-navbar{width:180px}.viewFramework-product-col-1 .viewFramework-product-body{left:180px}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse{left:160px}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .product-navbar-collapse{right:-7px;left:auto}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .product-navbar-collapse>span{color:#546478}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .product-navbar-collapse-bg{right:0;left:auto;border-bottom:9px solid transparent;border-left:none;border-right:13px solid #f7f7f7;border-top:9px solid transparent}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .icon-collapse-left{display:inline}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .icon-collapse-right{display:none}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse:hover .product-navbar-collapse{right:0;left:auto}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse:hover .product-navbar-collapse-bg{border-bottom:8px solid transparent;border-left:none;border-right:20px solid #f7f7f7;border-top:8px solid transparent}.viewFramework-product-col-2 .viewFramework-product-navbar-bg,.viewFramework-product-col-2 .viewFramework-product-navbar{width:360px}.viewFramework-product-col-2 .viewFramework-product-body{left:360px}.viewFramework-animate{-webkit-animation-duration:0.1s;animation-duration:0.1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.viewFramework-fadeIn{-webkit-animation-name:viewFrameworkFadeIn;animation-name:viewFrameworkFadeIn}@-webkit-keyframes viewFrameworkFadeIn{0%{opacity:0}100%{opacity:1}}@keyframes viewFrameworkFadeIn{0%{opacity:0}100%{opacity:1}}.text-muted{color:#999 !important}.text-muted:hover{color:#999 !important}.text-info{color:#69C !important}.text-info:hover{color:#69C !important}.text-primary{color:#09C !important}.text-primary:hover{color:#09C !important}.text-success{color:#090 !important}.text-success:hover{color:#090 !important}.text-warning{color:#F90 !important}.text-warning:hover{color:#F90 !important}.text-danger{color:#F00 !important}.text-danger:hover{color:#F00 !important}.text-explode{color:#CCC !important;font-weight:normal !important;margin:0px 4px !important}span.label{font-weight:normal}.text-size-14{font-size:14px !important}.text-size-16{font-size:16px !important}.text-size-24{font-size:24px !important}.text-size-32{font-size:32px !important}.text-size-48{font-size:48px !important}.text-size-64{font-size:64px !important}.btn{font-size:12px;border-radius:0px;padding:8px 16px;height:32px;line-height:14px}.btn-default{color:#333;border:1px solid #ddd;background-color:#f7f7f7}.btn-default:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-default:focus{color:#333;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn-default:hover{color:#333;border:1px solid #ddd;background-color:#fff}.btn-primary{color:#fff;border:1px solid #09c;background-color:#09c}.btn-primary:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-primary:focus{color:#fff;border:1px solid #09c;background-color:#09c;outline:none}.btn-primary:hover{color:#fff;border:1px solid #28b5d6;background-color:#28b5d6}.btn-success{color:#fff;border:1px solid #57a235;background-color:#4db118}.btn-success:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-success:focus{color:#fff;border:1px solid #57a235;background-color:#4db118;outline:none}.btn-success:hover{color:#fff;border:1px solid #57bc20;background-color:#57bc20}.btn-warning{color:#333;border:1px solid #ddd;background-color:#f7f7f7}.btn-warning:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-warning:focus{color:#333;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn-warning:hover{color:#fff;border:1px solid #ffa200;background-color:#ffa200}.btn-danger{color:#333;color:#333;border:1px solid #ddd;background-color:#f7f7f7}.btn-danger:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-danger:focus{color:#333;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn-danger:hover{color:#fff;border:1px solid #f25721;background-color:#f25721}.btn-link{color:#06C;text-shadow:none;border:none}.btn-link:hover{color:#039}.btn-lg{font-size:14px;padding:12px 20px;height:40px;line-height:16px}.btn-sm{font-size:12px;padding:4px 12px;height:24px;line-height:14px}.btn-xs{font-size:12px;padding:2px 8px;height:20px;line-height:14px}.btn.disabled,.btn[disabled]{text-shadow:none;filter:none;opacity:1;color:#bbb;border:1px solid #ddd;background-color:#f7f7f7}.btn.disabled:active,.btn[disabled]:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn.disabled:focus,.btn[disabled]:focus{color:#bbb;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn.disabled:hover,.btn[disabled]:hover{color:#bbb;border:1px solid #ddd;background-color:#f7f7f7}.btn.btn-link.disabled,.btn.btn-link[disabled]{border:none;background:transparent none}.btn.btn-primary.disabled,.btn.btn-primary[disabled]{color:#EEE;text-shadow:none;filter:none;opacity:1;color:#fff;border:1px solid #ccc;background-color:#ccc}.btn.btn-primary.disabled:active,.btn.btn-primary[disabled]:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn.btn-primary.disabled:focus,.btn.btn-primary[disabled]:focus{color:#fff;border:1px solid #ccc;background-color:#ccc;outline:none}.btn.btn-primary.disabled:hover,.btn.btn-primary[disabled]:hover{color:#fff;border:1px solid #ccc;background-color:#ccc}.btn-default-active,.btn-default-active:hover,.btn-default-active:focus{border:1px solid #485260;background-color:#525d6d;background:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #525d6d), color-stop(100%, #525d6d));background:-webkit-linear-gradient(top, #525d6d,#525d6d);background:-moz-linear-gradient(top, #525d6d,#525d6d);background:-o-linear-gradient(top, #525d6d,#525d6d);background:linear-gradient(top, #525d6d,#525d6d);color:#FFFFFF;box-shadow:inset 0px 1px 2px rgba(0,0,0,0.3)}.btn-toinstlist{border:1px solid #BBB;color:#666;text-shadow:none;vertical-align:middle;margin-top:7px}.btn-toinstlist .icon-toinstlist{width:12px;height:12px;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;background:url(images/toinstlist.png) center 1px no-repeat}.console-sub-title+.table{margin-top:0px}.table-header{border:1px solid #e1e6eb;width:100%;z-index:1;margin-bottom:-1px;padding:8px;line-height:32px;display:table}.table-header+.table{margin-top:0px}.table{background:#FFF;font-size:12px;border-top:1px solid #e1e6eb;margin-top:8px;border:1px solid #e1e6eb}.table thead tr th{padding:8px 8px;font-weight:normal;color:#999;border-bottom:1px solid #e1e6eb;background-color:#F5F6FA}.table thead tr th a.dropdown-toggle{color:#999}.table thead tr th .dropdown.open a{color:#333}.table tbody tr td{padding:12px 8px;border-top:0px;border-bottom:1px solid #e1e6eb;vertical-align:middle}.table tbody tr td p{margin-bottom:0px}.table tfoot tr td{padding:12px 8px;border-bottom:1px solid #e1e6eb;vertical-align:middle}.table .text-right .dropdown-menu{text-align:left;left:auto;right:0px}.table-hover tbody tr:hover td{background-color:#F9F9FA}.pagination{margin:0px;vertical-align:middle;border-radius:0px}.pagination li a,.pagination li span{height:32px;line-height:20px;color:#333;cursor:pointer;border-color:#CCC}.pagination li a:hover{color:#FFF;background-color:#28B5D6;border-color:#28B5D6}.pagination li span{color:#999}.pagination li span:hover{background:none}.pagination li:first-child>a,.pagination li:first-child>span{border-radius:0px}.pagination li:last-child>a,.pagination li:last-child>span{border-radius:0px}.pagination li.active a,.pagination li.active span{background-color:#09C;border:1px solid #09C}.pagination li.active a:hover,.pagination li.active span:hover{background-color:#09C;border:1px solid #09C}.pagination-info{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;padding:0px 16px;color:#888}.form-group{margin-top:8px;margin-bottom:16px}.help-block{margin:4px 0px}.form-control{height:32px;border-radius:0px;padding:6px;-webkit-transition:none;transition:none;font-size:12px}.form-control:focus{-webkit-box-shadow:none;box-shadow:none}.form-control[size],.form-control[cols],.form-control.autosize{width:auto}.form-control.inline{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline}select{color:#555555;vertical-align:middle;background-color:#ffffff;background-image:none;border:1px solid #cccccc}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#090}.has-success .form-control,input.ng-valid.ng-dirty,textarea.ng-valid.ng-dirty{border-color:#090}.has-success .form-control:focus,input.ng-valid.ng-dirty:focus,textarea.ng-valid.ng-dirty:focus{border-color:#2A0;-webkit-box-shadow:none;box-shadow:none}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#F90}.has-warning .form-control{border-color:#F90}.has-warning .form-control:focus{border-color:#FA0;-webkit-box-shadow:none;box-shadow:none}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#E40}.has-error .form-control,input.ng-invalid.ng-dirty,textarea.ng-invalid.ng-dirty{border-color:#E40}.has-error .form-control:focus,input.ng-invalid.ng-dirty:focus,textarea.ng-invalid.ng-dirty:focus{border-color:#F30;-webkit-box-shadow:none;box-shadow:none}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{color:#999}label.control-label{font-weight:normal;font-size:12px;color:#666}.form-inline .form-group{margin:4px 8px 4px 0px}.form-inline .form-control{width:auto}.form-inline .input-group-btn{width:auto}select.input-lg,.input-lg{height:40px}select.input-sm,.input-sm{height:24px}.console-onoff{vertical-align:middle;width:50px;height:20px;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;background-image:url("images/on-off.png");background-image:-webkit-image-set(url("images/on-off.png") 1x, url("images/on-off@2x.png") 2x);background-image:-moz-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-o-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-ms-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-repeat:no-repeat;background-position:0px 0px;cursor:pointer}.console-onoff .onoff-handle{display:block;width:50px;height:20px;-webkit-transition:background-position 0.2s ease;-moz-transition:background-position 0.2s ease;-o-transition:background-position 0.2s ease;transition:background-position 0.2s ease;background-image:url("images/on-off.png");background-image:-webkit-image-set(url("images/on-off.png") 1x, url("images/on-off@2x.png") 2x);background-image:-moz-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-o-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-ms-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-repeat:no-repeat;background-position:0px 0px}.console-onoff .onoff-loading{display:block;width:50px;height:20px;-webkit-transition:background-position 0.2s ease;-moz-transition:background-position 0.2s ease;-o-transition:background-position 0.2s ease;transition:background-position 0.2s ease;background-image:url("images/on-off-loading.gif");background-image:-webkit-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-image:-moz-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-image:-o-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-image:-ms-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-repeat:no-repeat;background-position:0px 0px}.console-onoff-on{background-position:0px -40px}.console-onoff-on .onoff-handle{background-position:0px 0px}.console-onoff-on .onoff-loading{background-position:32px 4px}.console-onoff-off{background-position:0px -60px}.console-onoff-off .onoff-handle{background-position:-28px 0px}.console-onoff-off .onoff-loading{background-position:4px 4px}.console-onoff[disabled="disabled"]{cursor:not-allowed;background-position:0px -80px}.console-onoff[disabled="disabled"] .onoff-loading{display:none}.console-onoff-on[disabled="disabled"] .onoff-handle{background-position:0px -20px}.console-onoff-off[disabled="disabled"] .onoff-handle{background-position:-28px -20px}.console-number-spinner{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;vertical-align:middle}.console-number-spinner .form-control{width:auto;float:left;text-indent:-16px}.console-number-spinner .console-number-spinner-action{width:14px;height:30px;float:left;margin-left:-16px;border-left:1px solid #E3E3E3;margin-top:1px}.console-number-spinner .console-number-spinner-action button{width:14px;height:15px;overflow:hidden;line-height:16px;font-size:12px;border:0px;background-color:transparent;padding:0px;margin:0px;display:block;color:#999;text-align:center;outline:0px}.console-number-spinner .console-number-spinner-action button:hover{color:#06C}.console-number-spinner .console-number-spinner-action button[disabled]{color:#999}.console-number-spinner .console-number-spinner-action .console-number-spinner-down{border-top:1px solid #E3E3E3}.console-timepicker{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;vertical-align:middle}.console-datepicker{padding:8px}.console-datepicker thead .h6 th{padding-top:8px}.console-datepicker tbody tr:first-child td{padding-top:8px}.console-datepicker tbody .btn{border:0px !important}.console-datepicker tbody .btn:hover{background:#F3F3F3}.console-datepicker tbody .btn-default{background:transparent}.console-datepicker tbody .active,.console-datepicker tbody .active:hover,.console-datepicker tbody .active span{background:#3C0;color:#FFF}.console-datepicker tbody .btn[disabled="disabled"] .btn[disabled="disabled"] span{color:#CCC}.console-datepicker em{font-size:12px;color:#ACD}.aliyun-console-topbar{position:relative;z-index:100;clear:both;height:50px;background:#09C;font-size:12px;min-width:990px}.aliyun-console-topbar a{text-decoration:none}.aliyun-console-topbar a:focus{outline:none}.aliyun-console-topbar .accessibility-ast{position:absolute;top:-10000px;left:-10000px;width:100px}.aliyun-console-topbar .accessibility-ast:focus{position:absolute;top:0;left:310px}.aliyun-console-topbar .icon-arrow-down{display:inline-block;width:18px;text-align:center;vertical-align:middle;transition:transform 0.2s, vertical-align 0.2s;-o-transition:transform 0.2s, vertical-align 0.2s;-ms-transition:transform 0.2s, vertical-align 0.2s;-moz-transition:transform 0.2s, vertical-align 0.2s;-webkit-transition:transform 0.2s, vertical-align 0.2s}.aliyun-console-topbar .dropdown .dropdown-menu{z-index:1;font-size:12px;border-radius:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.aliyun-console-topbar .dropdown .dropdown-menu a{padding:0}.aliyun-console-topbar .dropdown.open .icon-arrow-down{vertical-align:text-top;transform:rotate(180deg);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg)}.aliyun-console-topbar .topbar-wrap,.aliyun-console-topbar .topbar-logo,.aliyun-console-topbar .topbar-home,.aliyun-console-topbar .topbar-home-link,.aliyun-console-topbar .topbar-nav,.aliyun-console-topbar .topbar-info{height:100%}.aliyun-console-topbar .topbar-left{float:left}.aliyun-console-topbar .topbar-right{float:right}.aliyun-console-topbar .topbar-clearfix:before,.aliyun-console-topbar .topbar-clearfix:after{display:table;content:" "}.aliyun-console-topbar .topbar-clearfix:after{clear:both}.aliyun-console-topbar .topbar-head{background:#008fbf;height:50px;position:relative;z-index:3}.aliyun-console-topbar .topbar-nav{position:relative;z-index:2;background:#09C}.aliyun-console-topbar .topbar-logo,.aliyun-console-topbar .topbar-home{display:block;width:50px;background:#0099cc;font-size:28px;color:#FFF;text-align:center;line-height:50px}.aliyun-console-topbar .topbar-logo span,.aliyun-console-topbar .topbar-home span{line-height:50px}.aliyun-console-topbar .topbar-logo{background:#0087b4}.aliyun-console-topbar .topbar-home{margin-right:1px;font-size:20px}.aliyun-console-topbar .topbar-home:hover{background:#008fbf}.aliyun-console-topbar .topbar-home-link{padding:0 20px;margin-right:1px;background:#09c}.aliyun-console-topbar .topbar-home{-o-transition:all 0.15s, 0.15s;-ms-transition:all 0.15s, 0.15s;-moz-transition:all 0.15s, 0.15s;-webkit-transition:all 0.15s, 0.15s}.aliyun-console-topbar .topbar-btn{color:#fff;font-size:14px;line-height:50px}.aliyun-console-topbar .topbar-btn:hover,.aliyun-console-topbar .topbar-btn.topbar-btn-dark{background:#008fbf}.aliyun-console-topbar .topbar-nav.open .topbar-nav-btn{background:#fff;color:#333}.aliyun-console-topbar .topbar-nav-btn{padding:0 20px;display:inline-block;height:50px}.aliyun-console-topbar .topbar-nav-list{border:none;padding:10px;margin-top:0;white-space:nowrap}.aliyun-console-topbar .topbar-nav-list .topbar-nav-col{display:inline-block;vertical-align:top;padding:0 10px}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item .topbar-nav-item-title{margin:3px 0px;color:#999;font-weight:600}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item .topbar-nav-gap{border-top:1px solid #f2f2f2;width:100%;margin:6px 0 10px}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul{padding:0;margin:8px 0 0 0;list-style:none}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li{min-width:160px;height:28px;line-height:28px;margin-bottom:2px}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li a{display:block;height:100%;padding:0 10px;text-decoration:none;color:#333}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li a:hover{background-color:#f2f2f2}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li a .topbar-nav-item-icon{padding-right:2px;font-size:16px;vertical-align:text-bottom;display:inline-block}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li.topbar-unservice a{color:#999}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li.topbar-unservice a .topbar-nav-item-icon{color:#999}.aliyun-console-topbar .topbar-info{background:#008fbf;position:absolute;z-index:1;top:0;right:0}.aliyun-console-topbar .topbar-info .topbar-btn{padding:0 10px;height:50px;display:block;z-index:2;background:#09c}.aliyun-console-topbar .topbar-info .topbar-btn:hover,.aliyun-console-topbar .topbar-info .topbar-btn.topbar-btn-dark{background:#008fbf}.aliyun-console-topbar .topbar-info .topbar-btn.open{position:relative}.aliyun-console-topbar .topbar-info .topbar-btn-search{padding:0;margin-left:1px}.aliyun-console-topbar .topbar-info .topbar-info-gap{color:#fff}.aliyun-console-topbar .topbar-info .dropdown .dropdown-menu{width:100%;min-width:0;margin:0;border:none}.aliyun-console-topbar .topbar-info .dropdown.open .topbar-btn{color:#333;background:#fff;border-bottom:1px solid #eaedf1;position:relative}.aliyun-console-topbar .topbar-info .topbar-info-btn{height:40px;border-bottom:1px solid #eaedf1}.aliyun-console-topbar .topbar-info .topbar-info-btn a{line-height:39px;padding-left:10px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu{width:310px;left:auto;right:0}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity{height:80px;padding:8px 16px;position:relative}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity .user-identity-item{height:32px;line-height:32px;display:block}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity .user-identity-colon{padding:0 5px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity-sign{padding:2px 6px;background:#7ecef4;color:#fff;border-radius:1px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity-sign-wrap{position:absolute;top:14px;right:30px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-btn-link{display:inline-block;color:#06C}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-btn-link:hover{background:none;text-decoration:underline}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-btn-link.user-btn-link-signout{float:right;padding:0 16px}.aliyun-console-topbar .topbar-info-item{display:inline-block;margin-left:1px}.aliyun-console-topbar .topbar-notice{position:relative;font-size:12px;margin-left:1px;padding:0 12px 0 8px !important}.aliyun-console-topbar .topbar-notice .topbar-notice-panel{display:none}.aliyun-console-topbar .topbar-notice.open .topbar-notice-panel{display:block}.aliyun-console-topbar .topbar-notice .topbar-notice-panel{position:absolute;top:48px;left:-185px;width:440px;border-radius:2px;z-index:15;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.175);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.175);box-shadow:0 1px 2px rgba(0,0,0,0.175)}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-arrow{background:url(images/notice-arrow.png) 0 0 no-repeat;width:11px;height:6px;position:absolute;top:-6px;left:220px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-head{height:50px;background-color:#eaedf1;padding:0 15px;line-height:50px;color:#333;font-size:14px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body{height:300px;overflow-y:auto;background:#fff;font-size:12px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul{list-style:none;margin:0;padding:0}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li{height:60px;line-height:20px;border-bottom:1px solid #eaedf1}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li a{display:block;height:100%;padding:10px 10px;background:#fff;color:#333}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li a .topbar-notice-link{display:block;max-width:300px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:#06c}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li a:hover{background:#f9f9f9}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li.topbar-notice-readed a{color:#666}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li.topbar-notice-readed a .topbar-notice-time{color:#999}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body .topbar-notice-empty{text-align:center;color:#666;margin-top:80px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-foot{height:50px;line-height:50px;background:#fff;text-align:center}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-class{padding:8px 0;float:right}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-class .topbar-notice-class-name{display:block;height:24px;line-height:24px;width:66px;background:#eaedf1;text-align:center;border-radius:3px}.aliyun-console-topbar .topbar-btn-notice{width:auto;display:block;height:50px}.aliyun-console-topbar .topbar-btn-notice .topbar-btn-notice-icon{font-size:24px;line-height:50px;vertical-align:text-bottom;color:#fff}.aliyun-console-topbar .topbar-btn-notice .topbar-btn-notice-num{font-size:12px;color:#fff;background:#ff9900;border-radius:5px;padding:2px 5px;display:inline-block;margin-top:15px;line-height:16px;vertical-align:top;text-align:center}.aliyun-console-topbar .topbar-btn-notice .topbar-btn-notice-num.topbar-btn-notice-num-zero{color:#00ace9;background-color:#0087b4}.aliyun-console-topbar .topbar-btn-notice .topbar-nav-item-short{padding-left:2px}.aliyun-console-topbar .topbar-qrcode{position:relative;margin-left:1px}.aliyun-console-topbar .topbar-qrcode:hover .topbar-qrcode-panel{display:block}.aliyun-console-topbar .topbar-qrcode .topbar-qrcode-panel{top:50px;left:0;position:absolute;width:130px;padding:12px 8px;background:#fff;border:1px solid #eaedf1;box-shadow:0 1px 3px rgba(0,0,0,0.1);display:none}.aliyun-console-topbar .topbar-qrcode .topbar-qrcode-image{width:100px;margin:0 auto}.aliyun-console-topbar .topbar-qrcode .topbar-qrcode-title{text-align:center;padding-top:10px}.aliyun-console-topbar .topbar-new-icon{position:relative;top:-4px;padding-left:2px}.aliyun-console-topbar-search{position:relative;z-index:1}.aliyun-console-topbar-search:hover{background:#008fbf}.aliyun-console-topbar-search:hover .topbar-search-ask{background:#008fbf}.aliyun-console-topbar-search .topbar-search-ask{width:200px;height:50px;border:0;background:#09c;line-height:26px;padding:12px 30px 12px 10px;display:block;color:#fff;-webkit-border-radius:1px 1px;-moz-border-radius:1px / 1px;border-radius:1px / 1px;-o-transition:all 0.15s, 0.15s;-ms-transition:all 0.15s, 0.15s;-moz-transition:all 0.15s, 0.15s;-webkit-transition:all 0.15s, 0.15s}.aliyun-console-topbar-search .topbar-search-ask:focus{outline:none}.aliyun-console-topbar-search .topbar-search-ask-shade{color:#00ace9}.aliyun-console-topbar-search .topbar-search-mark{font-size:16px;line-height:50px;position:absolute;height:50px;width:40px;color:#fff;text-decoration:none;display:block;text-align:center;top:0;right:0}.aliyun-console-topbar-search .topbar-search-mark .icon-search,.aliyun-console-topbar-search .topbar-search-mark .icon-enter{line-height:50px}.aliyun-console-topbar-search.topbar-search-active{background:#008fbf}.aliyun-console-topbar-search.topbar-search-active .topbar-search-ask{background:#008fbf}.aliyun-console-topbar-search.topbar-search-active .topbar-search-ask-shade{color:#fff}.aliyun-console-topbar-search-v1_3_21{position:relative}.aliyun-console-topbar-search-v1_3_21.topbar-search-dropdown-open .topbar-btn{background:#008fbf}.aliyun-console-topbar-search-v1_3_21 .icon-search{font-size:16px;padding-right:4px;position:relative;top:2px}.aliyun-console-topbar-search-v1_3_21 .topbar-search-dropdown{height:38px;position:absolute;bottom:-38px;right:-1px;border:2px solid #008fbf;background:#fff}.aliyun-console-topbar-search-v1_3_21 .topbar-search-dropdown input{display:block;height:34px;padding:4px 6px;margin-right:30px;width:250px;border-width:0;outline:0;line-height:34px;color:#546478;font-size:12px}.aliyun-console-topbar-search-v1_3_21 .topbar-search-dropdown .topbar-search-mark{position:absolute;right:0;top:0;height:34px;width:34px;display:block;line-height:34px;text-align:center;color:#546478}.aliyun-console-topbar-help{position:fixed;top:0;right:0;bottom:0}.aliyun-console-topbar-help .topbar-help-inner{width:486px;overflow:hidden;background:#fff;border-left:1px solid #e1e6eb;position:absolute;right:-486px;-webkit-box-shadow:0 0px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 0px 10px rgba(0,0,0,0.1);box-shadow:0 0px 10px rgba(0,0,0,0.1);-o-transition:all 0.2s ease, 0.2s ease;-ms-transition:all 0.2s ease, 0.2s ease;-moz-transition:all 0.2s ease, 0.2s ease;-webkit-transition:all 0.2s ease, 0.2s ease;z-index:1;top:50px;bottom:0}.aliyun-console-topbar-help .topbar-help-inner.topbar-help-show{right:0px}.aliyun-console-topbar-help .topbar-help-head{height:68px;padding-left:20px;line-height:68px;border-bottom:1px solid #e1e6eb;position:relative;color:#333}.aliyun-console-topbar-help .topbar-help-body{position:absolute;top:68px;bottom:0;background:#fff}.aliyun-console-topbar-help .topbar-help-iframe{height:100%}.aliyun-console-topbar-help .topbar-help-close{font-size:18px;float:right;height:68px;width:68px;line-height:68px !important;text-align:center;color:#546478;cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.aliyun-console-topbar-help .topbar-help-close:hover{color:#000}.console-topbar-new{position:relative;z-index:100;clear:both;height:40px;background:#34383c;font-size:12px;min-width:1000px}.console-topbar-new .console-topbar-btn{width:50px;height:40px;display:inline-block;vertical-align:middle;margin-right:1px;background-color:#2a2e31;text-decoration:none;text-align:center;color:#fff;line-height:40px;-o-transition:all 0.3s;-ms-transition:all 0.3s;-moz-transition:all 0.3s;-webkit-transition:all 0.3s}.console-topbar-new .console-topbar-btn .console-topbar-btn-text{font-size:14px;text-align:center;white-space:nowrap;display:none}.console-topbar-new .console-topbar-btn .console-topbar-btn-icon{font-size:22px;display:inline;line-height:40px;color:#fff}.console-topbar-new .console-topbar-btn .caret{-o-transition:-o-transform 0.3s;-ms-transition:-ms-transform 0.3s;-moz-transition:-moz-transform 0.3s;-webkit-transition:-webkit-transform 0.3s;transition:transform 0.3s}.console-topbar-new .console-topbar-btn:hover{width:auto}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-inverse,.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-inverse-white{background-color:#585e65;color:#fff}.console-topbar-new .console-topbar-btn:hover .console-topbar-btn-text{display:inline}.console-topbar-new .console-topbar-btn:hover .console-topbar-btn-icon{display:none;vertical-align:text-bottom}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-home{width:106px}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-nav{width:120px}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-ak{width:104px}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-workorder{width:94px}.console-topbar-new .console-topbar-btn.console-topbar-btn-last{margin-right:0}.console-topbar-new .console-topbar-btn.console-topbar-logo-icon{-o-transition:none;-ms-transition:none;-moz-transition:none;-webkit-transition:none;color:#2a2e31}.console-topbar-new .console-topbar-btn.console-topbar-logo-icon img{width:22px;height:22px;display:block;margin:9px 14px}.console-topbar-new .console-topbar-btn.console-topbar-nav-link{font-size:12px;width:auto;padding:0 15px}.console-topbar-new .console-topbar-btn.console-topbar-nav-link .console-topbar-nav-link-icon{width:16px;height:19px;vertical-align:middle;position:relative;display:inline-block;margin-right:4px;overflow:hidden;font-size:16px}.console-topbar-new .console-topbar-btn.console-topbar-btn-user{width:auto}.console-topbar-new .console-topbar-btn.console-topbar-btn-user .console-topbar-btn-text{display:inline;padding:0 15px}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice{width:auto;padding:0 10px}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-btn-notice-icon{font-size:24px;line-height:40px;vertical-align:text-bottom}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-btn-notice-num{font-size:12px;color:#fff;background:#ff9900;border-radius:5px;padding:2px 1px;width:20px;display:inline-block;margin-top:10px;line-height:16px;vertical-align:top}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-btn-notice-num.console-topbar-btn-notice-num-zero{color:#999;background-color:#34383c}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-nav-item-short{padding-left:2px}.console-topbar-new .console-topbar-btn.console-topbar-btn-ak{overflow:hidden}.console-topbar-new .console-topbar-btn.console-topbar-btn-nav{overflow:hidden;position:relative;z-index:2}.console-topbar-new .console-topbar-nav .console-topbar-nav-list{border:1px solid #ddd;border-top:none;padding:10px;margin-top:0;margin-left:-1px}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-col{float:left;padding:0 10px}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item .console-topbar-nav-item-title{margin:3px 0px;color:#999;font-weight:600}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item .console-topbar-nav-gap{border-top:1px solid #f2f2f2;width:100%;margin:10px 0}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul{padding:0;margin:10px 0 0 0;list-style:none}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li{width:170px;height:30px;line-height:30px;margin-bottom:2px}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a{display:block;height:100%;padding-left:10px;text-decoration:none;color:#333}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a:hover{background-color:#f2f2f2}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon{padding-right:2px;font-size:16px;vertical-align:text-bottom}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ecs{color:#007eff}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-slb{color:#f27741}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-rds{color:#20f8b8}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-oss{color:#ade675}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-cdn{color:#bff3fe}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ots{color:#15d4f0}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ocs{color:#40ff8f}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-odps{color:#ffba00}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ace{color:#c8341c}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-yundun{color:#298edb}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-yunjiankong{color:#86f2af}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-sls{color:#075ac0}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-oas{color:#79df71}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ess{color:#0cf}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-mqs{color:#fff400}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-vpc{color:#6cf}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-opensearch{color:#5bc8e8}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-lightcloud{color:#6bbd52}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-pts{color:#009dff}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ons{color:#6b3100}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-dpc{color:#289de9}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ads{color:#71ceec}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-mts{color:#f93}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-drds{color:#6f9}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li.console-topbar-unservice a{color:#999}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li.console-topbar-unservice a .console-topbar-nav-item-icon{color:#999}.console-topbar-new .dropdown .dropdown-menu{z-index:1;border-radius:0;-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.175);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.175);box-shadow:0 2px 4px rgba(0,0,0,0.175)}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-inverse{background-color:#585e65;color:#fff}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-inverse-white{background-color:#fff;color:#333}.console-topbar-new .dropdown.open .console-topbar-btn .console-topbar-btn-text{display:inline}.console-topbar-new .dropdown.open .console-topbar-btn .console-topbar-btn-icon{display:none;vertical-align:text-bottom}.console-topbar-new .dropdown.open .console-topbar-btn:hover.console-topbar-btn-inverse-white{background-color:#fff !important;color:#333 !important}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-nav{width:120px}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-workorder{width:94px}.console-topbar-new .dropdown.open .console-topbar-btn .caret{transform:rotate(180deg);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg)}.console-topbar-new .console-topbar-user .dropdown-menu,.console-topbar-new .console-topbar-dropdown .dropdown-menu{margin-top:-1px}.console-topbar-new .console-topbar-user .dropdown-menu>li a,.console-topbar-new .console-topbar-dropdown .dropdown-menu>li a{padding:6px 20px}.console-topbar-new .console-topbar-workorder .dropdown-menu{min-width:96px}.console-topbar-new .console-topbar-workorder .dropdown-menu>li a{padding:6px 24px 6px 16px}.console-topbar-new .console-topbar-notice{position:relative}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel{display:none}.console-topbar-new .console-topbar-notice.open .console-topbar-notice-panel{display:block}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel{position:absolute;top:38px;left:-170px;width:390px;border:1px solid #bbb;border-radius:2px;z-index:15;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.175);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.175);box-shadow:0 1px 2px rgba(0,0,0,0.175)}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-arrow{background:url(images/notice-arrow.png) 0 0 no-repeat;width:11px;height:6px;position:absolute;top:-6px;left:196px}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-head{height:40px;border-bottom:1px solid #ccc;background-color:#f2f2f2;padding:0 15px;line-height:40px;color:#333;font-size:14px}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body{height:240px;overflow-y:auto;background:#fff}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul{list-style:none;margin:0 5px;padding:0}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li{height:40px;line-height:40px;border-bottom:1px solid #ececec}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a{display:block;height:100%;padding:0 10px;background:#fff}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a span{display:block}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a .console-topbar-notice-link{float:left;max-width:272px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a .console-topbar-notice-time{float:right;color:#333}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a:hover{background:#f9f9f9}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li.console-topbar-notice-readed a{color:#666}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li.console-topbar-notice-readed a .console-topbar-notice-time{color:#999}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body .console-topbar-notice-empty{text-align:center;color:#666;margin-top:80px}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-foot{height:48px;line-height:48px;background:#fff}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-foot .console-topbar-notice-more{padding-right:15px}.console-topbar-new .console-topbar-locale{float:left}.console-topbar-new .console-topbar-locale .dropdown-menu{right:0;left:auto;margin-top:-1px;width:50px;min-width:60px}.console-topbar-new .console-topbar-locale .console-topbar-btn.console-topbar-btn-locale{width:60px;background:none}.console-topbar-new .console-topbar-locale .console-topbar-btn.console-topbar-btn-locale .console-topbar-btn-text{display:block}.console-topbar-new.console-topbar-new-en .console-topbar-btn:hover.console-topbar-btn-home{width:116px}.console-topbar-new.console-topbar-new-en .console-topbar-btn:hover.console-topbar-btn-nav{width:170px}.console-topbar-new.console-topbar-new-en .console-topbar-btn:hover.console-topbar-btn-workorder{width:146px}.console-topbar-new.console-topbar-new-en .console-topbar-nav .console-topbar-nav-item ul li{width:auto !important}.console-topbar-new.console-topbar-new-en .console-topbar-nav .console-topbar-nav-item ul li a{padding:0 10px}.console-topbar-new.console-topbar-new-en .dropdown.open .console-topbar-btn.console-topbar-btn-nav{width:170px}.console-topbar-new.console-topbar-new-en .dropdown.open .console-topbar-btn.console-topbar-btn-workorder{width:146px}.console-navbar{font-size:12px;color:#666666;word-wrap:break-word;height:56px;border:none;border-bottom:1px solid #dddddd;box-shadow:0px 0px 4px rgba(0,0,0,0.1);position:relative;box-sizing:content-box;margin-bottom:0px;background-color:#fff;border-radius:0 !important;z-index:2}.console-navbar *{box-sizing:content-box}.console-navbar a{color:#333}.console-navbar .console-navbar-title{float:left;line-height:56px;padding:0 40px 0 14px;font-size:18px;color:#999999}.console-navbar .console-navbar-title .console-navbar-subtitle{margin-right:5px}.console-navbar .nav li{float:left;display:inline;margin:0 20px;height:56px;font-size:14px}.console-navbar .nav li a{padding:0 2px;float:left;height:55px;color:#333333;line-height:56px;text-decoration:none}.console-navbar .nav li a:hover,.console-navbar .nav li a:focus{background-color:#fff}.console-navbar .nav li.active{height:55px}.console-navbar .nav li.active a{color:#ff4902;border-bottom:2px solid #ff4902}.console-navbar .console-navbar-a-default{cursor:default}.console-navbar .console-navbar-links-example{margin-top:15px;padding:0 15px 0;line-height:24px;border-left:1px solid #eeeeee}.console-navbar .console-navbar-links-example a{color:#b3b3b3}.console-title{padding:16px 0px;min-height:70px}.console-title .nav-pills{display:inline-block;vertical-align:bottom}.console-title .nav-pills li a,.console-title .nav-pills li a:focus,.console-title .nav-pills li button,.console-title .nav-pills li button:focus{padding:6px 6px}.console-title h1,.console-title h2,.console-title h3,.console-title h4,.console-title h5,.console-title h6{display:inline-block;text-indent:8px;border-left:2px solid #88B7E0;margin-top:0px;margin-bottom:0px;margin-right:8px}.console-title h1{margin-top:0px;margin-bottom:0px}.console-title h2{margin-top:2px;margin-bottom:2px}.console-title h3{margin-top:4px;margin-bottom:4px}.console-title h4{margin-top:6px;margin-bottom:6px}.console-title h5{margin-top:8px;margin-bottom:8px}.console-title-border{border-bottom:1px solid #DDD}.console-sub-title{position:relative;padding-left:16px;margin-bottom:-1px;display:table;width:100%;z-index:1;height:40px;border:1px solid #E1E6EB;border-left:3px solid #778;background-color:#F4F5F9}.console-sub-title h5{color:#666;font-size:14px}.console-box-border{border:1px solid #E1E6EB}.margin-left,.margin-left-1{margin-left:8px !important}.margin-left-2{margin-left:16px !important}.margin-left-3{margin-left:24px !important}.margin-left-4{margin-left:32px !important}.margin-right,.margin-right-1{margin-right:8px !important}.margin-right-2{margin-right:16px !important}.margin-right-3{margin-right:24px !important}.margin-right-4{margin-right:32px !important}.margin-top,.margin-top-1{margin-top:8px !important}.margin-top-2{margin-top:16px !important}.margin-top-3{margin-top:24px !important}.margin-top-4{margin-top:32px !important}.row-padding-1{padding-top:8px;padding-bottom:8px}.row-padding,.row-padding-2{padding-top:16px;padding-bottom:16px}.row-padding-3{padding-top:24px;padding-bottom:24px}.row-padding-4{padding-top:32px;padding-bottom:32px}.row-margin-1{margin-top:8px;margin-bottom:8px}.row-margin,.row-margin-2{margin-top:16px;margin-bottom:16px}.row-margin-3{margin-top:24px;margin-bottom:24px}.row-margin-4{margin-top:32px;margin-bottom:32px}.col-padding-1{padding-left:8px;padding-right:8px}.col-padding,.col-padding-2{padding-left:16px;padding-right:16px}.col-padding-3{padding-left:24px;padding-right:24px}.col-padding-4{padding-left:32px;padding-right:32px}.col-margin-1{margin-left:8px;margin-right:8px}.col-margin,.col-margin-2{margin-left:16px;margin-right:16px}.col-margin-3{margin-left:24px;margin-right:24px}.col-margin-4{margin-left:32px;margin-right:32px}.inline-block{display:inline-block !important;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline}.partition{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;padding:0px 4px}.no-data{padding:24px 0px;text-align:center;color:#666}@font-face{font-family:'aliyun-console-font';src:url("fonts/aliyun-console-font.eot?t91au5");src:url("fonts/aliyun-console-font.eot?t91au5#iefix") format("embedded-opentype"),url("fonts/aliyun-console-font.ttf?t91au5") format("truetype"),url("fonts/aliyun-console-font.woff?t91au5") format("woff"),url("fonts/aliyun-console-font.svg?t91au5#aliyun-console-font") format("svg");font-weight:normal;font-style:normal}[class^="icon-"],[class*=" icon-"]{font-family:'aliyun-console-font' !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-logo2:before{content:"\e63b"}.icon-logo1:before{content:"\e63a"}.icon-logo-new:before{content:"\e97f"}.icon-dms-2:before{content:"\e92d"}.icon-dms-3:before{content:"\e92e"}.icon-dms:before{content:"\e92f"}.icon-gpdb-2:before{content:"\e983"}.icon-gpdb-3:before{content:"\e984"}.icon-gpdb:before{content:"\e985"}.icon-schedulerx-2:before{content:"\e986"}.icon-schedulerx-3:before{content:"\e987"}.icon-schedulerx:before{content:"\e988"}.icon-txc-2:before{content:"\e989"}.icon-txc-3:before{content:"\e98a"}.icon-txc:before{content:"\e98b"}.icon-csb-2:before{content:"\e909"}.icon-csb-3:before{content:"\e90a"}.icon-csb:before{content:"\e90b"}.icon-mobsec-2:before{content:"\e96d"}.icon-mobsec-3:before{content:"\e96e"}.icon-mobsec:before{content:"\e96f"}.icon-mss-2:before{content:"\e970"}.icon-mss-3:before{content:"\e971"}.icon-mss:before{content:"\e972"}.icon-sos-2:before{content:"\e973"}.icon-sos-3:before{content:"\e974"}.icon-sos:before{content:"\e975"}.icon-sppc-2:before{content:"\e976"}.icon-sppc-3:before{content:"\e977"}.icon-sppc:before{content:"\e978"}.icon-webfirewall-2:before{content:"\e979"}.icon-webfirewall-3:before{content:"\e97a"}.icon-webfirewall:before{content:"\e97b"}.icon-xianzhi-2:before{content:"\e97c"}.icon-xianzhi-3:before{content:"\e97d"}.icon-xianzhi:before{content:"\e97e"}.icon-livevideo-2:before{content:"\e964"}.icon-livevideo-3:before{content:"\e965"}.icon-livevideo:before{content:"\e966"}.icon-slm-2:before{content:"\e967"}.icon-slm-3:before{content:"\e968"}.icon-slm:before{content:"\e969"}.icon-vod-2:before{content:"\e96a"}.icon-vod-3:before{content:"\e96b"}.icon-vod:before{content:"\e96c"}.icon-kms-2:before{content:"\e95e"}.icon-kms-3:before{content:"\e95f"}.icon-kms:before{content:"\e960"}.icon-nas-2:before{content:"\e961"}.icon-nas-3:before{content:"\e962"}.icon-nas:before{content:"\e963"}.icon-apigateway-2:before{content:"\e94f"}.icon-apigateway-3:before{content:"\e950"}.icon-apigateway:before{content:"\e951"}.icon-oceanbase-2:before{content:"\e952"}.icon-oceanbase-3:before{content:"\e953"}.icon-oceanbase:before{content:"\e954"}.icon-petadata-2:before{content:"\e955"}.icon-petadata-3:before{content:"\e956"}.icon-petadata:before{content:"\e957"}.icon-ecsm-2:before{content:"\e958"}.icon-ecsm-3:before{content:"\e959"}.icon-ecsm:before{content:"\e95a"}.icon-yundunzhengshu-2:before{content:"\e95b"}.icon-yundunzhengshu-3:before{content:"\e95c"}.icon-yundunzhengshu:before{content:"\e95d"}.icon-cdi-2:before{content:"\e93a"}.icon-cdi-3:before{content:"\e93b"}.icon-cdi:before{content:"\e93c"}.icon-disk-2:before{content:"\e93d"}.icon-disk-3:before{content:"\e93e"}.icon-disk:before{content:"\e93f"}.icon-dsi-2:before{content:"\e940"}.icon-dsi-3:before{content:"\e941"}.icon-dsi:before{content:"\e942"}.icon-hpc-2:before{content:"\e943"}.icon-hpc-3:before{content:"\e944"}.icon-hpc:before{content:"\e945"}.icon-httpdns-2:before{content:"\e946"}.icon-httpdns-3:before{content:"\e947"}.icon-httpdns:before{content:"\e948"}.icon-iot-2:before{content:"\e949"}.icon-iot-3:before{content:"\e94a"}.icon-iot2:before{content:"\e94b"}.icon-vipaegis-2:before{content:"\e94c"}.icon-vipaegis-3:before{content:"\e94d"}.icon-vipaegis:before{content:"\e94e"}.icon-cs-2:before{content:"\e92a"}.icon-cs-3:before{content:"\e92b"}.icon-cs:before{content:"\e92c"}.icon-ewh-2:before{content:"\e930"}.icon-ewh-3:before{content:"\e931"}.icon-ewh:before{content:"\e932"}.icon-expressconnect-2:before{content:"\e933"}.icon-expressconnect-3:before{content:"\e934"}.icon-expressconnect:before{content:"\e935"}.icon-hsm-2:before{content:"\e936"}.icon-hsm-3:before{content:"\e937"}.icon-hsm:before{content:"\e938"}.icon-kuaizhaolian:before{content:"\e939"}.icon-mongodb-2:before{content:"\e927"}.icon-mongodb-3:before{content:"\e928"}.icon-mongodb:before{content:"\e929"}.icon-actiontrail-2:before{content:"\e90f"}.icon-actiontrail-3:before{content:"\e910"}.icon-actiontrail:before{content:"\e911"}.icon-ats-2:before{content:"\e912"}.icon-ats-3:before{content:"\e913"}.icon-ats:before{content:"\e914"}.icon-cli-2:before{content:"\e915"}.icon-cli-3:before{content:"\e916"}.icon-cli:before{content:"\e917"}.icon-directmail-2:before{content:"\e918"}.icon-directmail-3:before{content:"\e919"}.icon-directmail:before{content:"\e91a"}.icon-eclipse-2:before{content:"\e91b"}.icon-eclipse-3:before{content:"\e91c"}.icon-eclipse:before{content:"\e91d"}.icon-havip-2:before{content:"\e91e"}.icon-havip-3:before{content:"\e91f"}.icon-havip:before{content:"\e920"}.icon-ros-2:before{content:"\e921"}.icon-ros-3:before{content:"\e922"}.icon-ros:before{content:"\e923"}.icon-visualstudio-2:before{content:"\e924"}.icon-visualstudio-3:before{content:"\e925"}.icon-visualstudio:before{content:"\e926"}.icon-emr-2:before{content:"\e90c"}.icon-emr-3:before{content:"\e90d"}.icon-emr:before{content:"\e90e"}.icon-antifraud-3:before{content:"\e903"}.icon-antifraud:before{content:"\e904"}.icon-antifraud-2:before{content:"\e905"}.icon-ddosbasic:before{content:"\e906"}.icon-ddosbasic-3:before{content:"\e907"}.icon-ddosbasic-2:before{content:"\e908"}.icon-aegis:before{content:"\e900"}.icon-aegis-3:before{content:"\e901"}.icon-aegis-2:before{content:"\e902"}.icon-amr-2:before{content:"\e71c"}.icon-amr-3:before{content:"\e71d"}.icon-amr:before{content:"\e71e"}.icon-eip-2:before{content:"\e71f"}.icon-eip-3:before{content:"\e720"}.icon-eip:before{content:"\e721"}.icon-expense-i18n:before{content:"\e71b"}.icon-aps-2:before{content:"\e715"}.icon-aps-3:before{content:"\e716"}.icon-aps:before{content:"\e717"}.icon-batchcompute-2:before{content:"\e718"}.icon-batchcompute-3:before{content:"\e719"}.icon-batchcompute:before{content:"\e71a"}.icon-sas-2:before{content:"\e70c"}.icon-sas-3:before{content:"\e70d"}.icon-sas:before{content:"\e70e"}.icon-scan-2:before{content:"\e70f"}.icon-scan-3:before{content:"\e710"}.icon-scan:before{content:"\e711"}.icon-waf-2:before{content:"\e712"}.icon-waf-3:before{content:"\e713"}.icon-waf:before{content:"\e714"}.icon-mns-2:before{content:"\e709"}.icon-mns-3:before{content:"\e70a"}.icon-mns:before{content:"\e70b"}.icon-qrcode:before{content:"\e708"}.icon-unfold:before{content:"\e707"}.icon-fold:before{content:"\e706"}.icon-form:before{content:"\e6fd"}.icon-accelerate:before{content:"\e6fe"}.icon-feedback:before{content:"\e702"}.icon-vdc-2:before{content:"\e703"}.icon-vdc-3:before{content:"\e704"}.icon-vdc:before{content:"\e705"}.icon-new:before{content:"\e6fc"}.icon-collapse-right:before{content:"\e6fb"}.icon-collapse-left:before{content:"\e6fa"}.icon-aec:before{content:"\e6f3"}.icon-aic:before{content:"\e6f4"}.icon-mobile-2:before{content:"\e6f5"}.icon-amc:before{content:"\e6f6"}.icon-arc:before{content:"\e6f7"}.icon-game:before{content:"\e6f8"}.icon-iot:before{content:"\e6f9"}.icon-right:before{content:"\e6f2"}.icon-afc:before{content:"\e6f0"}.icon-specs:before{content:"\e6f1"}.icon-pen-2:before{content:"\e6c8"}.icon-key:before{content:"\e635"}.icon-bsn:before{content:"\e6ea"}.icon-mac-2:before{content:"\e6eb"}.icon-mac-3:before{content:"\e6ec"}.icon-mac:before{content:"\e6ed"}.icon-fenxiao:before{content:"\e6ee"}.icon-account-2:before{content:"\e6ef"}.icon-qiyeyouxiang-2:before{content:"\e6be"}.icon-qiyeyouxiang-3:before{content:"\e6bf"}.icon-qiyeyouxiang:before{content:"\e6c0"}.icon-yuming-2:before{content:"\e6d3"}.icon-yuming-3:before{content:"\e6df"}.icon-yuming:before{content:"\e6e0"}.icon-yumingyuwangzhan-2:before{content:"\e6e1"}.icon-yumingyuwangzhan-3:before{content:"\e6e2"}.icon-yumingyuwangzhan:before{content:"\e6e3"}.icon-yunjiexi-2:before{content:"\e6e4"}.icon-yunjiexi-3:before{content:"\e6e5"}.icon-yunjiexi:before{content:"\e6e6"}.icon-yunxunizhuji-2:before{content:"\e6e7"}.icon-yunxunizhuji-3:before{content:"\e6e8"}.icon-yunxunizhuji:before{content:"\e6e9"}.icon-api-3:before{content:"\e6d2"}.icon-api-2:before{content:"\e6d4"}.icon-api:before{content:"\e6d5"}.icon-dpa-2:before{content:"\e6d6"}.icon-dpa-3:before{content:"\e6d7"}.icon-dpa:before{content:"\e6d8"}.icon-lvwang-2:before{content:"\e6d9"}.icon-lvwang-3:before{content:"\e6da"}.icon-lvwang:before{content:"\e6db"}.icon-mas-2:before{content:"\e6dc"}.icon-mas-3:before{content:"\e6dd"}.icon-mas:before{content:"\e6de"}.icon-dts-2:before{content:"\e6cf"}.icon-dts-3:before{content:"\e6d0"}.icon-dts:before{content:"\e6d1"}.icon-android:before{content:"\e6c9"}.icon-cps-2:before{content:"\e6ca"}.icon-cps-3:before{content:"\e6cb"}.icon-cps:before{content:"\e6cc"}.icon-ios:before{content:"\e6cd"}.icon-vitality:before{content:"\e6ce"}.icon-dfs-2:before{content:"\e6bb"}.icon-dfs-3:before{content:"\e6bc"}.icon-dfs:before{content:"\e6bd"}.icon-edas-2:before{content:"\e6c1"}.icon-edas-3:before{content:"\e6c2"}.icon-edas:before{content:"\e6c3"}.icon-enter:before{content:"\e6c4"}.icon-usableCenter-2:before{content:"\e6c5"}.icon-usableCenter-3:before{content:"\e6c6"}.icon-usableCenter:before{content:"\e6c7"}.icon-ace-2:before{content:"\e600"}.icon-ace:before{content:"\e601"}.icon-add-1:before{content:"\e602"}.icon-add-2:before{content:"\e603"}.icon-add:before{content:"\e604"}.icon-ads-2:before{content:"\e605"}.icon-ads:before{content:"\e606"}.icon-amplify:before{content:"\e607"}.icon-arrow-down:before{content:"\e608"}.icon-arrow-left:before{content:"\e609"}.icon-arrow-right:before{content:"\e60a"}.icon-arrow-up:before{content:"\e60b"}.icon-backup:before{content:"\e60c"}.icon-bell:before{content:"\e60d"}.icon-buy:before{content:"\e60e"}.icon-calendar:before{content:"\e60f"}.icon-cdn-2:before{content:"\e610"}.icon-cdn:before{content:"\e611"}.icon-cdp:before{content:"\e612"}.icon-clock:before{content:"\e613"}.icon-cloudisk:before{content:"\e614"}.icon-cloudisk2:before{content:"\e615"}.icon-db-g:before{content:"\e616"}.icon-db-r:before{content:"\e617"}.icon-db-sign:before{content:"\e618"}.icon-db-t:before{content:"\e619"}.icon-db:before{content:"\e61a"}.icon-ddos-2:before{content:"\e61b"}.icon-ddos:before{content:"\e61c"}.icon-detail-2:before{content:"\e61d"}.icon-detail:before{content:"\e61e"}.icon-disk-image:before{content:"\e61f"}.icon-down:before{content:"\e620"}.icon-dpc-2:before{content:"\e621"}.icon-dpc:before{content:"\e622"}.icon-drds-2:before{content:"\e623"}.icon-drds:before{content:"\e624"}.icon-ecs-2:before{content:"\e625"}.icon-ecs:before{content:"\e626"}.icon-ess-2:before{content:"\e627"}.icon-ess:before{content:"\e628"}.icon-exec-snapshot-policy:before{content:"\e629"}.icon-goback:before{content:"\e62a"}.icon-graphs:before{content:"\e62b"}.icon-help-1:before{content:"\e62c"}.icon-help-2:before{content:"\e62d"}.icon-help:before{content:"\e62e"}.icon-home:before{content:"\e62f"}.icon-info-1:before{content:"\e630"}.icon-info-2:before{content:"\e631"}.icon-info:before{content:"\e632"}.icon-invite:before{content:"\e633"}.icon-jiankong-2:before{content:"\e634"}.icon-lightcloud-2:before{content:"\e636"}.icon-lightcloud:before{content:"\e637"}.icon-log:before{content:"\e638"}.icon-logo:before{content:"\e639"}.icon-menu:before{content:"\e63c"}.icon-mqs-2:before{content:"\e63d"}.icon-mqs:before{content:"\e63e"}.icon-mts:before{content:"\e63f"}.icon-narrow:before{content:"\e640"}.icon-no-1:before{content:"\e641"}.icon-no-2:before{content:"\e642"}.icon-no:before{content:"\e643"}.icon-oas-2:before{content:"\e644"}.icon-oas:before{content:"\e645"}.icon-ocs-2:before{content:"\e646"}.icon-ocs:before{content:"\e647"}.icon-odps-2:before{content:"\e648"}.icon-odps:before{content:"\e649"}.icon-ons-2:before{content:"\e64a"}.icon-ons:before{content:"\e64b"}.icon-opensearch-2:before{content:"\e64c"}.icon-opensearch:before{content:"\e64d"}.icon-oss-2:before{content:"\e64e"}.icon-oss:before{content:"\e64f"}.icon-ots-2:before{content:"\e650"}.icon-ots:before{content:"\e651"}.icon-pen:before{content:"\e652"}.icon-performance:before{content:"\e653"}.icon-pts-2:before{content:"\e654"}.icon-pts:before{content:"\e655"}.icon-ram-2:before{content:"\e656"}.icon-ram:before{content:"\e657"}.icon-rds-2:before{content:"\e658"}.icon-rds:before{content:"\e659"}.icon-regional:before{content:"\e65a"}.icon-remove-1:before{content:"\e65b"}.icon-remove-2:before{content:"\e65c"}.icon-remove:before{content:"\e65d"}.icon-renew-mgt:before{content:"\e65e"}.icon-safe-lock:before{content:"\e65f"}.icon-safetycontrol:before{content:"\e660"}.icon-search:before{content:"\e661"}.icon-setup:before{content:"\e662"}.icon-shift-in:before{content:"\e663"}.icon-slb-2:before{content:"\e664"}.icon-slb:before{content:"\e665"}.icon-sls-2:before{content:"\e666"}.icon-sls:before{content:"\e667"}.icon-snapshot:before{content:"\e668"}.icon-text-free:before{content:"\e669"}.icon-threshold:before{content:"\e66a"}.icon-tree:before{content:"\e66b"}.icon-unlock:before{content:"\e66c"}.icon-up:before{content:"\e66d"}.icon-updown:before{content:"\e66e"}.icon-viewtable:before{content:"\e66f"}.icon-vpc-2:before{content:"\e670"}.icon-vpc:before{content:"\e671"}.icon-warning-1:before{content:"\e672"}.icon-warning-2:before{content:"\e673"}.icon-warning:before{content:"\e674"}.icon-weekly:before{content:"\e675"}.icon-yes-1:before{content:"\e676"}.icon-yes-2:before{content:"\e677"}.icon-yes:before{content:"\e678"}.icon-yundun-2:before{content:"\e679"}.icon-yundun:before{content:"\e67a"}.icon-yunjiankong:before{content:"\e67b"}.icon-annex:before{content:"\e67c"}.icon-renew:before{content:"\e67d"}.icon-renew-2:before{content:"\e67e"}.icon-plus-border:before{content:"\e67f"}.icon-wo-domain:before{content:"\e680"}.icon-wo-email:before{content:"\e681"}.icon-wo-host:before{content:"\e682"}.icon-wo-sitebuild:before{content:"\e683"}.icon-wo-salepre:before{content:"\e684"}.icon-wo-beian:before{content:"\e685"}.icon-wo-account:before{content:"\e686"}.icon-wo-finance:before{content:"\e687"}.icon-square:before{content:"\e688"}.icon-left:before{content:"\e689"}.icon-upload:before{content:"\e68a"}.icon-list-open:before{content:"\e68b"}.icon-pause:before{content:"\e68c"}.icon-list-close:before{content:"\e68d"}.icon-circle:before{content:"\e68e"}.icon-refresh:before{content:"\e68f"}.icon-return:before{content:"\e690"}.icon-undo:before{content:"\e691"}.icon-alipay:before{content:"\e692"}.icon-auto-renew:before{content:"\e693"}.icon-mobile:before{content:"\e694"}.icon-account:before{content:"\e695"}.icon-services:before{content:"\e696"}.icon-expense:before{content:"\e697"}.icon-redisa-2:before{content:"\e698"}.icon-redisa:before{content:"\e699"}.icon-ddos-3:before{content:"\e69a"}.icon-redisa-3:before{content:"\e69b"}.icon-toolsimage-2:before{content:"\e69c"}.icon-cdp-2:before{content:"\e69d"}.icon-mts-2:before{content:"\e69e"}.icon-toolsimage:before{content:"\e69f"}.icon-toolsimage-3:before{content:"\e6a0"}.icon-ons-3:before{content:"\e6a1"}.icon-ram-3:before{content:"\e6a2"}.icon-yundun-3:before{content:"\e6a3"}.icon-pts-3:before{content:"\e6a4"}.icon-mts-3:before{content:"\e6a5"}.icon-mqs-3:before{content:"\e6a6"}.icon-drds-3:before{content:"\e6a7"}.icon-cdp-3:before{content:"\e6a8"}.icon-dpc-3:before{content:"\e6a9"}.icon-ads-3:before{content:"\e6aa"}.icon-jiankong-3:before{content:"\e6ab"}.icon-vpc-3:before{content:"\e6ac"}.icon-slb-3:before{content:"\e6ad"}.icon-rds-3:before{content:"\e6ae"}.icon-ots-3:before{content:"\e6af"}.icon-oss-3:before{content:"\e6b0"}.icon-ess-3:before{content:"\e6b1"}.icon-opensearch-3:before{content:"\e6b2"}.icon-odps-3:before{content:"\e6b3"}.icon-ocs-3:before{content:"\e6b4"}.icon-oas-3:before{content:"\e6b5"}.icon-lightcloud-3:before{content:"\e6b6"}.icon-cdn-3:before{content:"\e6b7"}.icon-ace-3:before{content:"\e6b8"}.icon-sls-3:before{content:"\e6b9"}.icon-ecs-3:before{content:"\e6ba"}.modal-content{border-radius:0px;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);-webkit-box-shadow:0px 5px 10px rgba(0,0,0,0.5);-moz-box-shadow:0px 5px 10px rgba(0,0,0,0.5);box-shadow:0px 5px 10px rgba(0,0,0,0.5)}.modal-footer{margin-top:0px}.modal-title{font-size:14px}.modal-header .close{font-size:28px;margin-top:-8px;font-weight:normal}.modal-backdrop{background-color:#FFF}.console-message-dialog .modal-body .lead{font-size:16px}.console-message-dialog .modal-body p{margin-top:6px}.nav-tabs>li>a,.nav-tabs.nav-justified>li>a{border-radius:0px 0px 0px 0px}.nav-tabs{border-color:#ddd}.nav-tabs>li{margin-left:-1px;border-top:1px solid #ddd;border-left:1px solid #ddd;border-right:1px solid #ddd;z-index:1}.nav-tabs>li>a,.nav-tabs>li>a:focus{color:#666;border-left:0px;border-right:0px;margin-right:0px;padding:10px 16px;background:#FBFAF8;border-bottom:0px}.nav-tabs>li.active{border-top:0px;border-left:1px solid #ddd;border-right:1px solid #ddd;z-index:3}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{border-top:2px solid #00a2ca;border-left:0px;border-right:0px;border-bottom:1px solid #FFF;color:#333}.nav-tabs>li>a:hover{background-color:#FFF;color:#09C}.nav-tabs .open>a,.nav-tabs .open>a:hover,.nav-tabs .open>a:focus{color:#000;background-color:#FAFAFA;border-color:#EEE}.nav-tabs.nav-justified>li:first-child{border-left:1px solid #ddd}.nav-tabs.nav-justified>li{border-top:1px solid #ddd;border-left:0px solid #ddd;border-right:1px solid #ddd;z-index:1}.nav-tabs.nav-justified>li>a{border-left:0px;border-right:0px;margin-right:0px;background-color:#fbfaf8;border-bottom:1px solid #ddd}.nav-tabs.nav-justified>li>a:hover{color:#09C;background-color:#FFF}.nav-tabs.nav-justified>li.active{border-top:0px;z-index:3}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-top:2px solid #00a2ca;border-left:0px;border-right:0px;border-bottom:1px solid #FFF;color:#333;background-color:#FFF}.nav-pills li a,.nav-pills li a:focus,.nav-pills li button,.nav-pills li button:focus{padding:6px 12px;border-radius:0px;border:1px solid #D9DEE4;background-color:#D9DEE4;color:#666;line-height:20px;height:32px;margin-left:2px}.nav-pills li a:hover,.nav-pills li a:focus:hover,.nav-pills li button:hover,.nav-pills li button:focus:hover{border:1px solid #D9DEE4;background-color:#DCE2E7;color:#444}.nav-pills li.active a,.nav-pills li.active a:hover,.nav-pills li.active a:focus,.nav-pills li.active button,.nav-pills li.active button:hover,.nav-pills li.active button:focus{border:1px solid #546478;background-color:#546478;color:#FFFFFF}.c-texttrimmer-pen{position:absolute;width:18px;height:18px;font-size:12px;padding:2px;text-align:center;margin-left:6px}.c-texttrimmer-box{position:absolute;padding:16px;background:#fff;z-index:1000;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);-webkit-border-radius:0px;-moz-border-radius:0px;-ms-border-radius:0px;-o-border-radius:0px;border-radius:0px;-webkit-box-shadow:1px 1px 8px rgba(0,0,0,0.5);-moz-box-shadow:1px 1px 8px rgba(0,0,0,0.5);box-shadow:1px 1px 8px rgba(0,0,0,0.5)}.c-texttrimmer-box:focus{outline:none}.c-texttrimmer-box p{margin:0 0 10px}.c-texttrimmer-box p.c-texttrimmer-tip{color:red}.c-texttrimmer-box .c-texttrimmer-btnbox a{margin-right:8px}.modal,.modal-open{overflow:auto;overflow-y:auto}.console-helper{position:absolute;height:100%;width:400px;right:0px;top:32px;z-index:1000;border:1px solid #eee;background:#fff;border-left:1px solid #dddddd;box-shadow:0px 0px 4px rgba(0,0,0,0.2);position:fixed}.console-helper-animation{-webkit-transition:all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);transition:all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);-webkit-transform:translateX(0);transform:translateX(0)}.console-helper-folded{right:-400px}.console-helper-folded .console-helper-head .console-helper-button{margin-left:-44px}.console-helper-head{height:56px;background:#f5f5f5;border-bottom:1px solid #dddddd}.console-helper-head .console-helper-button{float:left;background:url(images/helper-icon.png) center center no-repeat;height:32px;width:32px;margin:12px;cursor:pointer;opacity:0.6}.console-helper-head .console-helper-button:hover{opacity:1}.console-helper-head .console-helper-title{float:left;font-size:14px;line-height:32px;height:32px;margin:12px 0;color:#333}.console-helper-body .console-helper-nav{border-bottom:1px solid #dddddd;margin:0 20px;list-style:none;overflow:hidden;zoom:1;padding:0}.console-helper-body .console-helper-nav li{float:left;padding:12px}.console-helper-body .console-helper-nav li a{color:#666}.console-helper-body .console-helper-nav li a:hover{color:#000}.console-helper-body .console-helper-nav li.active{border-bottom:2px solid #999}.console-helper-body .console-helper-panel-list .console-helper-panel{margin:20px}.console-helper-body .console-helper-panel-list .console-helper-panel .console-helper-xiaoyun .console-helper-xiaoyun-search{height:32px}.console-helper-body .console-helper-panel-list .console-helper-panel .console-helper-xiaoyun .console-helper-xiaoyun-recommend ul{list-style:none;margin:0;padding:0}.console-helper-foot{background:#f5f5f5;position:absolute;width:100%;bottom:0;left:0;border-top:1px solid #eee}.console-helper-foot .console-helper-service{overflow:hidden;zoom:1;height:32px;margin:12px;list-style:none}.console-helper-foot .console-helper-service li{width:48%;float:left}.console-helper-foot .console-helper-service li p{margin:0;color:#666}.console-helper-foot .console-helper-service li p a{color:#666}.console-helper-foot .console-helper-service li p a:hover{text-decoration:underline}.growl{z-index:9999999;top:50px;width:260px}.alert-success{color:#090;background-color:#F2FFEA;border-color:#C7DDB9}.alert-success .alert-link{color:#063;font-weight:normal}.alert-info{color:#555;background-color:#F9F9F9;border-color:#DDD}.alert-info .alert-link{color:#06C;font-weight:normal}.alert-warning{color:#f68300;background-color:#FCF8E2;border-color:#FBECCB}.alert-warning .alert-link{color:#c50;font-weight:normal}.alert-danger{color:#ee2117;background-color:#FFF6F2;border-color:#F1ACAC}.alert-danger .alert-link{color:#b00;font-weight:normal}.alert{padding:6px 12px;line-height:18px;margin-bottom:6px;border-radius:0px}.alert .close{margin-top:-5px}.alert ul{padding-left:16px}.product-icons-32,.product-icons-48,.product-icons-64{background-repeat:no-repeat;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;background-image:url(aliyun-logo/product.icons.png);background-image:-webkit-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x);background-image:-moz-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x);background-image:-ms-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x);background-image:-os-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x)}.product-icons-32{width:32px;height:32px}.product-icons-48{width:48px;height:48px}.product-icons-64{width:64px;height:64px}.product-icons-32.product-icons-ace-grey{background-position:-448px -1088px !important}.product-icons-32.product-icons-ace{background-position:-800px -1024px !important}.product-icons-48.product-icons-ace-grey{background-position:-192px -832px !important}.product-icons-48.product-icons-ace{background-position:-720px -880px !important}.product-icons-64.product-icons-ace-grey{background-position:-576px -128px !important}.product-icons-64.product-icons-ace{background-position:0px -64px !important}.product-icons-32.product-icons-actiontrail-grey{background-position:-416px -1088px !important}.product-icons-32.product-icons-actiontrail{background-position:-384px -1088px !important}.product-icons-48.product-icons-actiontrail-grey{background-position:-976px -288px !important}.product-icons-48.product-icons-actiontrail{background-position:-432px -976px !important}.product-icons-64.product-icons-actiontrail-grey{background-position:-128px 0px !important}.product-icons-64.product-icons-actiontrail{background-position:-128px -64px !important}.product-icons-32.product-icons-ads-grey{background-position:-352px -1088px !important}.product-icons-32.product-icons-ads{background-position:-256px -1088px !important}.product-icons-48.product-icons-ads-grey{background-position:-768px -880px !important}.product-icons-48.product-icons-ads{background-position:-928px -384px !important}.product-icons-64.product-icons-ads-grey{background-position:-64px -128px !important}.product-icons-64.product-icons-ads{background-position:-128px -128px !important}.product-icons-32.product-icons-aegis-grey{background-position:-224px -1088px !important}.product-icons-32.product-icons-aegis{background-position:-192px -1088px !important}.product-icons-48.product-icons-aegis-grey{background-position:-688px -768px !important}.product-icons-48.product-icons-aegis{background-position:-832px -96px !important}.product-icons-64.product-icons-aegis-grey{background-position:-192px -64px !important}.product-icons-64.product-icons-aegis{background-position:-192px -128px !important}.product-icons-32.product-icons-antifraud-grey{background-position:-160px -1088px !important}.product-icons-32.product-icons-antifraud{background-position:-64px -1088px !important}.product-icons-48.product-icons-antifraud-grey{background-position:-880px -480px !important}.product-icons-48.product-icons-antifraud{background-position:-880px -720px !important}.product-icons-64.product-icons-antifraud-grey{background-position:-64px -192px !important}.product-icons-64.product-icons-antifraud{background-position:-128px -192px !important}.product-icons-32.product-icons-api-grey{background-position:-32px -1088px !important}.product-icons-32.product-icons-api{background-position:0px -1088px !important}.product-icons-48.product-icons-api-grey{background-position:-96px -928px !important}.product-icons-48.product-icons-api{background-position:-192px -928px !important}.product-icons-64.product-icons-api-grey{background-position:-256px 0px !important}.product-icons-64.product-icons-api{background-position:-256px -64px !important}.product-icons-32.product-icons-apigateway-grey{background-position:-1104px -1056px !important}.product-icons-32.product-icons-apigateway{background-position:-1104px -960px !important}.product-icons-48.product-icons-apigateway-grey{background-position:-480px -976px !important}.product-icons-48.product-icons-apigateway{background-position:-576px -976px !important}.product-icons-64.product-icons-apigateway-grey{background-position:-256px -192px !important}.product-icons-64.product-icons-apigateway{background-position:0px -256px !important}.product-icons-32.product-icons-aps-grey{background-position:-1104px -928px !important}.product-icons-32.product-icons-aps{background-position:-1104px -896px !important}.product-icons-48.product-icons-aps-grey{background-position:-832px -432px !important}.product-icons-48.product-icons-aps{background-position:-832px -528px !important}.product-icons-64.product-icons-aps-grey{background-position:-128px -256px !important}.product-icons-64.product-icons-aps{background-position:-192px -256px !important}.product-icons-32.product-icons-ats-grey{background-position:-1104px -864px !important}.product-icons-32.product-icons-ats{background-position:-1104px -768px !important}.product-icons-48.product-icons-ats-grey{background-position:-768px -832px !important}.product-icons-48.product-icons-ats{background-position:-880px 0px !important}.product-icons-64.product-icons-ats-grey{background-position:-320px 0px !important}.product-icons-64.product-icons-ats{background-position:-320px -64px !important}.product-icons-32.product-icons-batchcompute-grey{background-position:-1104px -736px !important}.product-icons-32.product-icons-batchcompute{background-position:-1104px -704px !important}.product-icons-48.product-icons-batchcompute-grey{background-position:-192px -880px !important}.product-icons-48.product-icons-batchcompute{background-position:-288px -880px !important}.product-icons-64.product-icons-batchcompute-grey{background-position:-320px -192px !important}.product-icons-64.product-icons-batchcompute{background-position:-320px -256px !important}.product-icons-32.product-icons-cas-grey{background-position:-1104px -672px !important}.product-icons-32.product-icons-cas{background-position:-1104px -576px !important}.product-icons-48.product-icons-cas-grey{background-position:-928px -432px !important}.product-icons-48.product-icons-cas{background-position:-928px -480px !important}.product-icons-64.product-icons-cas-grey{background-position:-64px -320px !important}.product-icons-64.product-icons-cas{background-position:-128px -320px !important}.product-icons-32.product-icons-cdi-grey{background-position:-1104px -544px !important}.product-icons-32.product-icons-cdi{background-position:-1104px -512px !important}.product-icons-48.product-icons-cdi-grey{background-position:-672px -928px !important}.product-icons-48.product-icons-cdi{background-position:-720px -928px !important}.product-icons-64.product-icons-cdi-grey{background-position:-256px -320px !important}.product-icons-64.product-icons-cdi{background-position:-320px -320px !important}.product-icons-32.product-icons-cdn-grey{background-position:-1104px -480px !important}.product-icons-32.product-icons-cdn{background-position:-1104px -384px !important}.product-icons-48.product-icons-cdn-grey{background-position:-976px -864px !important}.product-icons-48.product-icons-cdn{background-position:-976px -912px !important}.product-icons-64.product-icons-cdn-grey{background-position:-384px -64px !important}.product-icons-64.product-icons-cdn{background-position:-384px -128px !important}.product-icons-32.product-icons-cdp-grey{background-position:-1104px -352px !important}.product-icons-32.product-icons-cdp{background-position:-1104px -320px !important}.product-icons-48.product-icons-cdp-grey{background-position:-1024px -48px !important}.product-icons-48.product-icons-cdp{background-position:-1024px -96px !important}.product-icons-64.product-icons-cdp-grey{background-position:-384px -256px !important}.product-icons-64.product-icons-cdp{background-position:-384px -320px !important}.product-icons-32.product-icons-cli-grey{background-position:-1104px -288px !important}.product-icons-32.product-icons-cli{background-position:-1104px -192px !important}.product-icons-48.product-icons-cli-grey{background-position:-832px -144px !important}.product-icons-48.product-icons-cli{background-position:-832px -192px !important}.product-icons-64.product-icons-cli-grey{background-position:-64px -384px !important}.product-icons-64.product-icons-cli{background-position:-128px -384px !important}.product-icons-32.product-icons-containerservice-grey{background-position:-1104px -160px !important}.product-icons-32.product-icons-containerservice{background-position:-1104px -128px !important}.product-icons-48.product-icons-containerservice-grey{background-position:-832px -720px !important}.product-icons-48.product-icons-containerservice{background-position:-832px -768px !important}.product-icons-64.product-icons-containerservice-grey{background-position:-256px -384px !important}.product-icons-64.product-icons-containerservice{background-position:-320px -384px !important}.product-icons-32.product-icons-cps-grey{background-position:-1104px -96px !important}.product-icons-32.product-icons-cps{background-position:-1104px 0px !important}.product-icons-48.product-icons-cps-grey{background-position:-480px -832px !important}.product-icons-48.product-icons-cps{background-position:-528px -832px !important}.product-icons-64.product-icons-cps-grey{background-position:-448px 0px !important}.product-icons-64.product-icons-cps{background-position:-448px -64px !important}.product-icons-32.product-icons-csb-grey{background-position:-1056px -1056px !important}.product-icons-32.product-icons-csb{background-position:-1024px -1056px !important}.product-icons-48.product-icons-csb-grey{background-position:-880px -192px !important}.product-icons-48.product-icons-csb{background-position:-880px -240px !important}.product-icons-64.product-icons-csb-grey{background-position:-448px -192px !important}.product-icons-64.product-icons-csb{background-position:-448px -256px !important}.product-icons-32.product-icons-ddos-grey{background-position:-992px -1056px !important}.product-icons-32.product-icons-ddos{background-position:-896px -1056px !important}.product-icons-48.product-icons-ddos-grey{background-position:-880px -768px !important}.product-icons-48.product-icons-ddos{background-position:-880px -816px !important}.product-icons-64.product-icons-ddos-grey{background-position:-448px -384px !important}.product-icons-64.product-icons-ddos{background-position:0px -448px !important}.product-icons-32.product-icons-ddosbasic-grey{background-position:-864px -1056px !important}.product-icons-32.product-icons-ddosbasic{background-position:-832px -1056px !important}.product-icons-48.product-icons-ddosbasic-grey{background-position:-480px -880px !important}.product-icons-48.product-icons-ddosbasic{background-position:-528px -880px !important}.product-icons-64.product-icons-ddosbasic-grey{background-position:-128px -448px !important}.product-icons-64.product-icons-ddosbasic{background-position:-192px -448px !important}.product-icons-32.product-icons-dfs-grey{background-position:-800px -1056px !important}.product-icons-32.product-icons-dfs{background-position:-704px -1056px !important}.product-icons-48.product-icons-dfs-grey{background-position:-928px -144px !important}.product-icons-48.product-icons-dfs{background-position:-928px -192px !important}.product-icons-64.product-icons-dfs-grey{background-position:-320px -448px !important}.product-icons-64.product-icons-dfs{background-position:-384px -448px !important}.product-icons-32.product-icons-directmail-grey{background-position:-672px -1056px !important}.product-icons-32.product-icons-directmail{background-position:-640px -1056px !important}.product-icons-48.product-icons-directmail-grey{background-position:-928px -672px !important}.product-icons-48.product-icons-directmail{background-position:-928px -720px !important}.product-icons-64.product-icons-directmail-grey{background-position:-512px 0px !important}.product-icons-64.product-icons-directmail{background-position:-512px -64px !important}.product-icons-32.product-icons-disk-grey{background-position:-608px -1056px !important}.product-icons-32.product-icons-disk{background-position:-512px -1056px !important}.product-icons-48.product-icons-disk-grey{background-position:-336px -928px !important}.product-icons-48.product-icons-disk{background-position:-384px -928px !important}.product-icons-64.product-icons-disk-grey{background-position:-512px -192px !important}.product-icons-64.product-icons-disk{background-position:-512px -256px !important}.product-icons-32.product-icons-dms-grey{background-position:-480px -1056px !important}.product-icons-32.product-icons-dms{background-position:-448px -1056px !important}.product-icons-48.product-icons-dms-grey{background-position:-912px -928px !important}.product-icons-48.product-icons-dms{background-position:-976px 0px !important}.product-icons-64.product-icons-dms-grey{background-position:-512px -384px !important}.product-icons-64.product-icons-dms{background-position:-512px -448px !important}.product-icons-32.product-icons-dpc-grey{background-position:-416px -1056px !important}.product-icons-32.product-icons-dpc{background-position:-320px -1056px !important}.product-icons-48.product-icons-dpc-grey{background-position:-976px -528px !important}.product-icons-48.product-icons-dpc{background-position:-976px -576px !important}.product-icons-64.product-icons-dpc-grey{background-position:-64px -512px !important}.product-icons-64.product-icons-dpc{background-position:-128px -512px !important}.product-icons-32.product-icons-drds-grey{background-position:-288px -1056px !important}.product-icons-32.product-icons-drds{background-position:-256px -1056px !important}.product-icons-48.product-icons-drds-grey{background-position:-144px -976px !important}.product-icons-48.product-icons-drds{background-position:-192px -976px !important}.product-icons-64.product-icons-drds-grey{background-position:-256px -512px !important}.product-icons-64.product-icons-drds{background-position:-320px -512px !important}.product-icons-32.product-icons-dsi-grey{background-position:-224px -1056px !important}.product-icons-32.product-icons-dsi{background-position:-128px -1056px !important}.product-icons-48.product-icons-dsi-grey{background-position:-720px -976px !important}.product-icons-48.product-icons-dsi{background-position:-768px -976px !important}.product-icons-64.product-icons-dsi-grey{background-position:-448px -512px !important}.product-icons-64.product-icons-dsi{background-position:-512px -512px !important}.product-icons-32.product-icons-dts-grey{background-position:-96px -1056px !important}.product-icons-32.product-icons-dts{background-position:-64px -1056px !important}.product-icons-48.product-icons-dts-grey{background-position:-1024px -288px !important}.product-icons-48.product-icons-dts{background-position:-1024px -336px !important}.product-icons-64.product-icons-dts-grey{background-position:-576px -64px !important}.product-icons-64.product-icons-dts{background-position:0px 0px !important}.product-icons-32.product-icons-eclipse-grey{background-position:-32px -1056px !important}.product-icons-32.product-icons-eclipse{background-position:-1072px -992px !important}.product-icons-48.product-icons-eclipse-grey{background-position:-832px 0px !important}.product-icons-48.product-icons-eclipse{background-position:-832px -48px !important}.product-icons-64.product-icons-eclipse-grey{background-position:-576px -256px !important}.product-icons-64.product-icons-eclipse{background-position:-576px -320px !important}.product-icons-32.product-icons-ecs-grey{background-position:-1072px -960px !important}.product-icons-32.product-icons-ecs{background-position:-1072px -928px !important}.product-icons-48.product-icons-ecs-grey{background-position:-832px -288px !important}.product-icons-48.product-icons-ecs{background-position:-832px -336px !important}.product-icons-64.product-icons-ecs-grey{background-position:-576px -448px !important}.product-icons-64.product-icons-ecs{background-position:-576px -512px !important}.product-icons-32.product-icons-edas-grey{background-position:-1072px -896px !important}.product-icons-32.product-icons-edas{background-position:-1072px -800px !important}.product-icons-48.product-icons-edas-grey{background-position:-832px -576px !important}.product-icons-48.product-icons-edas{background-position:-832px -624px !important}.product-icons-64.product-icons-edas-grey{background-position:-64px -576px !important}.product-icons-64.product-icons-edas{background-position:-128px -576px !important}.product-icons-32.product-icons-elp-grey{background-position:-1072px -768px !important}.product-icons-32.product-icons-elp{background-position:-1072px -736px !important}.product-icons-48.product-icons-elp-grey{background-position:-48px -832px !important}.product-icons-48.product-icons-elp{background-position:-96px -832px !important}.product-icons-64.product-icons-elp-grey{background-position:-256px -576px !important}.product-icons-64.product-icons-elp{background-position:-320px -576px !important}.product-icons-32.product-icons-emapreduce-grey{background-position:-1072px -704px !important}.product-icons-32.product-icons-emapreduce{background-position:-1072px -608px !important}.product-icons-48.product-icons-emapreduce-grey{background-position:-336px -832px !important}.product-icons-48.product-icons-emapreduce{background-position:-384px -832px !important}.product-icons-64.product-icons-emapreduce-grey{background-position:-448px -576px !important}.product-icons-64.product-icons-emapreduce{background-position:-512px -576px !important}.product-icons-32.product-icons-esn-grey{background-position:-1072px -576px !important}.product-icons-32.product-icons-esn{background-position:-1072px -544px !important}.product-icons-48.product-icons-esn-grey{background-position:-624px -832px !important}.product-icons-48.product-icons-esn{background-position:-672px -832px !important}.product-icons-64.product-icons-esn-grey{background-position:-640px 0px !important}.product-icons-64.product-icons-esn{background-position:-640px -64px !important}.product-icons-32.product-icons-ess-grey{background-position:-1072px -512px !important}.product-icons-32.product-icons-ess{background-position:-1072px -416px !important}.product-icons-48.product-icons-ess-grey{background-position:-880px -48px !important}.product-icons-48.product-icons-ess{background-position:-880px -96px !important}.product-icons-64.product-icons-ess-grey{background-position:-640px -192px !important}.product-icons-64.product-icons-ess{background-position:-640px -256px !important}.product-icons-32.product-icons-expressconnect-grey{background-position:-1072px -384px !important}.product-icons-32.product-icons-expressconnect{background-position:-1072px -352px !important}.product-icons-48.product-icons-expressconnect-grey{background-position:-880px -336px !important}.product-icons-48.product-icons-expressconnect{background-position:-880px -384px !important}.product-icons-64.product-icons-expressconnect-grey{background-position:-640px -384px !important}.product-icons-64.product-icons-expressconnect{background-position:-640px -448px !important}.product-icons-32.product-icons-havip-grey{background-position:-1072px -320px !important}.product-icons-32.product-icons-havip{background-position:-1072px -224px !important}.product-icons-48.product-icons-havip-grey{background-position:-880px -624px !important}.product-icons-48.product-icons-havip{background-position:-880px -672px !important}.product-icons-64.product-icons-havip-grey{background-position:-640px -576px !important}.product-icons-64.product-icons-havip{background-position:0px -640px !important}.product-icons-32.product-icons-hpc-grey{background-position:-1072px -192px !important}.product-icons-32.product-icons-hpc{background-position:-1072px -160px !important}.product-icons-48.product-icons-hpc-grey{background-position:-48px -880px !important}.product-icons-48.product-icons-hpc{background-position:-96px -880px !important}.product-icons-64.product-icons-hpc-grey{background-position:-128px -640px !important}.product-icons-64.product-icons-hpc{background-position:-192px -640px !important}.product-icons-32.product-icons-hsm-grey{background-position:-1072px -128px !important}.product-icons-32.product-icons-hsm{background-position:-1072px -32px !important}.product-icons-48.product-icons-hsm-grey{background-position:-336px -880px !important}.product-icons-48.product-icons-hsm{background-position:-384px -880px !important}.product-icons-64.product-icons-hsm-grey{background-position:-320px -640px !important}.product-icons-64.product-icons-hsm{background-position:-384px -640px !important}.product-icons-32.product-icons-iot-grey{background-position:-1072px 0px !important}.product-icons-32.product-icons-iot{background-position:-1024px -1024px !important}.product-icons-48.product-icons-iot-grey{background-position:-624px -880px !important}.product-icons-48.product-icons-iot{background-position:-672px -880px !important}.product-icons-64.product-icons-iot-grey{background-position:-512px -640px !important}.product-icons-64.product-icons-iot{background-position:-576px -640px !important}.product-icons-32.product-icons-jiankong-grey{background-position:-992px -1024px !important}.product-icons-32.product-icons-jiankong{background-position:-896px -1024px !important}.product-icons-48.product-icons-jiankong-grey{background-position:-928px 0px !important}.product-icons-48.product-icons-jiankong{background-position:-928px -48px !important}.product-icons-64.product-icons-jiankong-grey{background-position:-704px 0px !important}.product-icons-64.product-icons-jiankong{background-position:-704px -64px !important}.product-icons-32.product-icons-keyongxingzhongxin-grey{background-position:-864px -1024px !important}.product-icons-32.product-icons-keyongxingzhongxin{background-position:-832px -1024px !important}.product-icons-48.product-icons-keyongxingzhongxin-grey{background-position:-928px -288px !important}.product-icons-48.product-icons-keyongxingzhongxin{background-position:-144px -832px !important}.product-icons-64.product-icons-keyongxingzhongxin-grey{background-position:-704px -192px !important}.product-icons-64.product-icons-keyongxingzhongxin{background-position:-704px -256px !important}.product-icons-32.product-icons-kms-grey{background-position:-704px -1024px !important}.product-icons-32.product-icons-kms{background-position:-672px -1024px !important}.product-icons-48.product-icons-kms-grey{background-position:-928px -576px !important}.product-icons-48.product-icons-kms{background-position:-928px -624px !important}.product-icons-64.product-icons-kms-grey{background-position:-704px -448px !important}.product-icons-64.product-icons-kms{background-position:-704px -512px !important}.product-icons-32.product-icons-kvstore-grey{background-position:-640px -1024px !important}.product-icons-32.product-icons-kvstore{background-position:-608px -1024px !important}.product-icons-48.product-icons-kvstore-grey{background-position:-928px -864px !important}.product-icons-48.product-icons-kvstore{background-position:0px -928px !important}.product-icons-64.product-icons-kvstore-grey{background-position:-704px -576px !important}.product-icons-64.product-icons-kvstore{background-position:-704px -640px !important}.product-icons-32.product-icons-livevideo-grey{background-position:-512px -1024px !important}.product-icons-32.product-icons-livevideo{background-position:-480px -1024px !important}.product-icons-48.product-icons-livevideo-grey{background-position:-240px -928px !important}.product-icons-48.product-icons-livevideo{background-position:-288px -928px !important}.product-icons-64.product-icons-livevideo-grey{background-position:-128px -704px !important}.product-icons-64.product-icons-livevideo{background-position:-192px -704px !important}.product-icons-32.product-icons-lvwang-grey{background-position:-448px -1024px !important}.product-icons-32.product-icons-lvwang{background-position:-416px -1024px !important}.product-icons-48.product-icons-lvwang-grey{background-position:-528px -928px !important}.product-icons-48.product-icons-lvwang{background-position:-576px -928px !important}.product-icons-64.product-icons-lvwang-grey{background-position:-256px -704px !important}.product-icons-64.product-icons-lvwang{background-position:-320px -704px !important}.product-icons-32.product-icons-mac-grey{background-position:-320px -1024px !important}.product-icons-32.product-icons-mac{background-position:-288px -1024px !important}.product-icons-48.product-icons-mac-grey{background-position:-816px -928px !important}.product-icons-48.product-icons-mac{background-position:-864px -928px !important}.product-icons-64.product-icons-mac-grey{background-position:-512px -704px !important}.product-icons-64.product-icons-mac{background-position:-576px -704px !important}.product-icons-32.product-icons-man-grey{background-position:-256px -1024px !important}.product-icons-32.product-icons-man{background-position:-224px -1024px !important}.product-icons-48.product-icons-man-grey{background-position:-976px -144px !important}.product-icons-48.product-icons-man{background-position:-976px -192px !important}.product-icons-64.product-icons-man-grey{background-position:-640px -704px !important}.product-icons-64.product-icons-man{background-position:-704px -704px !important}.product-icons-32.product-icons-mns-grey{background-position:-128px -1024px !important}.product-icons-32.product-icons-mns{background-position:-96px -1024px !important}.product-icons-48.product-icons-mns-grey{background-position:-976px -432px !important}.product-icons-48.product-icons-mns{background-position:-976px -480px !important}.product-icons-64.product-icons-mns-grey{background-position:-768px -128px !important}.product-icons-64.product-icons-mns{background-position:-768px -192px !important}.product-icons-32.product-icons-mongodb-grey{background-position:-64px -1024px !important}.product-icons-32.product-icons-mongodb{background-position:-32px -1024px !important}.product-icons-48.product-icons-mongodb-grey{background-position:-976px -720px !important}.product-icons-48.product-icons-mongodb{background-position:-976px -768px !important}.product-icons-64.product-icons-mongodb-grey{background-position:-768px -256px !important}.product-icons-64.product-icons-mongodb{background-position:-768px -320px !important}.product-icons-32.product-icons-mqs-grey{background-position:-1024px -960px !important}.product-icons-32.product-icons-mqs{background-position:-1024px -928px !important}.product-icons-48.product-icons-mqs-grey{background-position:-48px -976px !important}.product-icons-48.product-icons-mqs{background-position:-96px -976px !important}.product-icons-64.product-icons-mqs-grey{background-position:-768px -512px !important}.product-icons-64.product-icons-mqs{background-position:-768px -576px !important}.product-icons-32.product-icons-mss-grey{background-position:-1024px -896px !important}.product-icons-32.product-icons-mss{background-position:-1024px -864px !important}.product-icons-48.product-icons-mss-grey{background-position:-336px -976px !important}.product-icons-48.product-icons-mss{background-position:-384px -976px !important}.product-icons-64.product-icons-mss-grey{background-position:-768px -640px !important}.product-icons-64.product-icons-mss{background-position:-768px -704px !important}.product-icons-32.product-icons-mts-grey{background-position:-1024px -768px !important}.product-icons-32.product-icons-mts{background-position:-1024px -736px !important}.product-icons-48.product-icons-mts-grey{background-position:-624px -976px !important}.product-icons-48.product-icons-mts{background-position:-672px -976px !important}.product-icons-64.product-icons-mts-grey{background-position:-128px -768px !important}.product-icons-64.product-icons-mts{background-position:-192px -768px !important}.product-icons-32.product-icons-nas-grey{background-position:-1024px -704px !important}.product-icons-32.product-icons-nas{background-position:-1024px -672px !important}.product-icons-48.product-icons-nas-grey{background-position:-912px -976px !important}.product-icons-48.product-icons-nas{background-position:-960px -976px !important}.product-icons-64.product-icons-nas-grey{background-position:-256px -768px !important}.product-icons-64.product-icons-nas{background-position:-320px -768px !important}.product-icons-32.product-icons-oas-grey{background-position:-1024px -576px !important}.product-icons-32.product-icons-oas{background-position:-1024px -544px !important}.product-icons-48.product-icons-oas-grey{background-position:-1024px -192px !important}.product-icons-48.product-icons-oas{background-position:-1024px -240px !important}.product-icons-64.product-icons-oas-grey{background-position:-512px -768px !important}.product-icons-64.product-icons-oas{background-position:-576px -768px !important}.product-icons-32.product-icons-oceanbase-grey{background-position:0px -1056px !important}.product-icons-32.product-icons-oceanbase{background-position:-1024px -512px !important}.product-icons-48.product-icons-oceanbase-grey{background-position:-1024px -432px !important}.product-icons-48.product-icons-oceanbase{background-position:-1024px -384px !important}.product-icons-64.product-icons-oceanbase-grey{background-position:-448px -768px !important}.product-icons-64.product-icons-oceanbase{background-position:-384px -768px !important}.product-icons-32.product-icons-ocs-grey{background-position:-1024px -608px !important}.product-icons-32.product-icons-ocs{background-position:-1024px -640px !important}.product-icons-48.product-icons-ocs-grey{background-position:-864px -976px !important}.product-icons-48.product-icons-ocs{background-position:-816px -976px !important}.product-icons-64.product-icons-ocs-grey{background-position:-64px -768px !important}.product-icons-64.product-icons-ocs{background-position:0px -768px !important}.product-icons-32.product-icons-odps-grey{background-position:-1024px -800px !important}.product-icons-32.product-icons-odps{background-position:-1024px -832px !important}.product-icons-48.product-icons-odps-grey{background-position:-288px -976px !important}.product-icons-48.product-icons-odps{background-position:-240px -976px !important}.product-icons-64.product-icons-odps-grey{background-position:-768px -448px !important}.product-icons-64.product-icons-odps{background-position:-768px -384px !important}.product-icons-32.product-icons-ons-grey{background-position:-1024px -992px !important}.product-icons-32.product-icons-ons{background-position:0px -1024px !important}.product-icons-48.product-icons-ons-grey{background-position:-976px -672px !important}.product-icons-48.product-icons-ons{background-position:-976px -624px !important}.product-icons-64.product-icons-ons-grey{background-position:-768px -64px !important}.product-icons-64.product-icons-ons{background-position:-768px 0px !important}.product-icons-32.product-icons-opensearch-grey{background-position:-160px -1024px !important}.product-icons-32.product-icons-opensearch{background-position:-192px -1024px !important}.product-icons-48.product-icons-opensearch-grey{background-position:-976px -96px !important}.product-icons-48.product-icons-opensearch{background-position:-976px -48px !important}.product-icons-64.product-icons-opensearch-grey{background-position:-448px -704px !important}.product-icons-64.product-icons-opensearch{background-position:-384px -704px !important}.product-icons-32.product-icons-oss-grey{background-position:-352px -1024px !important}.product-icons-32.product-icons-oss{background-position:-384px -1024px !important}.product-icons-48.product-icons-oss-grey{background-position:-480px -928px !important}.product-icons-48.product-icons-oss{background-position:-432px -928px !important}.product-icons-64.product-icons-oss-grey{background-position:-64px -704px !important}.product-icons-64.product-icons-oss{background-position:0px -704px !important}.product-icons-32.product-icons-ots-grey{background-position:-544px -1024px !important}.product-icons-32.product-icons-ots{background-position:-576px -1024px !important}.product-icons-48.product-icons-ots-grey{background-position:-928px -816px !important}.product-icons-48.product-icons-ots{background-position:-928px -768px !important}.product-icons-64.product-icons-ots-grey{background-position:-704px -384px !important}.product-icons-64.product-icons-ots{background-position:-704px -320px !important}.product-icons-32.product-icons-petadata-grey{background-position:-736px -1024px !important}.product-icons-32.product-icons-petadata{background-position:-768px -1024px !important}.product-icons-48.product-icons-petadata-grey{background-position:-928px -336px !important}.product-icons-48.product-icons-petadata{background-position:-928px -240px !important}.product-icons-64.product-icons-petadata-grey{background-position:-704px -128px !important}.product-icons-64.product-icons-petadata{background-position:-640px -640px !important}.product-icons-32.product-icons-pts-grey{background-position:-928px -1024px !important}.product-icons-32.product-icons-pts{background-position:-960px -1024px !important}.product-icons-48.product-icons-pts-grey{background-position:-816px -880px !important}.product-icons-48.product-icons-pts{background-position:-576px -880px !important}.product-icons-64.product-icons-pts-grey{background-position:-448px -640px !important}.product-icons-64.product-icons-pts{background-position:-256px -640px !important}.product-icons-32.product-icons-ram-grey{background-position:-1072px -64px !important}.product-icons-32.product-icons-ram{background-position:-1072px -96px !important}.product-icons-48.product-icons-ram-grey{background-position:-240px -880px !important}.product-icons-48.product-icons-ram{background-position:0px -880px !important}.product-icons-64.product-icons-ram-grey{background-position:-64px -640px !important}.product-icons-64.product-icons-ram{background-position:-640px -512px !important}.product-icons-32.product-icons-rds-grey{background-position:-1072px -256px !important}.product-icons-32.product-icons-rds{background-position:-1072px -288px !important}.product-icons-48.product-icons-rds-grey{background-position:-880px -528px !important}.product-icons-48.product-icons-rds{background-position:-880px -288px !important}.product-icons-64.product-icons-rds-grey{background-position:-640px -320px !important}.product-icons-64.product-icons-rds{background-position:-640px -128px !important}.product-icons-32.product-icons-ros-grey{background-position:-1072px -448px !important}.product-icons-32.product-icons-ros{background-position:-1072px -480px !important}.product-icons-48.product-icons-ros-grey{background-position:-816px -832px !important}.product-icons-48.product-icons-ros{background-position:-576px -832px !important}.product-icons-64.product-icons-ros-grey{background-position:-576px -576px !important}.product-icons-64.product-icons-ros{background-position:-384px -576px !important}.product-icons-32.product-icons-sas-grey{background-position:-1072px -640px !important}.product-icons-32.product-icons-sas{background-position:-1072px -672px !important}.product-icons-48.product-icons-sas-grey{background-position:-240px -832px !important}.product-icons-48.product-icons-sas{background-position:0px -832px !important}.product-icons-64.product-icons-sas-grey{background-position:-192px -576px !important}.product-icons-64.product-icons-sas{background-position:0px -576px !important}.product-icons-32.product-icons-scan-grey{background-position:-1072px -832px !important}.product-icons-32.product-icons-scan{background-position:-1072px -864px !important}.product-icons-48.product-icons-scan-grey{background-position:-832px -480px !important}.product-icons-48.product-icons-scan{background-position:-832px -240px !important}.product-icons-64.product-icons-scan-grey{background-position:-576px -384px !important}.product-icons-64.product-icons-scan{background-position:-576px -192px !important}.product-icons-32.product-icons-slb-grey{background-position:-1072px -1024px !important}.product-icons-32.product-icons-slb{background-position:-1024px -480px !important}.product-icons-48.product-icons-slb-grey{background-position:-736px -768px !important}.product-icons-48.product-icons-slb{background-position:-1024px -144px !important}.product-icons-64.product-icons-slb-grey{background-position:-576px 0px !important}.product-icons-64.product-icons-slb{background-position:-384px -512px !important}.product-icons-32.product-icons-slm-grey{background-position:-160px -1056px !important}.product-icons-32.product-icons-slm{background-position:-192px -1056px !important}.product-icons-48.product-icons-slm-grey{background-position:-528px -976px !important}.product-icons-48.product-icons-slm{background-position:0px -976px !important}.product-icons-64.product-icons-slm-grey{background-position:-192px -512px !important}.product-icons-64.product-icons-slm{background-position:0px -512px !important}.product-icons-32.product-icons-sls-grey{background-position:-352px -1056px !important}.product-icons-32.product-icons-sls{background-position:-384px -1056px !important}.product-icons-48.product-icons-sls-grey{background-position:-976px -336px !important}.product-icons-48.product-icons-sls{background-position:-768px -928px !important}.product-icons-64.product-icons-sls-grey{background-position:-512px -320px !important}.product-icons-64.product-icons-sls{background-position:-512px -128px !important}.product-icons-32.product-icons-sos-grey{background-position:-544px -1056px !important}.product-icons-32.product-icons-sos{background-position:-576px -1056px !important}.product-icons-48.product-icons-sos-grey{background-position:-144px -928px !important}.product-icons-48.product-icons-sos{background-position:-928px -528px !important}.product-icons-64.product-icons-sos-grey{background-position:-448px -448px !important}.product-icons-64.product-icons-sos{background-position:-256px -448px !important}.product-icons-32.product-icons-toolsimage-grey{background-position:-736px -1056px !important}.product-icons-32.product-icons-toolsimage{background-position:-768px -1056px !important}.product-icons-48.product-icons-toolsimage-grey{background-position:-864px -880px !important}.product-icons-48.product-icons-toolsimage{background-position:-432px -880px !important}.product-icons-64.product-icons-toolsimage-grey{background-position:-64px -448px !important}.product-icons-64.product-icons-toolsimage{background-position:-448px -320px !important}.product-icons-32.product-icons-vipaegis-grey{background-position:-928px -1056px !important}.product-icons-32.product-icons-vipaegis{background-position:-960px -1056px !important}.product-icons-48.product-icons-vipaegis-grey{background-position:-880px -576px !important}.product-icons-48.product-icons-vipaegis{background-position:-880px -144px !important}.product-icons-64.product-icons-vipaegis-grey{background-position:-448px -128px !important}.product-icons-64.product-icons-vipaegis{background-position:-384px -384px !important}.product-icons-32.product-icons-visualstudio-grey{background-position:-1104px -32px !important}.product-icons-32.product-icons-visualstudio{background-position:-1104px -64px !important}.product-icons-48.product-icons-visualstudio-grey{background-position:-288px -832px !important}.product-icons-48.product-icons-visualstudio{background-position:-832px -672px !important}.product-icons-64.product-icons-visualstudio-grey{background-position:-192px -384px !important}.product-icons-64.product-icons-visualstudio{background-position:0px -384px !important}.product-icons-32.product-icons-vod-grey{background-position:-1104px -224px !important}.product-icons-32.product-icons-vod{background-position:-1104px -256px !important}.product-icons-48.product-icons-vod-grey{background-position:-784px -768px !important}.product-icons-48.product-icons-vod{background-position:-1024px 0px !important}.product-icons-64.product-icons-vod-grey{background-position:-384px -192px !important}.product-icons-64.product-icons-vod{background-position:-384px 0px !important}.product-icons-32.product-icons-vpc-grey{background-position:-1104px -416px !important}.product-icons-32.product-icons-vpc{background-position:-1104px -448px !important}.product-icons-48.product-icons-vpc-grey{background-position:-976px -384px !important}.product-icons-48.product-icons-vpc{background-position:-624px -928px !important}.product-icons-64.product-icons-vpc-grey{background-position:-192px -320px !important}.product-icons-64.product-icons-vpc{background-position:0px -320px !important}.product-icons-32.product-icons-waf-grey{background-position:-1104px -608px !important}.product-icons-32.product-icons-waf{background-position:-1104px -640px !important}.product-icons-48.product-icons-waf-grey{background-position:-928px -96px !important}.product-icons-48.product-icons-waf{background-position:-144px -880px !important}.product-icons-64.product-icons-waf-grey{background-position:-320px -128px !important}.product-icons-64.product-icons-waf{background-position:-256px -256px !important}.product-icons-32.product-icons-xianzhi-grey{background-position:-1104px -800px !important}.product-icons-32.product-icons-xianzhi{background-position:-1104px -832px !important}.product-icons-48.product-icons-xianzhi-grey{background-position:-432px -832px !important}.product-icons-48.product-icons-xianzhi{background-position:-832px -384px !important}.product-icons-64.product-icons-xianzhi-grey{background-position:-64px -256px !important}.product-icons-64.product-icons-xianzhi{background-position:-256px -128px !important}.product-icons-32.product-icons-ysf-grey{background-position:-1104px -992px !important}.product-icons-32.product-icons-ysf{background-position:-1104px -1024px !important}.product-icons-48.product-icons-ysf-grey{background-position:-976px -816px !important}.product-icons-48.product-icons-ysf{background-position:-48px -928px !important}.product-icons-64.product-icons-ysf-grey{background-position:-192px -192px !important}.product-icons-64.product-icons-ysf{background-position:0px -192px !important}.product-icons-32.product-icons-yundun-grey{background-position:-96px -1088px !important}.product-icons-32.product-icons-yundun{background-position:-128px -1088px !important}.product-icons-48.product-icons-yundun-grey{background-position:-720px -832px !important}.product-icons-48.product-icons-yundun{background-position:-640px -768px !important}.product-icons-64.product-icons-yundun-grey{background-position:-192px 0px !important}.product-icons-64.product-icons-yundun{background-position:0px -128px !important}.product-icons-32.product-icons-yunjiankong-grey{background-position:-288px -1088px !important}.product-icons-32.product-icons-yunjiankong{background-position:-320px -1088px !important}.product-icons-48.product-icons-yunjiankong-grey{background-position:-880px -432px !important}.product-icons-48.product-icons-yunjiankong{background-position:-976px -240px !important}.product-icons-64.product-icons-yunjiankong-grey{background-position:-64px -64px !important}.product-icons-64.product-icons-yunjiankong{background-position:-64px 0px !important}.console-search{box-sizing:border-box;float:left;margin-right:1px;position:relative;z-index:11}.console-search *{box-sizing:border-box}.console-search .console-search-ask{position:relative}.console-search .console-search-ask .console-search-ask-input{width:200px;height:40px;background:#2a2e31;border:0;padding:12px 30px 12px 10px;display:inline-block;color:#999;-webkit-border-radius:1px 1px;-moz-border-radius:1px / 1px;border-radius:1px / 1px;-o-transition:all 0.3s, 0.3s;-ms-transition:all 0.3s, 0.3s;-moz-transition:all 0.3s, 0.3s;-webkit-transition:all 0.3s, 0.3s}.console-search .console-search-ask .console-search-ask-input:focus{outline:none}.console-search .console-search-ask .console-search-mark{font-size:16px;line-height:30px;position:absolute;height:100%;width:40px;color:#eee;padding:5px;text-decoration:none;display:block}.console-search .console-search-ask .console-search-questionmark{right:0;top:0}.console-search .console-search-ask-active .console-search-ask-input{width:320px;height:40px;background:#f2f2f2;border:0;color:#000}.console-search .console-search-ask-active .console-search-questionmark{color:#0099cc}.console-search .console-search-answer{width:402px;margin-top:2px;left:-1px;border:1px solid #d4d4d4;border-top:none;background:#fff;position:absolute;-webkit-border-radius:2px 2px;-moz-border-radius:2px / 2px;border-radius:2px / 2px;text-shadow:1px}.console-search .console-search-answer .console-search-answer-head{background:#f8f8f8;border-bottom:1px solid #eee;height:42px}.console-search .console-search-answer .console-search-answer-head ul{list-style:none;margin:0;padding:0 24px}.console-search .console-search-answer .console-search-answer-head ul li{float:left;margin-right:14px;height:42px;line-height:42px}.console-search .console-search-answer .console-search-answer-head ul li a{display:block;width:100%;height:100%;color:#666;text-decoration:none}.console-search .console-search-answer .console-search-answer-head ul li a:hover{color:#ff6500;border-bottom:2px solid #ff6500}.console-search .console-search-answer .console-search-answer-head ul li.console-search-tab-active a{color:#ff6500;border-bottom:2px solid #ff6500}.console-search .console-search-answer .console-search-answer-body{padding:0 24px}.console-search .console-search-answer .console-search-answer-body .console-search-answer-list .console-search-answer-item{height:40px;line-height:40px;border-bottom:1px solid #eee}.console-search .console-search-answer .console-search-answer-body .console-search-answer-list .console-search-answer-item a{color:#00a2ca}.console-search .console-search-answer .console-search-answer-body .console-search-answer-more{height:40px;line-height:40px;text-align:right}.console-search .console-search-answer .console-search-answer-body .console-search-answer-more a{color:#00a2ca}.selector{width:100%;height:140px;border:1px solid #999;background-color:#FFF;overflow-x:hidden;overflow-y:auto}.selector .selector-list{list-style:none;margin:0px;padding:0px}.selector .selector-list .selector-item{height:32px;line-height:32px;overflow:hidden;border-bottom:1px solid #DDD;text-overflow:ellipsis;white-space:nowrap;text-indent:8px}.selector .selector-list .selector-item:hover{color:#06C;background-color:#FAFCFF;cursor:pointer}.selector .selector-list .selector-item.active{background-color:#37C;color:#FFF}.selector .selector-list .selector-item.disabled{color:#AAA;cursor:not-allowed;background-color:#FAFAFA}.selector .selector-msg{text-align:center;color:#999;height:32px;line-height:32px}.selector.selector-status-error .selector-msg{cursor:pointer}.selector.selector-status-hasmore .selector-msg{cursor:pointer}.list-selector .selector-box{width:45%;float:left}.list-selector .selector-box .inner-wrap{border:1px solid #bbb;height:200px;overflow:hidden}.list-selector .selector-box .inner-wrap .inner-head{border:1px solid #eee;margin:6px;position:relative}.list-selector .selector-box .inner-wrap .inner-head input{border:0;width:90%}.list-selector .selector-box .inner-wrap .inner-head input:focus{outline:0}.list-selector .selector-box .inner-wrap .inner-head .search{width:20px;height:20px;line-height:20px;padding:0 6px;cursor:pointer;position:absolute;right:0;top:0}.list-selector .selector-box .inner-wrap .inner-body{height:160px;overflow-y:auto;overflow-x:hidden;border:0}.list-selector .selector-box .inner-wrap .inner-body2{height:200px;overflow-y:auto;overflow-x:hidden;border:0}.list-selector .selector-mid{width:10%;text-align:center;float:left}.list-selector .selector-mid .mid-box{margin:10px auto;height:40px;width:40px;font-weight:bold;border:1px solid #bbb;background:#f7f7f7;display:block;cursor:pointer}.list-selector .selector-mid .mid-margin{margin-top:80px;margin-bottom:10px}.aliyun-console-table-search-list{min-width:100px}.console-global-notice{position:relative;margin-top:-1px;z-index:1}.console-global-notice .console-global-notice-nav{position:absolute;top:13px;left:25px;z-index:2}.console-global-notice .console-global-notice-nav span{width:12px;height:12px;display:block;float:left;background:#e8e8e8;border-radius:12px;margin-right:3px;cursor:pointer}.console-global-notice .console-global-notice-nav span.active{background:#999999}.console-global-notice .console-global-notice-list{height:50px;position:relative}.console-global-notice .console-global-notice-list .console-global-notice-item{position:absolute;width:100%;top:0;left:0;z-index:1;padding:10px 12px;border-radius:2px;margin-bottom:0px;text-align:left}.console-global-notice .console-global-notice-list .console-global-notice-item .console-global-notice-nomore{position:absolute;top:8px;right:12px}.console-global-notice .console-global-notice-list .console-global-notice-item .console-global-notice-content{padding-right:80px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.console-clip-copy{background:rgba(0,0,0,0.75);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#bf000000,endColorstr=#bf000000);position:absolute;color:#fff;line-height:24px;height:24px;overflow:hidden;padding:0px 12px 0px 30px}.console-clip-copy .rectangle1,.console-clip-copy .rectangle2{position:absolute;top:8px;left:13px;border:1px solid #fff;width:10px;height:12px;background:#404040;z-index:2}.console-clip-copy .rectangle2{left:15px;z-index:1;top:5px}.console-clip-copyed{padding-left:12px}.console-clip-copyed .rectangle1,.console-clip-copyed .rectangle2{display:none}.console-aside-wrap{position:fixed;z-index:105}.console-aside-wrap .console-aside{position:absolute;display:none}.console-aside-wrap .console-aside.console-aside-transform{-o-transition:all 0.3s, 0.3s;-ms-transition:all 0.3s, 0.3s;-moz-transition:all 0.3s, 0.3s;-webkit-transition:all 0.3s, 0.3s}.console-aside-wrap.top{top:0;width:100%}.console-aside-wrap.top .console-aside{top:0;width:100%}.console-aside-wrap.right{right:0;height:100%;top:0}.console-aside-wrap.right .console-aside{right:0;height:100%}.console-aside-wrap.left{left:0;height:100%;top:0}.console-aside-wrap.left .console-aside{left:0;height:100%}.console-aside-wrap.bottom{bottom:0;width:100%}.console-aside-wrap.bottom .console-aside{bottom:0;width:100%}.table-default-viewer{width:100%;background-color:#FFF}.table-default-viewer td{padding:11px 20px;border:1px solid #eeeeee}.table-default-viewer.off{display:none}.table-viewer-topbar-content{padding:0px;margin:0px;margin-right:8px}.table-viewer-header{margin-top:10px;margin-bottom:-1px;height:40px;background:#F5f6FA;line-height:38px;border:1px solid #e1e6eb;position:relative;border-left:4px solid #6d7781}.table-viewer-header .table-viewer-topbar-title{font-size:14px;color:#333333;display:inline-block;margin-left:16px}.table-viewer-header .table-viewer-topbar-content{margin-right:60px}.table-viewer-header .toggle-drop-down-icon{-webkit-user-select:none;-moz-user-select:none;user-select:none;-o-user-select:none;-ms-user-select:none;position:absolute;width:40px;height:39px;right:0;top:0;border-left:1px solid #e1e6eb}.table-viewer-header .table-viewer-dropdown{display:inline-block;margin:10px;font-size:20px}.simple-chart{height:100%;border:1px solid #ccd6e0;position:relative;-webkit-box-shadow:0px 0px 3px rgba(0,0,0,0.1);-moz-box-shadow:0px 0px 3px rgba(0,0,0,0.1);box-shadow:0px 0px 3px rgba(0,0,0,0.1)}.simple-chart .simple-chart-head{height:40px;line-height:40px;font-size:14px;padding-left:10px;background:#f8f9fb}.simple-chart .simple-chart-head-title{float:left}.simple-chart .simple-chart-operations{float:right}.simple-chart .simple-chart-operations .simple-chart-btn{display:inline-block;border-left:1px solid #e1e6eb;width:40px;text-align:center;cursor:pointer}.simple-chart .simple-chart-operations .simple-chart-btn span{font-size:14px;font-weight:bold;vertical-align:text-bottom}.simple-chart .simple-chart-body{border-top:1px solid #ccd6e0;padding:0px 2px 2px 0px}.simple-chart .simple-chart-body .highcharts-container{float:left}.simple-chart .simple-chart-body-inner{height:100%}.simple-chart .simple-chart-annulus-center{position:absolute;text-align:center}.simple-chart .simple-chart-annulus-center .simple-chart-annulus-number{color:#0099cc;font-size:32px}.simple-chart.simple-chart-nowrap{border:none;-webkit-box-shadow:0px 0px 0px;-moz-box-shadow:0px 0px 0px;box-shadow:0px 0px 0px}.simple-chart.simple-chart-nowrap .simple-chart-head{display:none}.simple-chart.simple-chart-nowrap .simple-chart-body{border:none;height:100% !important}.console-middle-page{margin-top:80px}.console-middle-page .console-middle-page-icon{text-align:right}.console-middle-page .console-middle-page-title{font-size:20px;margin:0;line-height:48px}.console-middle-page .console-middle-page-text{font-size:12px;color:#666}.console-middle-page .console-middle-page-link{border-top:1px solid #EEE;margin-top:16px;padding-top:16px}.console-middle-page .console-middle-page-link>a{padding-right:14px}.console-rank-select{height:32px;padding:8px 0px;overflow:hidden}.console-rank-select .rank-item{width:20px;height:16px;line-height:16px;overflow:hidden;display:block;float:left;font-size:16px;color:#CCC;cursor:pointer;zoom:1}.console-rank-select .rank-active{color:#09C}.console-rank-select .rank-hover{color:#3CF}.simple-loading{position:relative}.simple-loading .simple-loading-inner{margin-left:auto;margin-right:auto}.simple-loading-1:before,.simple-loading-1:after,.simple-loading-1{border-radius:50%;width:14px;height:14px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation:simple-loading-1 1.8s infinite ease-in-out;animation:simple-loading-1 1.8s infinite ease-in-out}.simple-loading-1{font-size:10px;position:relative;text-indent:-9999em;-webkit-animation-delay:0.16s;animation-delay:0.16s}.simple-loading-1:before{left:-30px}.simple-loading-1:after{left:30px;-webkit-animation-delay:0.32s;animation-delay:0.32s}.simple-loading-1:before,.simple-loading-1:after{content:'';position:absolute;top:0}@-webkit-keyframes simple-loading-1{0%,80%,100%{box-shadow:0 2.5em 0 -1.3em #ddd}40%{box-shadow:0 2.5em 0 0 #ddd}}@keyframes simple-loading-1{0%,80%,100%{box-shadow:0 2.5em 0 -1.3em #ddd}40%{box-shadow:0 2.5em 0 0 #ddd}}.feedback-container{position:fixed;right:0px;bottom:100px;font-family:微软雅黑, 'Microsoft Yahei', 'Hiragino Sans GB', tahoma, arial, 宋体;font-size:12px;font-stretch:normal;font-style:normal;font-variant:normal;font-weight:normal;z-index:100}.feedback-container:hover .feedback-trigger .feedback-trigger-text,.feedback-container.active .feedback-trigger .feedback-trigger-text{width:56px;padding:0 0px 0 4px}.feedback-container h1,.feedback-container h2,.feedback-container textarea,.feedback-container input{margin:0;padding:0;border:0}.feedback-container .feedback{position:absolute;width:396px;background:rgba(0,162,202,0.5);padding:3px;right:81px;bottom:0}.feedback-container .feedback .feedback-panel{width:390px;background:#fff;padding:20px}.feedback-container .feedback .feedback-title{border-bottom:1px solid #eee;padding-bottom:8px;margin-bottom:15px}.feedback-container .feedback .feedback-title h1{color:#000;font-size:16px;display:inline-block}.feedback-container .feedback .feedback-title h1 i{cursor:pointer;margin-top:6px;float:right}.feedback-container .feedback .feedback-content{position:relative;margin-bottom:15px}.feedback-container .feedback .feedback-content h2{font-size:14px;margin-bottom:5px}.feedback-container .feedback .feedback-content .must{position:absolute;left:-10px;top:3px;color:red}.feedback-container .feedback textarea,.feedback-container .feedback input{font:12px/1.5 "\5FAE\8F6F\96C5\9ED1","Microsoft Yahei","Hiragino Sans GB",tahoma,arial,"\5B8B\4F53"}.feedback-container .feedback .feedback-content textarea{resize:none;height:106px;width:100%;padding:9px 10px;margin:6px 1px 1px 0;border:solid 1px #e8e8e8;border-radius:4px;line-height:16px;color:#333;font-size:12px;outline:0}.feedback-container .feedback .feedback-content .feedback-content-count{color:#666;margin-top:5px}.feedback-container .feedback .feedback-contact{margin-bottom:25px;position:relative}.feedback-container .feedback .feedback-contact h2{font-size:14px;margin-bottom:5px}.feedback-container .feedback .feedback-contact input{height:36px;margin-bottom:20px;resize:none;width:100%;padding:9px 10px;margin:6px 1px 1px 0;border:solid 1px #e8e8e8;line-height:16px;color:#333;font-size:12px;outline:0;background:#fff;border-radius:4px;text-decoration:none;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;user-select:none}.feedback-container .feedback .feedback-contact input:focus{background:#e9fbfe;border:solid 1px #e8e8e8}.feedback-container .feedback .feedback-contact .inputError{position:absolute;bottom:-22px;left:2px;color:red}.feedback-container .feedback .submit-btn{padding:0;height:24px;line-height:24px;font-size:12px;display:inline-block;min-width:78px;background:#00a2ca;color:#fff;text-align:center;outline:none;border-radius:0;text-decoration:none;cursor:pointer}.feedback-container .feedback .submit-btn:hover{background:#33b5d4;border-color:#33b5d4;text-decoration:none}.feedback-container .feedback .feedback-footer{text-align:center;padding:5px 0}.feedback-container .feedback .submit-btn.disabled{background:#efefef;border-color:#efefef;color:#ccc;cursor:default}.feedback .thanks{font-size:16px;margin-left:15px;color:#000;position:relative;top:-9px}.feedback .feedback-close{display:inline-block;float:right;cursor:pointer;font-size:18px}.feedback .feedback-check{font-size:30px;color:#65ce00}.feedback-container .feedback-trigger{display:inline-block;background-color:#3d5061;font-size:12px;color:#fff;border-radius:4px;cursor:pointer;padding:4px 4px 1px 1px}.feedback-container .feedback-trigger .feedback-trigger-text{padding:0;display:inline-block;height:16px;overflow:hidden;width:0;-moz-transition:all 0.3s ease-in;-webkit-transition:all 0.3s ease-in;-o-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.feedback-container .feedback-trigger .feedback-trigger-icon{padding:0;display:inline-block;font-size:15px}.console-guide-dialog .modal-dialog{width:840px}.console-guide-dialog .modal-body{margin-bottom:15px}.console-guide-dialog .carousel-control{display:none}.console-guide-dialog .carousel-indicators{bottom:-40px !important}.console-guide-dialog .carousel-indicators li{background:#e8e8e8;width:16px;height:16px;border-radius:12px;margin:0 0 0 2px}.console-guide-dialog .carousel-indicators li.active{background:#09c;width:16px;height:16px;border-radius:12px;margin:0 0 0 2px}.console-guide-dialog .console-guide-dialog-link{position:absolute;z-index:100;bottom:20px;right:20px}.console-tag-select{position:absolute;width:320px}.console-tag-select ul{list-style:none;box-shadow:none !important;display:block;margin:0;padding:0}.console-tag-select .console-tag-dropdown{position:absolute;top:100%;left:0;z-index:1000;margin-top:2px;width:326px}.console-tag-select .console-tag-dropdown .dropdown-menu{position:static;border:1px solid #e1e6eb;width:160px}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-key-item-block{padding:7px 16px;display:block}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-key-item-empty{color:#999;font-style:italic}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-key-item-title .console-tag-key-item-block{background:#F5F6FA;border-bottom:1px solid #eee}.console-tag-select .console-tag-dropdown .dropdown-menu li a{border-bottom:1px solid #eee;white-space:pre-line;position:relative}.console-tag-select .console-tag-dropdown .dropdown-menu li a:hover,.console-tag-select .console-tag-dropdown .dropdown-menu li a:focus{background-color:#F9F9FA}.console-tag-select .console-tag-dropdown .dropdown-menu li:last-child a{border-bottom:none}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:hover,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:focus{text-decoration:none;outline:0;-webkit-transition:backgroud 0.2s ease, 0.2s ease}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a .console-tag-selected-icon,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:hover .console-tag-selected-icon,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:focus .console-tag-selected-icon{display:block}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active{border-left:2px solid #09c;margin-left:-1px}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active a,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active a:hover,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active a:focus{padding-left:15px}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-value-item a.tag-active,.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-value-item a.tag-active:hover,.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-value-item a.tag-active:focus{background-color:#F9F9FA}.console-tag-select .console-tag-dropdown .console-tag-value-dropdown{margin-left:-1px}.console-tag-select .console-tag-pagepick{padding:0 5px}.console-tag-select .console-tag-pagepick a{display:inline-block !important;border-bottom:none !important;-webkit-user-select:none}.console-tag-select .console-tag-selected-icon{display:none;float:right;font-size:14px;position:absolute;right:8px;top:8px}.console-tag-select .console-tag-label-wrap{padding-left:2px}.console-tag-select .console-tag-label{padding:5px 20px 5px 5px;background:#f1f1f1;position:relative;margin-left:2px}.console-tag-select .console-tag-label .console-tag-label-remove{position:absolute;top:0;right:0;width:20px;height:27px;line-height:27px;text-align:center;cursor:pointer}.console-tag-select-view .console-tag-label{padding:5px 20px 5px 5px;background:#f1f1f1;position:relative;margin-left:2px}.console-tag-select-view .console-tag-label .console-tag-label-remove{position:absolute;top:0;right:0;width:20px;height:27px;line-height:27px;text-align:center;cursor:pointer;color:#666}.console-tag-select-view .console-tag-label .console-tag-label-colon{padding:0 1px}.console-tag-edit .tag-panel{min-height:120px;border:2px dashed #ddd;padding:8px}.console-tag-edit .tag-panel .console-tag-select-view .console-tag-label{margin:4px}.console-tag-edit .tag-edit-tip{color:#999;font-style:italic;margin-top:8px}.console-tag-edit-form{display:inline-block}.console-tag-edit-form input.form-control{width:80px}.console-tag-edit-form.form-inline .form-group{margin:0 8px 0 0px}.console-tag-loading-overlay{position:absolute;height:100%;width:100%;top:0;left:0;background:#fff;opacity:0.5;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50)}.console-tag-loading-block{position:absolute;top:50%;left:50%}.console-searchbar-textinput{min-width:180px}.console-shuttle .title{border-left:1px solid #e1e6eb;border-right:1px solid #e1e6eb;border-top:1px solid #e1e6eb;background-color:#F5F6FA;padding-left:5px;line-height:30px}.console-shuttle .search{position:relative}.console-shuttle .search .icon-search{position:absolute;right:5px;top:10px;font-size:12px}.console-shuttle .search input{width:100%;height:32px}.console-shuttle .selector{border:1px solid #e1e6eb;height:240px}.console-shuttle .left-selector{height:220px}.console-shuttle .right-selector{height:240px}.console-shuttle .search-hidden{height:240px}.console-shuttle .btn{display:block;margin:12px 45%}.console-shuttle .console-selector{width:40%}.console-shuttle .console-selector-result{width:40%}.console-shuttle .selector-group-options{vertical-align:middle;width:20%;text-align:center;margin-top:100px}.console-shuttle .selector-list .line-head{line-height:12px;margin-bottom:8px;color:#000}.console-shuttle .selector-list .line-bottom{line-height:12px;color:#999}.console-shuttle .selector-list .line-column-left{display:inline-block}.console-shuttle .selector-list .line-column-right{display:inline-block;float:right;padding:5px 0;color:#000}.console-shuttle .selector-list .line-yellow-text{color:#ff6600}.console-shuttle .selector-list .selector-item{height:auto;line-height:normal;padding:10px;text-indent:0}.console-shuttle .selector-list .selector-item:hover{color:auto;background:#f9f9f9}.console-shuttle .selector-list .selector-item.active{color:#fff;background:#0099cb}.console-shuttle .selector-list .selector-item.active .line-head{color:#fff}.console-shuttle .selector-list .selector-item.active .line-bottom{color:#fff}.console-shuttle .selector-list .selector-item.active .line-yellow-text{color:#fff}.console-shuttle .selector-list .selector-item.active .line-column-right{color:#fff}body{font-size:12px}body,h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue", "Luxi Sans", "DejaVu Sans", Tahoma, "Hiragino Sans GB", STHeiti, "Microsoft YaHei"}a{color:#06C;cursor:pointer}a:hover{color:#039}label{font-size:100%}.nowrap{white-space:nowrap}.breakall{word-break:break-all;word-wrap:break-word}input::-ms-clear{display:none}input[type="radio"],input[type="checkbox"]{margin-top:2px;margin-top:1px \9}.console-container{padding:0 15px}.console-sidebar{border-right:1px solid #DDD}.console-sidebar .nav{margin-right:4px}.console-sidebar .nav li{border-bottom:1px solid #DDD;padding:4px 0px;position:relative}.console-sidebar .nav li a{color:#333;padding:6px 16px;border-left:2px solid #FFF}.console-sidebar .nav li a:hover{background-color:#FFF;border-left:2px solid #F90}.console-sidebar .nav li a span[class^="icon-"]{position:absolute;left:0px;top:8px;color:#999;font-size:14px}.console-sidebar .nav li.active a{color:#fff;border-left:2px solid #F90;background-color:#313844}.console-sidebar .nav .nav{margin-right:0px}.console-sidebar .nav .nav li{border-bottom:0px}.console-sidebar .nav .nav li a{text-indent:12px;background:#FFF;border-left-color:#FFF;color:#333}.console-sidebar .nav .nav li a:hover{background-color:#FFF;border-left:2px solid #F90}.console-sidebar .nav .nav li.active a{color:#fff;border-left:2px solid #F90;background-color:#313844}.console-instance-head{padding:3px 0px;border-bottom:1px solid #DDD}.console-instance-head h3.instance-id{display:inline-block;margin-right:8px;vertical-align:middle}.console-instance-head .pull-right{padding:16px 0px 10px}.dropdown-menu{font-size:12px;border-radius:0px;padding:0px;box-shadow:2px 2px 8px rgba(0,0,0,0.2)}.dropdown-menu li a{padding:7px 16px}.dropdown-menu .divider{margin:0px 0px}.console-chart{width:100%}.tooltip{word-break:break-all}.popover .popover-inner{padding:8px}.popover .popover-inner .popover-content{padding:0px}.console-not-service{margin-top:80px}.console-not-service .console-not-service-icon{text-align:right;padding-top:8px}.console-not-service .console-not-service-title{font-size:20px}.console-not-service .console-not-service-text{font-size:12px;color:#666}.console-not-service .console-not-service-link{border-top:1px solid #EEE;margin-top:16px;padding-top:16px}.console-step{height:24px;position:relative;margin-left:0px;margin-right:0px}.console-step .step{font-size:14px;height:24px;line-height:24px;color:#FFF;background:#cacaca;z-index:1;text-align:center}.console-step .step:before{content:'';display:block;position:absolute;left:-12px;z-index:8;top:0px;border-top:12px solid #cacaca;border-left:12px solid transparent !important;border-bottom:12px solid #cacaca}.console-step .step:after{content:'';display:block;width:16px;height:24px;position:absolute;right:0px;z-index:9;top:0px;border-top:12px solid transparent !important;border-left:12px solid #cacaca;border-bottom:12px solid transparent !important;background-color:#FFF}.console-step .step-first:before{display:none}.console-step .step-end:after{display:none}.console-step .step-pass{background-color:#99dcf3}.console-step .step-pass:after{border-color:#99dcf3}.console-step .step-pass:before{border-color:#99dcf3}.console-step .step-active{background-color:#00a0c7}.console-step .step-active:after{border-color:#00a0c7}.console-step .step-active:before{border-color:#00a0c7} diff --git a/console/src/main/resources/static/console-fe/public/css/font-awesome.css b/console/src/main/resources/static/console-fe/public/css/font-awesome.css new file mode 100644 index 000000000..09f8a2e58 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/css/font-awesome.css @@ -0,0 +1,2099 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.5.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.eot b/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.eot new file mode 100644 index 000000000..df84394b2 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.eot differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.ttf b/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.ttf new file mode 100644 index 000000000..a765a05c2 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.ttf differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.woff b/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.woff new file mode 100644 index 000000000..34fc56827 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/aliyun-console-font.woff differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/font_515771_emcns5054x3whfr.ttf b/console/src/main/resources/static/console-fe/public/css/fonts/font_515771_emcns5054x3whfr.ttf new file mode 100644 index 000000000..95551f3de Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/font_515771_emcns5054x3whfr.ttf differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/font_515771_emcns5054x3whfr.woff b/console/src/main/resources/static/console-fe/public/css/fonts/font_515771_emcns5054x3whfr.woff new file mode 100644 index 000000000..9487bb681 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/font_515771_emcns5054x3whfr.woff differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.ttf b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.ttf new file mode 100644 index 000000000..cbe70c8ef Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.ttf differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.woff b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.woff new file mode 100644 index 000000000..b14ec18a5 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.woff differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.woff2 b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.woff2 new file mode 100644 index 000000000..9fd172f9d Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-bold.woff2 differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.ttf b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.ttf new file mode 100644 index 000000000..695e4d485 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.ttf differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.woff b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.woff new file mode 100644 index 000000000..b41d9054d Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.woff differ diff --git a/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.woff2 b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.woff2 new file mode 100644 index 000000000..74b5d0e80 Binary files /dev/null and b/console/src/main/resources/static/console-fe/public/css/fonts/roboto-regular.woff2 differ diff --git a/console/src/main/resources/static/console-fe/public/css/icon.css b/console/src/main/resources/static/console-fe/public/css/icon.css new file mode 100644 index 000000000..60cc705ce --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/css/icon.css @@ -0,0 +1,262 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +@font-face { + /*无边框*/ + font-family: "iconfont-1"; + src: url('icon1/iconfont.eot?t=1458627591'); /* IE9*/ + src: url('icon1/iconfont.eot?t=1458627591#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('icon1/iconfont.woff?t=1458627591') format('woff'), /* chrome, firefox */ url('icon1/iconfont.ttf?t=1458627591') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('icon1/iconfont.svg?t=1458627591#iconfont') format('svg'); /* iOS 4.1- */ +} + +@font-face { + /*有边框*/ + font-family: "iconfont-2"; + src: url('icon/iconfont.eot'); /* IE9*/ + src: url('icon/iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('icon/iconfont.woff') format('woff'), /* chrome, firefox */ url('icon/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('icon/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */ +} + +.iconfont { + /* 有边框 */ + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + -moz-osx-font-smoothing: grayscale; +} + +.iconfont-1 { + /*无边框*/ + + font-family: "iconfont-1" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + -moz-osx-font-smoothing: grayscale; +} + +.iconfont-2 { + /*有边框*/ + font-family: "iconfont-2" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + -moz-osx-font-smoothing: grayscale; +} + +.logo { + +} + +.panel-logo { + padding-right: 2px; + font-size: 18px; + display: inline-block; + color: #333; +} + +.icon-lg { + font-size: 80px !important; +} + +.icon-size-md { + font-size: 40px !important; + vertical-align: middle; +} + +.icon-size-lg { + font-size: 80px !important; + vertical-align: middle; +} + +.icon-hsf:before { + content: "\e62f" !important; +} + +.icon-rocketmq:before { + content: "\e632" !important; +} + +.icon-notify:before { + content: "\e61e" !important; +} + +.icon-tddl:before { + content: "\e61e" !important; +} + +.icon-pandora:before { + content: "\e622" !important; +} + +.icon-ailtomcat:before { + content: "\e628" !important; +} + +.icon-configserver:before { + content: "\e61e" !important; +} + +.icon-diamondserver:before { + content: "\e62a" !important; +} + +.icon-vipserver:before { + content: "\e625" !important; +} + +.icon-eagleeye:before { + content: "\e62c" !important; +} + +.icon-tengine:before { + content: "\e635" !important; +} + +.icon-tair:before { + content: "\e634" !important; +} + +.icon-hbase:before { + content: "\e62d" !important; +} + +.icon-jstorm:before { + content: "\e627" !important; +} + +.icon-histore:before { + content: "\e62e" !important; +} + +.icon-jingwei:before { + content: "\e61e" !important; +} + +.icon-txc:before { + content: "\e636" !important; +} + +.icon-edas:before { + content: "\e620" !important; +} + +.icon-csb:before { + content: "\e61e" !important; +} + +.icon-ons:before { + content: "\e630" !important; +} + +.icon-drds:before { + content: "\e61f" !important; +} + +.icon-duct:before { + content: "\e62b" !important; +} + +.icon-amazon:before { + content: "\e61e" !important; +} + +.icon-autoload:before { + content: "\e61e" !important; +} + +.icon-switch:before { + content: "\e633" !important; +} + +.icon-sentinel:before { + content: "\e623" !important; +} + +.icon-preplan:before { + content: "\e631" !important; +} + +.icon-moses:before { + content: "\e61e" !important; +} + +.icon-zeus:before { + content: "\e61e" !important; +} + +.icon-athena:before { + content: "\e61e" !important; +} + +.icon-bcp:before { + content: "\e61e" !important; +} + +.icon-lark:before { + content: "\e61e" !important; +} + +.icon-nest:before { + content: "\e61e" !important; +} + +.icon-monkeyking:before { + content: "\e61e" !important; +} + +.icon-tab:before { + content: "\e61e" !important; +} + +.icon-oceanus:before { + content: "\e61e" !important; +} + +.icon-eos :before { + content: "\e61e" !important; +} + +.icon-sonar:before { + content: "\e61e" !important; +} + +.icon-ai:before { + content: "\e605" !important; +} + +.icon-hotcode:before { + content: "\e621" !important; +} + +.icon-taokeeper:before { + content: "\e624" !important; +} + +.icon-mdl:before { + content: "\e61e" !important; +} + +.icon-mw:before { + content: "\e61e" !important; +} + +.icon-default:before { + content: "\e607" !important; +} + +.icon-alitomcat:before { + content: "\e607" !important; +} \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/css/merge.css b/console/src/main/resources/static/console-fe/public/css/merge.css new file mode 100644 index 000000000..1305bd07a --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/css/merge.css @@ -0,0 +1,126 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +.CodeMirror-merge { + position: relative; + border: 1px solid #ddd; + white-space: pre; +} + +.CodeMirror-merge, .CodeMirror-merge .CodeMirror { + height: 350px; +} + +.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; } +.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; } +.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; } +.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; } + +.CodeMirror-merge-pane { + display: inline-block; + white-space: normal; + vertical-align: top; +} +.CodeMirror-merge-pane-rightmost { + position: absolute; + right: 0px; + z-index: 1; +} + +.CodeMirror-merge-gap { + z-index: 2; + display: inline-block; + height: 100%; + -moz-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + position: relative; + background: #f8f8f8; +} + +.CodeMirror-merge-scrolllock-wrap { + position: absolute; + bottom: 0; left: 50%; +} +.CodeMirror-merge-scrolllock { + position: relative; + left: -50%; + cursor: pointer; + color: #555; + line-height: 1; +} + +.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right { + position: absolute; + left: 0; top: 0; + right: 0; bottom: 0; + line-height: 1; +} + +.CodeMirror-merge-copy { + position: absolute; + cursor: pointer; + color: #44c; + z-index: 3; +} + +.CodeMirror-merge-copy-reverse { + position: absolute; + cursor: pointer; + color: #44c; +} + +.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } +.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } + +.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==); + background-position: bottom left; + background-repeat: repeat-x; +} + +.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==); + background-position: bottom left; + background-repeat: repeat-x; +} + +.CodeMirror-merge-r-chunk { background: #ffffe0; } +.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; } +.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; } +.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } + +.CodeMirror-merge-l-chunk { background: #eef; } +.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; } +.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; } +.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } + +.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; } +.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; } +.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; } + +.CodeMirror-merge-collapsed-widget:before { + content: "(...)"; +} +.CodeMirror-merge-collapsed-widget { + cursor: pointer; + color: #88b; + background: #eef; + border: 1px solid #ddf; + font-size: 90%; + padding: 0 3px; + border-radius: 4px; +} +.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; } diff --git a/console/src/main/resources/static/console-fe/public/img/TB118jPv_mWBKNjSZFBXXXxUFXa-2000-390.svg b/console/src/main/resources/static/console-fe/public/img/TB118jPv_mWBKNjSZFBXXXxUFXa-2000-390.svg new file mode 100644 index 000000000..8264c1aea --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/img/TB118jPv_mWBKNjSZFBXXXxUFXa-2000-390.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/index.html b/console/src/main/resources/static/console-fe/public/index.html new file mode 100644 index 000000000..a0046fee9 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/index.html @@ -0,0 +1,54 @@ + + + + + + + + + + Nacos + + + + + + + + + + + + +

+
+
+ + + + + + + + + + + + + + + + + + diff --git a/console/src/main/resources/static/console-fe/public/js/codemirror.addone.fullscreen.js b/console/src/main/resources/static/console-fe/public/js/codemirror.addone.fullscreen.js new file mode 100644 index 000000000..32afce68c --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/codemirror.addone.fullscreen.js @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { + if (old == CodeMirror.Init) old = false; + if (!old == !val) return; + if (val) setFullscreen(cm); + else setNormal(cm); + }); + + function setFullscreen(cm) { + var wrap = cm.getWrapperElement(); + cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, + width: wrap.style.width, height: wrap.style.height}; + wrap.style.width = ""; + wrap.style.height = "auto"; + wrap.className += " CodeMirror-fullscreen"; + document.documentElement.style.overflow = "hidden"; + cm.refresh(); + } + + function setNormal(cm) { + var wrap = cm.getWrapperElement(); + wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); + document.documentElement.style.overflow = ""; + var info = cm.state.fullScreenRestore; + wrap.style.width = info.width; wrap.style.height = info.height; + window.scrollTo(info.scrollLeft, info.scrollTop); + cm.refresh(); + } +}); \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/js/codemirror.addone.json-lint.js b/console/src/main/resources/static/console-fe/public/js/codemirror.addone.json-lint.js new file mode 100644 index 000000000..684673ef6 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/codemirror.addone.json-lint.js @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +// Depends on jsonlint.js from https://github.com/zaach/jsonlint + +// declare global: jsonlint + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.registerHelper("lint", "json", function(text) { + var found = []; + jsonlint.parseError = function(str, hash) { + var loc = hash.loc; + found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), + to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), + message: str}); + }; + try { jsonlint.parse(text); } + catch(e) {} + return found; +}); + +}); \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/js/codemirror.addone.lint.js b/console/src/main/resources/static/console-fe/public/js/codemirror.addone.lint.js new file mode 100644 index 000000000..d7363bfe4 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/codemirror.addone.lint.js @@ -0,0 +1,248 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var GUTTER_ID = "CodeMirror-lint-markers"; + + function showTooltip(e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip"; + tt.appendChild(content.cloneNode(true)); + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + if (tt.style.opacity != null) tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function showTooltipFor(e, content, node) { + var tooltip = showTooltip(e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n && n.nodeType == 11) n = n.host; + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + + function LintState(cm, options, hasGutter) { + this.marked = []; + this.options = options; + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; + this.waitingFor = 0 + } + + function parseOptions(_cm, options) { + if (options instanceof Function) return {getAnnotations: options}; + if (!options || options === true) options = {}; + return options; + } + + function clearMarks(cm) { + var state = cm.state.lint; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); + for (var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked.length = 0; + } + + function makeMarker(labels, severity, multiple, tooltips) { + var marker = document.createElement("div"), inner = marker; + marker.className = "CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker-multiple"; + } + + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(e, labels, inner); + }); + + return marker; + } + + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; + } + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); + } + return lines; + } + + function annotationTooltip(ann) { + var severity = ann.severity; + if (!severity) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message-" + severity; + tip.appendChild(document.createTextNode(ann.message)); + return tip; + } + + function lintAsync(cm, getAnnotations, passOptions) { + var state = cm.state.lint + var id = ++state.waitingFor + function abort() { + id = -1 + cm.off("change", abort) + } + cm.on("change", abort) + getAnnotations(cm.getValue(), function(annotations, arg2) { + cm.off("change", abort) + if (state.waitingFor != id) return + if (arg2 && annotations instanceof CodeMirror) annotations = arg2 + updateLinting(cm, annotations) + }, passOptions, cm); + } + + function startLinting(cm) { + var state = cm.state.lint, options = state.options; + var passOptions = options.options || options; // Support deprecated passing of `options` property in options + var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!getAnnotations) return; + if (options.async || getAnnotations.async) { + lintAsync(cm, getAnnotations, passOptions) + } else { + updateLinting(cm, getAnnotations(cm.getValue(), passOptions, cm)); + } + } + + function updateLinting(cm, annotationsNotSorted) { + clearMarks(cm); + var state = cm.state.lint, options = state.options; + + var annotations = groupByLine(annotationsNotSorted); + + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; + + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); + + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!severity) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); + + if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); + + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint-mark-" + severity, + __annotation: ann + })); + } + + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, + state.options.tooltips)); + } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); + } + + function onChange(cm) { + var state = cm.state.lint; + if (!state) return; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); + } + + function popupTooltips(annotations, e) { + var target = e.target || e.srcElement; + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(e, tooltip, target); + } + + function onMouseOver(cm, e) { + var target = e.target || e.srcElement; + if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; + var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; + var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; + for (var i = 0; i < spans.length; ++i) { + annotations.push(spans[i].__annotation); + } + if (annotations.length) popupTooltips(annotations, e); + } + + CodeMirror.defineOption("lint", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + if (cm.state.lint.options.lintOnChange !== false) + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + clearTimeout(cm.state.lint.timeout); + delete cm.state.lint; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); + if (state.options.lintOnChange !== false) + cm.on("change", onChange); + if (state.options.tooltips != false) + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + + startLinting(cm); + } + }); + + CodeMirror.defineExtension("performLint", function() { + if (this.state.lint) startLinting(this); + }); +}); \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/js/codemirror.js b/console/src/main/resources/static/console-fe/public/js/codemirror.js new file mode 100644 index 000000000..00872a7c2 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/codemirror.js @@ -0,0 +1,9512 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +// This is CodeMirror (http://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + +// Kludges for bugs and behavior differences that can't be feature +// detected are enabled based on userAgent etc sniffing. +var userAgent = navigator.userAgent; +var platform = navigator.platform; + +var gecko = /gecko\/\d/i.test(userAgent); +var ie_upto10 = /MSIE \d/.test(userAgent); +var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); +var edge = /Edge\/(\d+)/.exec(userAgent); +var ie = ie_upto10 || ie_11up || edge; +var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); +var webkit = !edge && /WebKit\//.test(userAgent); +var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); +var chrome = !edge && /Chrome\//.test(userAgent); +var presto = /Opera\//.test(userAgent); +var safari = /Apple Computer/.test(navigator.vendor); +var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); +var phantom = /PhantomJS/.test(userAgent); + +var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); +var android = /Android/.test(userAgent); +// This is woefully incomplete. Suggestions for alternative methods welcome. +var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); +var mac = ios || /Mac/.test(platform); +var chromeOS = /\bCrOS\b/.test(userAgent); +var windows = /win/i.test(platform); + +var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); +if (presto_version) { presto_version = Number(presto_version[1]); } +if (presto_version && presto_version >= 15) { presto = false; webkit = true; } +// Some browsers use the wrong event properties to signal cmd/ctrl on OS X +var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); +var captureRightClick = gecko || (ie && ie_version >= 9); + +function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + +var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } +}; + +function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e +} + +function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) +} + +function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e +} +// wrapper for elt, which removes the elt from the accessibility tree +function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e +} + +var range; +if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r +}; } +else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r +}; } + +function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) +} + +function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = document.activeElement; + } catch(e) { + activeElement = document.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement +} + +function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } +} +function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b +} + +var selectInput = function(node) { node.select(); }; +if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } +else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + +function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} +} + +function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target +} + +// Counts the column offset in a string, taking tabs into account. +// Used mostly to find indentation. +function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } +} + +var Delayed = function() {this.id = null;}; +Delayed.prototype.set = function (ms, f) { + clearTimeout(this.id); + this.id = setTimeout(f, ms); +}; + +function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 +} + +// Number of pixels added to scroller and sizer to hide scrollbar +var scrollerGap = 30; + +// Returned or thrown by various protocols to signal 'I'm not +// handling this'. +var Pass = {toString: function(){return "CodeMirror.Pass"}}; + +// Reused option objects for setSelection & friends +var sel_dontScroll = {scroll: false}; +var sel_mouse = {origin: "*mouse"}; +var sel_move = {origin: "+move"}; + +// The inverse of countColumn -- find the offset that corresponds to +// a particular column. +function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } +} + +var spaceStrs = [""]; +function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] +} + +function lst(arr) { return arr[arr.length-1] } + +function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out +} + +function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); +} + +function nothing() {} + +function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst +} + +var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; +function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) +} +function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) +} + +function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true +} + +// Extending unicode characters. A series of a non-extending char + +// any number of extending chars is treated as a single unit as far +// as editing and measuring is concerned. This is not fully correct, +// since some scripts/fonts/browsers also treat other configurations +// of code points as a group. +var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; +function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + +// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. +function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos +} + +// Returns the value from the range [`from`; `to`] that satisfies +// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`. +function findFirst(pred, from, to) { + for (;;) { + if (Math.abs(from - to) <= 1) { return pred(from) ? from : to } + var mid = Math.floor((from + to) / 2); + if (pred(mid)) { to = mid; } + else { from = mid; } + } +} + +// The display handles the DOM integration, both for input reading +// and content drawing. It holds references to DOM nodes and +// display-related state. + +function Display(place, doc, input) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + input.init(d); +} + +// Find the line object corresponding to the given line number. +function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] +} + +// Get the part of a document between two positions, as an array of +// strings. +function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out +} +// Get the lines between from and to, as array of strings. +function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out +} + +// Update the height of a line, propagating the height change +// upwards to parent nodes. +function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } +} + +// Given a line object, find its line number by walking up through +// its parent links. +function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first +} + +// Find the line at the given vertical position, using the height +// information in the document tree. +function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i +} + +function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + +function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) +} + +// A Pos instance represents a position within the text. +function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; +} + +// Compare two positions, return 0 if they are the same, a negative +// number when a is less, and a positive number otherwise. +function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + +function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + +function copyPos(x) {return Pos(x.line, x.ch)} +function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } +function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + +// Most of the external API clips given positions to make sure they +// actually exist within the document. +function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} +function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) +} +function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } +} +function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out +} + +// Optimize some code when these features are not used. +var sawReadOnlySpans = false; +var sawCollapsedSpans = false; + +function seeReadOnlySpans() { + sawReadOnlySpans = true; +} + +function seeCollapsedSpans() { + sawCollapsedSpans = true; +} + +// TEXTMARKER SPANS + +function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; +} + +// Search an array of spans for a span matching the given marker. +function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } +} +// Remove a span from an array, returning undefined if no spans are +// left (we don't store arrays for lines without spans). +function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r +} +// Add a span to a line. +function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.attachLine(line); +} + +// Used for the algorithm that adjusts markers for a change in the +// document. These functions cut an array of spans at a given +// character position, returning an array of remaining chunks (or +// undefined if nothing remains). +function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw +} +function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw +} + +// Given a change object, compute the new set of marker spans that +// cover the line in which the change took place. Removes spans +// entirely within the change, reconnects spans belonging to the +// same marker that appear on both sides of the change, and cuts off +// spans partially within the change. Returns an array of span +// arrays with one element for each line in (after) the change. +function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers +} + +// Remove spans that are empty and don't have a clearWhenEmpty +// option of false. +function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans +} + +// Used to 'clip' out readOnly ranges when making a change. +function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts +} + +// Connect or disconnect spans from a line. +function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; +} +function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; +} + +// Helpers used when computing which overlapping collapsed span +// counts as the larger one. +function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } +function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + +// Returns a number indicating which of two overlapping collapsed +// spans is larger (and thus includes the other). Falls back to +// comparing ids when the spans cover exactly the same range. +function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id +} + +// Find out whether a line ends or starts in a collapsed span. If +// so, return the marker for that span. +function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found +} +function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } +function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + +// Test whether there exists a collapsed span that partially +// overlaps (covers the start or end, but not both) of a new span. +// Such overlap is not allowed. +function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { + var line = getLine(doc, lineNo$$1); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } +} + +// A visual line is a line as drawn on the screen. Folding, for +// example, can cause multiple logical lines to appear on the same +// visual line. This finds the start of the visual line that the +// given line is part of (usually that is the line itself). +function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line +} + +function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line +} + +// Returns an array of logical lines that continue the visual line +// started by the argument, or undefined if there are no such lines. +function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines +} + +// Get the line number of the start of the visual line that the +// given line number is part of. +function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) +} + +// Get the line number of the start of the next visual line after +// the given line. +function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 +} + +// Compute whether a line is hidden. Lines count as hidden when they +// are part of a visual line that starts with another line, or when +// they are entirely covered by collapsed, non-widget span. +function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } +} +function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } +} + +// Find the height above the given line. +function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h +} + +// Compute the character length of a line, taking into account +// collapsed ranges (see markText) that might hide parts, and join +// other lines onto it. +function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len +} + +// Find the longest line in the document. +function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); +} + +// BIDI HELPERS + +function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr") } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } +} + +var bidiOther = null; +function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther +} + +// Bidirectional ordering algorithm +// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm +// that this (partially) implements. + +// One-char codes used for character types: +// L (L): Left-to-Right +// R (R): Right-to-Left +// r (AL): Right-to-Left Arabic +// 1 (EN): European Number +// + (ES): European Number Separator +// % (ET): European Number Terminator +// n (AN): Arabic Number +// , (CS): Common Number Separator +// m (NSM): Non-Spacing Mark +// b (BN): Boundary Neutral +// s (B): Paragraph Separator +// t (S): Segment Separator +// w (WS): Whitespace +// N (ON): Other Neutrals + +// Returns null if characters are ordered as they appear +// (left-to-right), or an array of sections ({from, to, level} +// objects) in the order in which they occur visually. +var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + + return direction == "rtl" ? order.reverse() : order + } +})(); + +// Get the bidi ordering for the given line (and cache it). Returns +// false for lines that are fully left-to-right, and an array of +// BidiSpan objects otherwise. +function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order +} + +function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target +} + +function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") +} + +function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0) { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") +} + +function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null +} + +// EVENT HANDLING + +// Lightweight event framework. on/off also work on DOM nodes, +// registering native DOM handlers. + +var noHandlers = []; + +var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map$$1 = emitter._handlers || (emitter._handlers = {}); + map$$1[type] = (map$$1[type] || noHandlers).concat(f); + } +}; + +function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers +} + +function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } +} + +function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } +} + +// The DOM events that CodeMirror handles can be overridden by +// registering a (non-DOM) handler on the editor for the event name, +// and preventDefault-ing the event in that handler. +function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore +} + +function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } +} + +function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 +} + +// Add on and off methods to a constructor's prototype, to make +// registering events on such objects more convenient. +function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; +} + +// Due to the fact that we still support jurassic IE versions, some +// compatibility wrappers are needed. + +function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } +} +function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } +} +function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false +} +function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + +function e_target(e) {return e.target || e.srcElement} +function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b +} + +// Detect drag-and-drop +var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div +}(); + +var zwspSupported; +function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node +} + +// Feature-detect IE's crummy client rect reporting for bidi text +var badBidiRects; +function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) +} + +// See if "".split is the broken IE version, if so, provide an +// alternative way to split lines. +var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result +} : function (string) { return string.split(/\r\n?|\n/); }; + +var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } +} : function (te) { + var range$$1; + try {range$$1 = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range$$1 || range$$1.parentElement() != te) { return false } + return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 +}; + +var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" +})(); + +var badZoomedRects = null; +function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 +} + +// Known modes, by name and by MIME +var modes = {}; +var mimeModes = {}; + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; +} + +function defineMIME(mime, spec) { + mimeModes[mime] = spec; +} + +// Given a MIME type, a {name, ...options} config object, or a name +// string, return a mode config object. +function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } +} + +// Given a mode spec (anything that resolveMode accepts), find and +// initialize an actual mode object. +function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj +} + +// This can be used to attach properties to mode objects from +// outside the actual mode definition. +var modeExtensions = {}; +function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); +} + +function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate +} + +// Given a mode and a state (for that mode), find the inner mode and +// state at the position that the state refers to. +function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} +} + +function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true +} + +// STRING STREAM + +// Fed to the mode parsers, provides helper functions to make +// parsers more succinct. + +var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; +}; + +StringStream.prototype.eol = function () {return this.pos >= this.string.length}; +StringStream.prototype.sol = function () {return this.pos == this.lineStart}; +StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; +StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } +}; +StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} +}; +StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start +}; +StringStream.prototype.eatSpace = function () { + var this$1 = this; + + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } + return this.pos > start +}; +StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; +StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} +}; +StringStream.prototype.backUp = function (n) {this.pos -= n;}; +StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) +}; +StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) +}; +StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } +}; +StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; +StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } +}; +StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) +}; + +var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; +}; + +var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; +}; + +Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line +}; + +Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } +}; + +Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } +}; + +Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state +}; + + +// Compute a style array (an array starting with a mode generation +// -- for invalidation -- followed by pairs of end positions and +// style strings), which is used to highlight the tokens on the +// line. +function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + context.state = state; + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} +} + +function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles +} + +function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context +} + +// Lightweight form of highlight -- proceed over this line and +// update state, but don't save a style array. Used for lines that +// aren't currently visible. +function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } +} + +function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } +} + +function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") +} + +var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; +}; + +// Utility for getTokenAt and getLineTokens +function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) +} + +function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type +} + +// Run the given mode's parser over a line, calling f for each token. +function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } +} + +// Finds the line to start with when starting a parse. Tries to +// find a line with a stateAfter, so that it can start with a +// valid state. If that fails, it returns the line with the +// smallest indentation, which tends to need the least context to +// parse correctly. +function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline +} + +function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); +} + +// LINE DATA STRUCTURE + +// Line objects. These hold state related to a line, including +// highlighting info (the styles array). +var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; +}; + +Line.prototype.lineNo = function () { return lineNo(this) }; +eventMixin(Line); + +// Change the content (text, markers) of a line. Automatically +// invalidates cached information and tries to re-estimate the +// line's height. +function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } +} + +// Detach a line from the document tree and its markers. +function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); +} + +// Convert a style as returned by a mode (either null, or a string +// containing one or more styles) to a CSS style. This is cached, +// and also looks for line-wide styles. +var styleToClassCache = {}; +var styleToClassCacheWithMode = {}; +function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) +} + +// Render the DOM representation of the text of a line. Also builds +// up a 'line map', which points at the DOM nodes that represent +// specific stretches of text, and is used by the measuring code. +// The returned object contains the DOM node, this map, and +// information about line-wide styles that were set by the mode. +function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder +} + +function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token +} + +// Build up the DOM representation for a single token, and add it to +// the line map. Takes care to render special characters separately. +function buildToken(builder, text, style, startStyle, endStyle, title, css) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (title) { token.title = title; } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); +} + +function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result +} + +// Work around nonsense dimensions being reported for stretches of +// right-to-left text. +function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, title, css) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } +} + +function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; +} + +// Outputs a number of spans to make up a line, taking highlighting +// and marked text into account. +function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = title = css = ""; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + if (m.title && !title) { title = m.title; } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } +} + + +// These objects are used to represent the visible (currently drawn) +// part of the document. A LineView may correspond to multiple +// logical lines, if those are connected by collapsed ranges. +function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); +} + +// Create a range of LineView objects for the given lines. +function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array +} + +var operationGroup = null; + +function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } +} + +function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) +} + +function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } +} + +var orphanDelayedCallbacks = null; + +// Often, we want to signal events at a point where we are in the +// middle of some work, but don't want the handler to start calling +// other methods on the editor, which might be in an inconsistent +// state or simply not expect any other events to happen. +// signalLater looks whether there are any handlers, and schedules +// them to be executed when the last operation ends, or, if no +// operation is active, when a timeout fires. +function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); +} + +function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } +} + +// When an aspect of a line changes, a string is added to +// lineView.changes. This updates the relevant part of the line's +// DOM structure. +function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; +} + +// Lines with gutter elements, widgets or a background class need to +// be wrapped, and have the extra elements added to the wrapper div +function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node +} + +function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } +} + +// Wrapper around buildLineContent which will reuse the structure +// in display.externalMeasured when possible. +function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) +} + +// Redraw the line's text. Interacts with the background and text +// classes because the mode may output tokens that influence these +// classes. +function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } +} + +function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; +} + +function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } +} + +function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (node.className == "CodeMirror-linewidget") + { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); +} + +// Build a line's DOM representation from scratch +function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node +} + +// A lineView may contain multiple logical lines (when merged by +// collapsed spans). The widgets for all of them need to be drawn. +function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } +} + +function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } +} + +function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } +} + +function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight +} + +// Return true when the given mouse event happened in a widget +function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } +} + +// POSITION MEASUREMENT + +function paddingTop(display) {return display.lineSpace.offsetTop} +function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} +function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data +} + +function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } +function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth +} +function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight +} + +// Ensure the lineView.wrapping.heights array is populated. This is +// an array of bottom offsets for the lines that make up a drawn +// line. When lineWrapping is on, there might be more than one +// height. +function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } +} + +// Find a line map (mapping character offsets to text nodes) and a +// measurement cache for the given line number. (A line view might +// contain multiple lines when collapsed ranges are present.) +function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } +} + +// Render a line into the hidden node display.externalMeasured. Used +// when measurement is needed for a line that's not in the viewport. +function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view +} + +// Get a {top, bottom, left, right} box (in line-local coordinates) +// for a given character. +function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) +} + +// Find a line view that corresponds to the given line number. +function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } +} + +// Measurement can be split in two steps, the set-up work that +// applies to the whole line, and the measurement of the actual +// character. Functions like coordsChar, that need to do a lot of +// measurements in a row, can thus ensure that the set-up work is +// only done once. +function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } +} + +// Given a prepared measurement object, measures the position of an +// actual character (or fetches it from the cache). +function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} +} + +var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + +function nodeAndOffsetInLineMap(map$$1, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map$$1.length; i += 3) { + mStart = map$$1[i]; + mEnd = map$$1[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map$$1[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { + node = map$$1[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { + node = map$$1[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} +} + +function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect +} + +function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result +} + +// Work around problem with bounding client rects on ranges being +// returned incorrectly when zoomed on IE10 and below. +function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} +} + +function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } +} + +function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } +} + +function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; +} + +function pageScrollX() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } + return window.pageXOffset || (document.documentElement || document.body).scrollLeft +} +function pageScrollY() { + if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } + return window.pageYOffset || (document.documentElement || document.body).scrollTop +} + +// Converts a {top, bottom, left, right} box from line-local +// coordinates into another coordinate system. Context may be one of +// "line", "div" (display.lineDiv), "local"./null (editor), "window", +// or "page". +function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) { + var size = widgetHeight(lineObj.widgets[i]); + rect.top += size; rect.bottom += size; + } } } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect +} + +// Coverts a box from "div" coords to another coordinate system. +// Context may be "window", "page", "div", or "local"./null. +function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} +} + +function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) +} + +// Returns a box for a given cursor position, which may have an +// 'other' property containing the position of the secondary cursor +// on a bidi boundary. +// A cursor Pos(line, char, "before") is on the same visual line as `char - 1` +// and after `char - 1` in writing order of `char - 1` +// A cursor Pos(line, char, "after") is on the same visual line as `char` +// and before `char` in writing order of `char` +// Examples (upper-case letters are RTL, lower-case are LTR): +// Pos(0, 1, ...) +// before after +// ab a|b a|b +// aB a|B aB| +// Ab |Ab A|b +// AB B|A B|A +// Every position after the last character on a line is considered to stick +// to the last character on the line. +function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = (part.level % 2) != 0; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val +} + +// Used to cheaply estimate the coordinates for a position. Used for +// intermediate scroll updates. +function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} +} + +// Positions returned by coordsChar contain some extra information. +// xRel is the relative x position of the input coordinates compared +// to the found position (so xRel > 0 means the coordinates are to +// the right of the character position, for example). When outside +// is true, that means the coordinates lie outside the line's +// vertical range. +function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = true; } + return pos +} + +// Compute the character position closest to the given coordinates. +// Input must be lineSpace-local ("div" coordinate system). +function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var merged = collapsedSpanAtEnd(lineObj); + var mergedPos = merged && merged.find(0, true); + if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) + { lineN = lineNo(lineObj = mergedPos.to.line); } + else + { return found } + } +} + +function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }; + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end); + return {begin: begin, end: end} +} + +function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) +} + +function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { + y -= heightAtLine(lineObj); + var begin = 0, end = lineObj.text.length; + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + var pos; + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + if (cm.options.lineWrapping) { + var assign; + ((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign)); + } + pos = new Pos(lineNo$$1, Math.floor(begin + (end - begin) / 2)); + var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left; + var dir = beginLeft < x ? 1 : -1; + var prevDiff, diff = beginLeft - x, prevPos; + var steps = Math.ceil((end - begin) / 4); + outer: do { + prevDiff = diff; + prevPos = pos; + var i = 0; + for (; i < steps; ++i) { + var prevPos$1 = pos; + pos = moveVisually(cm, lineObj, pos, dir); + if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) { + pos = prevPos$1; + break outer + } + } + diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x; + if (steps > 1) { + var diff_change_per_step = Math.abs(diff - prevDiff) / steps; + steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step)); + dir = diff < 0 ? 1 : -1; + } + } while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff))))) + if (Math.abs(diff) > Math.abs(prevDiff)) { + if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") } + pos = prevPos; + } + } else { + var ch = findFirst(function (ch) { + var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); + if (box.top > y) { + // For the cursor stickiness + end = Math.min(ch, end); + return true + } + else if (box.bottom <= y) { return false } + else if (box.left > x) { return true } + else if (box.right < x) { return false } + else { return (x - box.left < box.right - x) } + }, begin, end); + ch = skipExtendingChars(lineObj.text, ch, 1); + pos = new Pos(lineNo$$1, ch, ch == end ? "before" : "after"); + } + var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure); + if (y < coords.top || coords.bottom < y) { pos.outside = true; } + pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0); + return pos +} + +var measureText; +// Compute the default text height. +function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 +} + +// Compute the default character width. +function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 +} + +// Do a bulk-read of the DOM positions and sizes needed to draw the +// view, so that we don't interleave reading and writing to the DOM. +function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; + width[cm.options.gutters[i]] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} +} + +// Computes display.scroller.scrollLeft + display.gutters.offsetWidth, +// but using getBoundingClientRect to get a sub-pixel-accurate +// result. +function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left +} + +// Returns a function that estimates the height of a line, to use as +// first approximation until the line becomes visible (and is thus +// properly measurable). +function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } +} + +function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); +} + +// Given a mouse event, find the corresponding position. If liberal +// is false, it checks whether a gutter or scrollbar was clicked, +// and returns null if it was. forRect is used by rectangular +// selections, and tries to estimate a character position even for +// coordinates beyond the right of the text. +function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords +} + +// Find the view element corresponding to a given line. Return null +// when the line isn't visible. +function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } +} + +function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); +} + +function prepareSelection(cm, primary) { + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (primary === false && i == doc.sel.primIndex) { continue } + var range$$1 = doc.sel.ranges[i]; + if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } + var collapsed = range$$1.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + { drawSelectionCursor(cm, range$$1.head, curFragment); } + if (!collapsed) + { drawSelectionRange(cm, range$$1, selFragment); } + } + return result +} + +// Draws a cursor for the given range +function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } +} + +// Draws the given range as a highlighted selection +function drawSelectionRange(cm, range$$1, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) { + var leftPos = coords(from, "left"), rightPos, left, right; + if (from == to) { + rightPos = leftPos; + left = right = leftPos.left; + } else { + rightPos = coords(to - 1, "right"); + if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } + left = leftPos.left; + right = rightPos.right; + } + if (fromArg == null && from == 0) { left = leftSide; } + if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part + add(left, leftPos.top, null, leftPos.bottom); + left = leftSide; + if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top); } + } + if (toArg == null && to == lineLen) { right = rightSide; } + if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) + { start = leftPos; } + if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) + { end = rightPos; } + if (left < leftSide + 1) { left = leftSide; } + add(left, rightPos.top, right - left, rightPos.bottom); + }); + return {start: start, end: end} + } + + var sFrom = range$$1.from(), sTo = range$$1.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); +} + +// Cursor-blinking +function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, + cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } +} + +function ensureFocus(cm) { + if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } +} + +function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + onBlur(cm); + } }, 100); +} + +function onFocus(cm, e) { + if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); +} +function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); +} + +// Read the actual heights of the rendered lines, and update their +// stored heights to match. +function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], height = (void 0); + if (cur.hidden) { continue } + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + } + var diff = cur.line.height - height; + if (height < 2) { height = textHeight(display); } + if (diff > .005 || diff < -.005) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + } +} + +// Read and store the height of line widgets associated with the +// given line. +function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) + { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight; } } +} + +// Compute the lines that are visible in a given viewport (defaults +// the the current scroll position). viewport may contain top, +// height, and ensure (see op.scrollToPos) properties. +function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} +} + +// Re-align line numbers and gutter marks to compensate for +// horizontal scrolling. +function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } +} + +// Used to ensure that the line number gutter is still the right +// size for the current document size. Returns true when an update +// is needed. +function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm); + return true + } + return false +} + +// SCROLLING THINGS INTO VIEW + +// If an editor sits on the top or bottom of the window, partially +// scrolled out of view, this ensures that the cursor is visible. +function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } +} + +// Scroll a given position into view (immediately), verifying that +// it actually became visible (as line heights are accurately +// measured, the position of something may 'drift' during drawing). +function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect +} + +// Scroll a given set of coordinates into view (immediately). +function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } +} + +// Calculate a new scroll position needed to scroll the given +// rectangle into view. Returns an object with scrollTop and +// scrollLeft properties. When these are undefined, the +// vertical/horizontal position does not need to be adjusted. +function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; + var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result +} + +// Store a relative adjustment to the scroll position in the current +// operation (to be applied when the operation finishes). +function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; +} + +// Make sure that at the end of the operation the current cursor is +// shown. +function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; +} + +function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } +} + +function scrollToRange(cm, range$$1) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range$$1; +} + +// When an operation has its scrollToPos property set, and another +// scroll action is applied before the end of the operation, this +// 'simulates' scrolling that position into view in a cheap way, so +// that the effect of intermediate scroll commands is not ignored. +function resolveScrollToPos(cm) { + var range$$1 = cm.curOp.scrollToPos; + if (range$$1) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); + scrollToCoordsRange(cm, from, to, range$$1.margin); + } +} + +function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); +} + +// Sync the scrollable area and scrollbars, ensure the viewport +// covers the visible area. +function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); +} + +function setScrollTop(cm, val, forceScroll) { + val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } +} + +// Sync scroller and scrollbar, ensure the gutter elements are +// aligned. +function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); +} + +// SCROLLBARS + +// Prepare DOM reads needed to update the scrollbars. Done in one +// shot to minimize update/measure roundtrips. +function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } +} + +var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } +}; + +NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} +}; + +NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } +}; + +NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } +}; + +NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; +}; + +NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.pointerEvents = "auto"; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); +}; + +NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); +}; + +var NullScrollbars = function () {}; + +NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; +NullScrollbars.prototype.setScrollLeft = function () {}; +NullScrollbars.prototype.setScrollTop = function () {}; +NullScrollbars.prototype.clear = function () {}; + +function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } +} + +// Re-synchronize the fake scrollbars with the actual size of the +// content. +function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } +} + +var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + +function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } +} + +// Operations are used to wrap a series of changes to the editor +// state in such a way that each change won't have to update the +// cursor and display (which would be awkward, slow, and +// error-prone). Instead, display updates are batched and then all +// combined and executed at once. + +var nextOpId = 0; +// Start a new operation. +function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: null, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId // Unique ID + }; + pushOperation(cm.curOp); +} + +// Finish an operation, updating the display and signalling delayed events +function endOperation(cm) { + var op = cm.curOp; + finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); +} + +// The DOM updates done when an operation finishes are batched so +// that the minimum number of relayouts are required. +function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } +} + +function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); +} + +function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); +} + +function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(op.focus); } +} + +function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } +} + +function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } +} + +// Run the given function in an operation +function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } +} +// Wraps a function in an operation. Returns the wrapped function. +function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } +} +// Used to add methods to editor and doc instances, wrapping them in +// operations. +function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } +} +function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } +} + +// Updates the display.view data structure for a given change to the +// document. From and to are in pre-change coordinates. Lendiff is +// the amount of lines added or subtracted by the change. This is +// used for changes that span multiple lines, or change the way +// lines are divided into visual lines. regLineChange (below) +// registers single-line changes. +function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } +} + +// Register a change to a single line. Type must be one of "text", +// "gutter", "class", "widget" +function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } +} + +// Clear the view. +function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; +} + +function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} +} + +// Force the view to cover a given range, adding empty view element +// or clipping off existing ones as needed. +function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; +} + +// Count the number of lines in the view whose DOM representation is +// out of date (or nonexistent). +function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty +} + +// HIGHLIGHT WORKER + +function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } +} + +function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } +} + +// DISPLAY DRAWING + +var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; +}; + +DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } +}; +DisplayUpdate.prototype.finish = function () { + var this$1 = this; + + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this$1.events[i]); } +}; + +function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } +} + +function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result +} + +function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + snapshot.activeElt.focus(); + if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range$$1 = document.createRange(); + range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range$$1.collapse(false); + sel.removeAllRanges(); + sel.addRange(range$$1); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } +} + +// Does the actual updating of the line display. Bails out +// (returning false) when there is nothing to be done and forced is +// false. +function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true +} + +function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } +} + +function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } +} + +// Sync the actual display DOM structure with display.view, removing +// nodes for lines that are no longer in view, and creating the ones +// that are not there yet, and updating the ones that are out of +// date. +function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) { + } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } +} + +function updateGutterSpace(cm) { + var width = cm.display.gutters.offsetWidth; + cm.display.sizer.style.marginLeft = width + "px"; +} + +function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; +} + +// Rebuild the gutter elements, ensure the margin to the left of the +// code matches their width. +function updateGutters(cm) { + var gutters = cm.display.gutters, specs = cm.options.gutters; + removeChildren(gutters); + var i = 0; + for (; i < specs.length; ++i) { + var gutterClass = specs[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + cm.display.lineGutter = gElt; + gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = i ? "" : "none"; + updateGutterSpace(cm); +} + +// Make sure the gutters options contains the element +// "CodeMirror-linenumbers" when the lineNumbers option is true. +function setGuttersForLineNumbers(options) { + var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + if (found == -1 && options.lineNumbers) { + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + } else if (found > -1 && !options.lineNumbers) { + options.gutters = options.gutters.slice(0); + options.gutters.splice(found, 1); + } +} + +// Since the delta values reported on mouse wheel events are +// unstandardized between browsers and even browser versions, and +// generally horribly unpredictable, this code starts by measuring +// the scroll effect that the first few mouse wheel events have, +// and, from that, detects the way it can convert deltas to pixel +// offsets afterwards. +// +// The reason we want to know the amount a wheel event will scroll +// is that it gives us a chance to update the display before the +// actual scrolling happens, reducing flickering. + +var wheelSamples = 0; +var wheelPixelsPerUnit = null; +// Fill in a browser-detected starting value on browsers where we +// know one. These don't have to be accurate -- the result of them +// being wrong would just be a slight flicker on the first wheel +// scroll (if it is large enough). +if (ie) { wheelPixelsPerUnit = -.53; } +else if (gecko) { wheelPixelsPerUnit = 15; } +else if (chrome) { wheelPixelsPerUnit = -.7; } +else if (safari) { wheelPixelsPerUnit = -1/3; } + +function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} +} +function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta +} + +function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } +} + +// Selection objects are immutable. A new one is created every time +// the selection changes. A selection is one or more non-overlapping +// (and non-touching) ranges, sorted, and an integer that indicates +// which one is the primary selection (the one that's scrolled into +// view, that getCursor returns, etc). +var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; +}; + +Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + +Selection.prototype.equals = function (other) { + var this$1 = this; + + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this$1.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true +}; + +Selection.prototype.deepCopy = function () { + var this$1 = this; + + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } + return new Selection(out, this.primIndex) +}; + +Selection.prototype.somethingSelected = function () { + var this$1 = this; + + for (var i = 0; i < this.ranges.length; i++) + { if (!this$1.ranges[i].empty()) { return true } } + return false +}; + +Selection.prototype.contains = function (pos, end) { + var this$1 = this; + + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this$1.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 +}; + +var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; +}; + +Range.prototype.from = function () { return minPos(this.anchor, this.head) }; +Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; +Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + +// Take an unsorted, potentially overlapping set of ranges, and +// build a selection out of it. 'Consumes' ranges array (modifying +// it). +function normalizeSelection(ranges, primIndex) { + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + if (cmp(prev.to(), cur.from()) >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) +} + +function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) +} + +// Compute the position of the end of a change (its 'to' property +// refers to the pre-change end). +function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) +} + +// Adjust a position to refer to the post-change position of the +// same text, or the end of the change if the change covers it. +function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) +} + +function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(out, doc.sel.primIndex) +} + +function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } +} + +// Used by replaceSelections to allow moving the selection to the +// start or around the replaced test. Hint may be "start" or "around". +function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) +} + +// Used to get the editor into a consistent state again when options change. + +function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); +} + +function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } +} + +// DOCUMENT DATA STRUCTURE + +// By default, updates that start and end at the beginning of a line +// are treated specially, in order to make the association of line +// widgets and marker elements with the text behave more intuitive. +function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) +} + +// Perform a change on the document data structure. +function updateDoc(doc, change, markedSpans, estimateHeight$$1) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight$$1); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); +} + +// Call f for all linked documents. +function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); +} + +// Attach a document to an editor. +function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); +} + +function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); +} + +function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); +} + +function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1; +} + +// Create a history change event from an updateDoc-style change +// object. +function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange +} + +// Pop all selection events off the end of a history array. Stop at +// a change event. +function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } +} + +// Find the top change event in the history. Pop off selection +// events that are in the way. +function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } +} + +// Register a change in the history. Merges changes that are within +// a single operation, or are close together with an origin that +// allows merging (starting with "+") into a single event. +function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } +} + +function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) +} + +// Called whenever the selection changes, sets the new selection as +// the pending selection in the history, and pushes the old pending +// selection into the 'done' array when it was significantly +// different (in number of selected ranges, emptiness, or time). +function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } +} + +function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } +} + +// Used to store marked span information in the history. +function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); +} + +// When un/re-doing restores text containing marked spans, those +// that have been explicitly cleared should not be restored. +function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null +} + +// Retrieve and filter the old marked spans stored in a change event. +function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw +} + +// Used for un/re-doing changes from the history. Combines the +// result of computing the existing spans with the set of spans that +// existed in the history (so that deleting around a span and then +// undoing brings back the span). +function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old +} + +// Used both to provide a JSON-safe object in .getHistory, and, when +// detaching a document, to split the history in two +function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy +} + +// The 'scroll' parameter given to many of these indicated whether +// the new cursor position should be scrolled into view after +// modifying the selection. + +// If shift is held or the extend flag is set, extends a range to +// include a given position (and optionally a second position). +// Otherwise, simply returns the range between the given positions. +// Used for cursor motion and such. +function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } +} + +// Extend the primary selection range, discard the rest. +function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); +} + +// Extend all selections (pos is an array of selections with length +// equal the number of selections) +function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(out, doc.sel.primIndex); + setSelection(doc, newSel, options); +} + +// Updates a single range in the selection. +function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); +} + +// Reset the selection to a single range. +function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); +} + +// Give beforeSelectionChange handlers a change to influence a +// selection update. +function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + var this$1 = this; + + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) } + else { return sel } +} + +function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } +} + +// Set a new selection. +function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); +} + +function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm) + { ensureCursorVisible(doc.cm); } +} + +function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); +} + +// Verify that the selection does not partially select any atomic +// marked ranges. +function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); +} + +// Return a selection that does not partially select any atomic +// ranges. +function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(out, sel.primIndex) : sel +} + +function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos +} + +// Ensure a given position is not inside an atomic range. +function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found +} + +function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } +} + +function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); +} + +// UPDATING + +// Allow "beforeChange" event handlers to influence a change +function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { return null } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} +} + +// Apply a change to a document, and add it to the document's +// history, and propagating it to all linked documents. +function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); } + } else { + makeChangeInner(doc, change); + } +} + +function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); +} + +// Revert a change stored in a document's history. +function makeChangeFromHistory(doc, type, allowSelectionOnly) { + if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } + else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } +} + +// Sub-views need their line numbers shifted when text is added +// above or below them in the parent document. +function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } +} + +// More lower-level change function, handling only a single document +// (not linked ones). +function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); +} + +// Handle the interaction of a change to a document with the editor +// that this document is part of. +function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; +} + +function replaceRange(doc, code, from, to, origin) { + if (!to) { to = from; } + if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); +} + +// Rebasing/resetting history to deal with externally-sourced changes + +function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } +} + +// Tries to rebase an array of history events given a change in the +// document. If the change touches the same lines as the event, the +// event, and everything 'behind' it, is discarded. If the change is +// before the event, the event's positions are updated. Uses a +// copy-on-write scheme for the positions, to avoid having to +// reallocate them all on every rebase, but also avoid problems with +// shared position objects being unsafely updated. +function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } +} + +function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); +} + +// Utility for applying a change to a line by handle or number, +// returning the number and optionally registering the line as +// changed. +function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line +} + +// The document is represented as a BTree consisting of leaves, with +// chunk of lines in them, and branches, with up to ten leaves or +// other branch nodes below them. The top node is always a branch +// node, and is the document object itself (meaning it has +// additional methods and properties). +// +// All nodes have parent links. The tree is used both to go from +// line numbers to line objects, and to go from objects to numbers. +// It also indexes by height, and is used to convert between height +// and line object, and to find the total height of the document. +// +// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + +function LeafChunk(lines) { + var this$1 = this; + + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this$1; + height += lines[i].height; + } + this.height = height; +} + +LeafChunk.prototype = { + chunkSize: function chunkSize() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function removeInner(at, n) { + var this$1 = this; + + for (var i = at, e = at + n; i < e; ++i) { + var line = this$1.lines[i]; + this$1.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function collapse(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function insertInner(at, lines, height) { + var this$1 = this; + + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } + }, + + // Used to iterate over a part of the tree. + iterN: function iterN(at, n, op) { + var this$1 = this; + + for (var e = at + n; at < e; ++at) + { if (op(this$1.lines[at])) { return true } } + } +}; + +function BranchChunk(children) { + var this$1 = this; + + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this$1; + } + this.size = size; + this.height = height; + this.parent = null; +} + +BranchChunk.prototype = { + chunkSize: function chunkSize() { return this.size }, + + removeInner: function removeInner(at, n) { + var this$1 = this; + + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this$1.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this$1.height -= oldHeight - child.height; + if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function collapse(lines) { + var this$1 = this; + + for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } + }, + + insertInner: function insertInner(at, lines, height) { + var this$1 = this; + + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this$1.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this$1.children.splice(++i, 0, leaf); + leaf.parent = this$1; + } + child.lines = child.lines.slice(0, remaining); + this$1.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function maybeSpill() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function iterN(at, n, op) { + var this$1 = this; + + for (var i = 0; i < this.children.length; ++i) { + var child = this$1.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } +}; + +// Line widgets are block elements displayed above or below a line. + +var LineWidget = function(doc, node, options) { + var this$1 = this; + + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this$1[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; +}; + +LineWidget.prototype.clear = function () { + var this$1 = this; + + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } +}; + +LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + updateLineHeight(line, line.height + diff); + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } +}; +eventMixin(LineWidget); + +function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } +} + +function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); + return widget +} + +// TEXTMARKERS + +// Created with markText and setBookmark methods. A TextMarker is a +// handle that can be used to clear or find a marked position in the +// document. Line objects hold arrays (markedSpans) containing +// {from, to, marker} object pointing to such marker objects, and +// indicating that such a marker is present on that line. Multiple +// lines may point to the same marker when it spans across lines. +// The spans will have null for their from/to properties when the +// marker continues beyond the start/end of the line. Markers have +// links back to the lines they currently touch. + +// Collapsed markers have unique ids, in order to be able to order +// them, which is needed for uniquely determining an outer marker +// when they overlap (they may nest, but not partially overlap). +var nextMarkerId = 0; + +var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; +}; + +// Clear the marker. +TextMarker.prototype.clear = function () { + var this$1 = this; + + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this$1.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this$1); + if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } +}; + +// Find the position of the marker in the document. Returns a {from, +// to} object by default. Side can be passed to get a specific side +// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the +// Pos objects returned contain a line object, rather than a line +// number (used to prevent looking up the same line twice). +TextMarker.prototype.find = function (side, lineObj) { + var this$1 = this; + + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this$1.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this$1); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} +}; + +// Signals that the marker's widget changed, and surrounding layout +// should be recomputed. +TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); +}; + +TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); +}; + +TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } +}; +eventMixin(TextMarker); + +// Create a marker, wire it up to the right lines, and +function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker +} + +// SHARED TEXTMARKERS + +// A shared marker spans multiple linked documents. It is +// implemented as a meta-marker-object controlling multiple normal +// markers. +var SharedTextMarker = function(markers, primary) { + var this$1 = this; + + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this$1; } +}; + +SharedTextMarker.prototype.clear = function () { + var this$1 = this; + + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this$1.markers[i].clear(); } + signalLater(this, "clear"); +}; + +SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) +}; +eventMixin(SharedTextMarker); + +function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) +} + +function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) +} + +function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } +} + +function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); +} + +var nextDocId = 0; +var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); +}; + +Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range$$1 = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range$$1.head; } + else if (start == "anchor") { pos = range$$1.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } + else { pos = range$$1.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + var this$1 = this; + + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this$1, ranges[i].anchor), + clipPos(this$1, ranges[i].head)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var this$1 = this; + + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var this$1 = this; + + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var this$1 = this; + + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range$$1 = sel.ranges[i]; + changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this$1, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() {this.history = new History(this.history.maxGeneration);}, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history.maxGeneration); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo$$1 = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || + span.from == null && lineNo$$1 != from.line || + span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo$$1; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo$$1; + }); + return clipPos(this, Pos(lineNo$$1, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + var this$1 = this; + + if (other instanceof CodeMirror$1) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this$1.linked[i]; + if (link.doc != other) { continue } + this$1.linked.splice(i, 1); + other.unlinkDoc(this$1); + detachSharedMarkers(findSharedMarkers(this$1)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) +}); + +// Public alias. +Doc.prototype.eachLine = Doc.prototype.iter; + +// Kludge to work around strange IE behavior where it'll sometimes +// re-fire a series of drag-related events right after the drop (#1551) +var lastDrop = 0; + +function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) + { return } + + var reader = new FileReader; + reader.onload = operation(cm, function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; } + text[i] = content; + if (++read == n) { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + } + }); + reader.readAsText(file); + }; + for (var i = 0; i < n; ++i) { loadFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e){} + } +} + +function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } +} + +function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); +} + +function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } +} + +// These must be handled carefully, because naively registering a +// handler for each editor will cause the editors to never be +// garbage collected. + +function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"); + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { f(cm); } + } +} + +var globalsRegistered = false; +function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; +} +function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); +} +// Called when the window resizes +function onResize(cm) { + var d = cm.display; + if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) + { return } + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); +} + +var keyNames = { + 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" +}; + +// Number keys +for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } +// Alphabetic keys +for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } +// Function keys +for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + +var keyMap = {}; + +keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" +}; +// Note that the save and find-related commands aren't defined by +// default. User code or addons can define them. Unknown commands +// are simply ignored. +keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + fallthrough: "basic" +}; +// Very basic readline/emacs-style bindings, which are standard on Mac. +keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", + "Ctrl-O": "openLine" +}; +keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + fallthrough: ["basic", "emacsy"] +}; +keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + +// KEYMAP DISPATCH + +function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name +} + +// This is a kludge to keep keymaps mostly working as raw objects +// (backwards compatibility) while at the same time support features +// like normalization and multi-stroke key bindings. It compiles a +// new normalized keymap, and then updates the old object to reflect +// this. +function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap +} + +function lookupKey(key, map$$1, handle, context) { + map$$1 = getKeyMap(map$$1); + var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map$$1.fallthrough) { + if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") + { return lookupKey(key, map$$1.fallthrough, handle, context) } + for (var i = 0; i < map$$1.fallthrough.length; i++) { + var result = lookupKey(key, map$$1.fallthrough[i], handle, context); + if (result) { return result } + } + } +} + +// Modifier key presses don't count as 'real' key presses for the +// purpose of keymap fallthrough. +function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" +} + +function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name +} + +// Look up the name of a key as indicated by an event object. +function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + return addModifierNames(name, event, noShift) +} + +function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val +} + +// Helper for deleting text near the selection(s), used to implement +// backspace, delete, and similar functionality. +function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); +} + +// Commands are parameter-less actions that can be performed on an +// editor, mostly used for keybindings. +var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } +}; + + +function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) +} +function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) +} +function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start +} + +// Run a handler that was bound to a key. +function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done +} + +function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) +} + +// Note that, despite the name, this function is also used to check +// for bound mouse clicks. + +var stopSeq = new Delayed; +function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); + name = seq + " " + name; + } + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + if (seq && !result && /\'$/.test(name)) { + e_preventDefault(e); + return true + } + return !!result +} + +// Handle a key from the keydown event. +function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } +} + +// Handle a key from the keypress event +function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) +} + +var lastStoppedKey = null; +function onKeyDown(e) { + var cm = this; + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } +} + +function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); +} + +function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); +} + +function onKeyPress(e) { + var cm = this; + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); +} + +var DOUBLECLICK_DELAY = 400; + +var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; +}; + +PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button +}; + +var lastClick; +var lastDoubleClick; +function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } +} + +// A mouse down can be a single click, double click, triple click, +// start of selection drag, start of text drag, new cursor +// (ctrl-click), rectangle drag (alt-drag), or xwin +// middle-click-paste. Or it might be a click on something we should +// not interfere with, such as a scrollbar or widget. +function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + window.focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { onContextMenu(cm, e); } + else { delayBlurEvent(cm); } + } +} + +function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) +} + +function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value +} + +function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } +} + +// Start a text drag. When it ends, see if any dragging actually +// happen, and treat as a click if it didn't. +function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + off(document, "mouseup", dragEnd); + off(document, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if (webkit || ie && ie_version == 9) + { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + on(document, "mouseup", dragEnd); + on(document, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + delayBlurEvent(cm); + setTimeout(function () { return display.input.focus(); }, 20); +} + +function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) +} + +// Normal selection, as opposed to text dragging. +function leftButtonSelect(cm, event, start, behavior) { + var display = cm.display, doc = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range$$1 = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } + else + { ourRange = range$$1; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range$$1 = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range$$1.anchor, anchor) > 0) { + head = range$$1.head; + anchor = minPos(oldRange.from(), range$$1.anchor); + } else { + head = range$$1.anchor; + anchor = maxPos(oldRange.to(), range$$1.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head); + setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + e_preventDefault(e); + display.input.focus(); + off(document, "mousemove", move); + off(document, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (!e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(document, "mousemove", move); + on(document, "mouseup", up); +} + + +// Determines whether an event happened in the gutter, and fires the +// handlers for the corresponding event. +function gutterEvent(cm, e, type, prevent) { + var mX, mY; + try { mX = e.clientX; mY = e.clientY; } + catch(e) { return false } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.options.gutters[i]; + signal(cm, type, cm, line, gutter, e); + return e_defaultPrevented(e) + } + } +} + +function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) +} + +// CONTEXT MENU HANDLING + +// To make the context menu work, we need to briefly unhide the +// textarea (making it as unobtrusive as possible) to let the +// right-click take effect on it. +function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + cm.display.input.onContextMenu(e); +} + +function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) +} + +function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); +} + +var Init = {toString: function(){return "CodeMirror.Init"}}; + +var defaults = {}; +var optionHandlers = {}; + +function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + guttersChanged(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("firstLineNumber", 1, guttersChanged, true); + option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); +} + +function guttersChanged(cm) { + updateGutters(cm); + regChange(cm); + alignHorizontally(cm); +} + +function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } +} + +function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); +} + +// A CodeMirror instance represents an editor. This is the object +// that user code is usually dealing with. + +function CodeMirror$1(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + setGuttersForLineNumbers(options); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + this.doc = doc; + + var input = new CodeMirror$1.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input); + display.wrapper.CodeMirror = this; + updateGutters(this); + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(bind(onFocus, this), 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this$1, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } +} + +// The default configuration options. +CodeMirror$1.defaults = defaults; +// Functions to run when options are changed. +CodeMirror$1.optionHandlers = optionHandlers; + +// Attach the necessary event handlers when initializing the editor +function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); } + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); +} + +var initHooks = []; +CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); }; + +// Indent the given line. The how parameter can be "smart", +// "add"/null, "subtract", or "prev". When aggressive is false +// (typically set to true for forced single-line indents), empty +// lines are not indented, and places where the mode returns Pass +// are left alone. +function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } +} + +// This will be set to a {lineWise: bool, text: [string]} object, so +// that, when pasting, we know what kind of selections the copied +// text was made out of. +var lastCopied = null; + +function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; +} + +function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var paste = cm.state.pasteIncoming || origin == "paste"; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasing N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range$$1 = sel.ranges[i$1]; + var from = range$$1.from(), to = range$$1.to(); + if (range$$1.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) + { from = to = Pos(from.line, 0); } + } + updateInput = cm.curOp.updateInput; + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + cm.curOp.updateInput = updateInput; + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = false; +} + +function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } +} + +function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range$$1 = sel.ranges[i]; + if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } + var mode = cm.getModeAt(range$$1.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range$$1.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) + { indented = indentLine(cm, range$$1.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } + } +} + +function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} +} + +function disableBrowserMagic(field, spellcheck) { + field.setAttribute("autocorrect", "off"); + field.setAttribute("autocapitalize", "off"); + field.setAttribute("spellcheck", !!spellcheck); +} + +function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div +} + +// The publicly visible API. Note that methodOp(f) means +// 'wrap f in an operation, performed on its `this` parameter'. + +// This is not the complete set of editor methods. Most of the +// methods defined on the Doc type are also injected into +// CodeMirror.prototype, for backwards compatibility and +// convenience. + +var addEditorMethods = function(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map$$1, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); + }, + removeKeyMap: function(map$$1) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map$$1 || maps[i].name == map$$1) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var this$1 = this; + + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this$1.state.modeGen++; + regChange(this$1); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var this$1 = this; + + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range$$1 = ranges[i]; + if (!range$$1.empty()) { + var from = range$$1.from(), to = range$$1.to(); + var start = Math.max(end, from.line); + end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this$1, j, how); } + var newRanges = this$1.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range$$1.head.line > end) { + indentLine(this$1, range$$1.head.line, how, true); + end = range$$1.head.line; + if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var this$1 = this; + + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range$$1 = this.doc.sel.primary(); + if (start == null) { pos = range$$1.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range$$1.from() : range$$1.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var this$1 = this; + + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this$1.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range$$1) { + if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) + { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range$$1.from() : range$$1.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range$$1) { + var other = findPosH(doc, range$$1.head, dir, unit, false); + return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var this$1 = this; + + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this$1, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this$1, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range$$1) { + if (collapse) + { return dir < 0 ? range$$1.from() : range$$1.to() } + var headPos = cursorCoords(this$1, range$$1.head, "div"); + if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range$$1 == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range$$1, margin) { + if (range$$1 == null) { + range$$1 = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range$$1 == "number") { + range$$1 = {from: Pos(range$$1, 0), to: null}; + } else if (range$$1.from == null) { + range$$1 = {from: range$$1, to: null}; + } + if (!range$$1.to) { range$$1.to = range$$1.from; } + range$$1.margin = margin || 0; + + if (range$$1.from.line != null) { + scrollToRange(this, range$$1); + } else { + scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo$$1 = this.display.viewFrom; + this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } + ++lineNo$$1; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; +}; + +// Used for horizontal relative motion. Dir is -1 or 1 (left or +// right), unit can be "char", "column" (like char, but doesn't +// cross line boundaries), "word" (across next word), or "group" (to +// the start of next group of word or non-word-non-whitespace +// chars). The visually param controls whether, in right-to-left +// text, direction 1 means to move towards the next index in the +// string, or towards the character to the right of the current +// position. The resulting position will have a hitSide=true +// property if it reached the end of the document. +function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + function findNextLine() { + var l = pos.line + dir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result +} + +// For relative vertical movement. Dir may be -1 or 1. Unit can be +// "page" or "line". The resulting position will have a hitSide=true +// property if it reached the end of the document. +function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target +} + +// CONTENTEDITABLE INPUT STYLE + +var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; +}; + +ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + disableBrowserMagic(div, cm.options.spellcheck); + + on(div, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); +}; + +ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = this.cm.state.focused; + return result +}; + +ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); +}; + +ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); +}; + +ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); +}; + +ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); +}; + +ContentEditableInput.prototype.rememberSelection = function () { + var sel = window.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; +}; + +ContentEditableInput.prototype.selectionInEditor = function () { + var sel = window.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) +}; + +ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor()) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } +}; +ContentEditableInput.prototype.blur = function () { this.div.blur(); }; +ContentEditableInput.prototype.getField = function () { return this.div }; + +ContentEditableInput.prototype.supportsTouch = function () { return true }; + +ContentEditableInput.prototype.receivedFocus = function () { + var input = this; + if (this.selectionInEditor()) + { this.pollSelection(); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); +}; + +ContentEditableInput.prototype.selectionChanged = function () { + var sel = window.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset +}; + +ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = window.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } +}; + +ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } +}; + +ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); +}; +ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); +}; +ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); +}; +ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); +}; + +ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } +}; + +ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; +}; + +ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } +}; + +ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); +}; + +ContentEditableInput.prototype.onContextMenu = function () {}; +ContentEditableInput.prototype.resetPosition = function () {}; + +ContentEditableInput.prototype.needsContentAttribute = true; + +function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result +} + +function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false +} + +function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + +function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(); + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + closing = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText != null) { + addText(cmText || node.textContent.replace(/\u200b/g, "")); + return + } + var markerID = node.getAttribute("cm-marker"), range$$1; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range$$1 = found[0].find())) + { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p)$/i.test(node.nodeName); + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + } + return text +} + +function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } +} + +function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map$$1 = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map$$1.length; j += 3) { + var curNode = map$$1[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map$$1[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } +} + +// TEXTAREA INPUT STYLE + +var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Tracks when input.reset has punted to just putting a short + // string into the textarea instead of the full selection. + this.inaccurateSelection = false; + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; +}; + +TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + + // Wraps and hides input textarea + var div = this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + var te = this.textarea = div.firstChild; + display.wrapper.insertBefore(div, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = true; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (input.inaccurateSelection) { + input.prevInput = ""; + input.inaccurateSelection = false; + te.value = lastCopied.text.join("\n"); + selectInput(te); + } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = true; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + cm.state.pasteIncoming = true; + input.focus(); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); +}; + +TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result +}; + +TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } +}; + +// Reset the input to correspond to the selection (or to be empty, +// when not typing and nothing is selected) +TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing) { return } + var minimal, selected, cm = this.cm, doc = cm.doc; + if (cm.somethingSelected()) { + this.prevInput = ""; + var range$$1 = doc.sel.primary(); + minimal = hasCopyEvent && + (range$$1.to().line - range$$1.from().line > 100 || (selected = cm.getSelection()).length > 1000); + var content = minimal ? "-" : selected || cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + this.inaccurateSelection = minimal; +}; + +TextareaInput.prototype.getField = function () { return this.textarea }; + +TextareaInput.prototype.supportsTouch = function () { return false }; + +TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } +}; + +TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + +TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; +}; + +TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + +// Poll for input changes, using the normal rate of polling. This +// runs as long as the editor is focused. +TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); +}; + +// When an event has just come in that is likely to add or change +// something in the input textarea, we poll faster, to ensure that +// the change appears on the screen quickly. +TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); +}; + +// Read input from the textarea, and update the document to match. +// When something is selected, it is present in the textarea, and +// selected (unless it is huge, in which case a placeholder is +// used). When nothing is selected, the cursor sits after previously +// seen text (can be empty), which is stored in prevInput (we must +// not reset the textarea when typing, because that breaks IME). +TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true +}; + +TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } +}; + +TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); +}; + +TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + input.wrapper.style.cssText = "position: absolute"; + var wrapperBox = input.wrapper.getBoundingClientRect(); + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { window.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = true; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } +}; + +TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; +}; + +TextareaInput.prototype.setUneditable = function () {}; + +TextareaInput.prototype.needsContentAttribute = false; + +function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm +} + +function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; +} + +// EDITOR CONSTRUCTOR + +defineOptions(CodeMirror$1); + +addEditorMethods(CodeMirror$1); + +// Set up methods on CodeMirror's prototype to redirect to the editor's document. +var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); +for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror$1.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + +eventMixin(Doc); + +// INPUT HANDLING + +CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + +// MODE DEFINITION AND QUERYING + +// Extra arguments are stored as the mode's dependencies, which is +// used by (legacy) mechanisms like loadmode.js to automatically +// load a mode. (Preferred mechanism is the require/define calls.) +CodeMirror$1.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; } + defineMode.apply(this, arguments); +}; + +CodeMirror$1.defineMIME = defineMIME; + +// Minimal default mode. +CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); +CodeMirror$1.defineMIME("text/plain", "null"); + +// EXTENSIONS + +CodeMirror$1.defineExtension = function (name, func) { + CodeMirror$1.prototype[name] = func; +}; +CodeMirror$1.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; +}; + +CodeMirror$1.fromTextArea = fromTextArea; + +addLegacyProps(CodeMirror$1); + +CodeMirror$1.version = "5.27.4"; + +return CodeMirror$1; + +}))); diff --git a/console/src/main/resources/static/console-fe/public/js/codemirror.lib.clike-lint.js b/console/src/main/resources/static/console-fe/public/js/codemirror.lib.clike-lint.js new file mode 100644 index 000000000..8c6e921bb --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/codemirror.lib.clike-lint.js @@ -0,0 +1,799 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +function Context(indented, column, type, info, align, prev) { + this.indented = indented; + this.column = column; + this.type = type; + this.info = info; + this.align = align; + this.prev = prev; +} +function pushContext(state, col, type, info) { + var indent = state.indented; + if (state.context && state.context.type == "statement" && type != "statement") + indent = state.context.indented; + return state.context = new Context(indent, col, type, info, null, state.context); +} +function popContext(state) { + var t = state.context.type; + if (t == ")" || t == "]" || t == "}") + state.indented = state.context.indented; + return state.context = state.context.prev; +} + +function typeBefore(stream, state, pos) { + if (state.prevToken == "variable" || state.prevToken == "type") return true; + if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true; + if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true; +} + +function isTopScope(context) { + for (;;) { + if (!context || context.type == "top") return true; + if (context.type == "}" && context.prev.info != "namespace") return false; + context = context.prev; + } +} + +CodeMirror.defineMode("clike", function(config, parserConfig) { + var indentUnit = config.indentUnit, + statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, + dontAlignCalls = parserConfig.dontAlignCalls, + keywords = parserConfig.keywords || {}, + types = parserConfig.types || {}, + builtin = parserConfig.builtin || {}, + blockKeywords = parserConfig.blockKeywords || {}, + defKeywords = parserConfig.defKeywords || {}, + atoms = parserConfig.atoms || {}, + hooks = parserConfig.hooks || {}, + multiLineStrings = parserConfig.multiLineStrings, + indentStatements = parserConfig.indentStatements !== false, + indentSwitch = parserConfig.indentSwitch !== false, + namespaceSeparator = parserConfig.namespaceSeparator, + isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/, + numberStart = parserConfig.numberStart || /[\d\.]/, + number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i, + isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/, + isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/; + + var curPunc, isDefKeyword; + + function tokenBase(stream, state) { + var ch = stream.next(); + if (hooks[ch]) { + var result = hooks[ch](stream, state); + if (result !== false) return result; + } + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + if (isPunctuationChar.test(ch)) { + curPunc = ch; + return null; + } + if (numberStart.test(ch)) { + stream.backUp(1) + if (stream.match(number)) return "number" + stream.next() + } + if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + } + if (isOperatorChar.test(ch)) { + while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {} + return "operator"; + } + stream.eatWhile(isIdentifierChar); + if (namespaceSeparator) while (stream.match(namespaceSeparator)) + stream.eatWhile(isIdentifierChar); + + var cur = stream.current(); + if (contains(keywords, cur)) { + if (contains(blockKeywords, cur)) curPunc = "newstatement"; + if (contains(defKeywords, cur)) isDefKeyword = true; + return "keyword"; + } + if (contains(types, cur)) return "type"; + if (contains(builtin, cur)) { + if (contains(blockKeywords, cur)) curPunc = "newstatement"; + return "builtin"; + } + if (contains(atoms, cur)) return "atom"; + return "variable"; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) {end = true; break;} + escaped = !escaped && next == "\\"; + } + if (end || !(escaped || multiLineStrings)) + state.tokenize = null; + return "string"; + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = null; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function maybeEOL(stream, state) { + if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context)) + state.typeAtEndOfLine = typeBefore(stream, state, stream.pos) + } + + // Interface + + return { + startState: function(basecolumn) { + return { + tokenize: null, + context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false), + indented: 0, + startOfLine: true, + prevToken: null + }; + }, + + token: function(stream, state) { + var ctx = state.context; + if (stream.sol()) { + if (ctx.align == null) ctx.align = false; + state.indented = stream.indentation(); + state.startOfLine = true; + } + if (stream.eatSpace()) { maybeEOL(stream, state); return null; } + curPunc = isDefKeyword = null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style == "comment" || style == "meta") return style; + if (ctx.align == null) ctx.align = true; + + if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false))) + while (state.context.type == "statement") popContext(state); + else if (curPunc == "{") pushContext(state, stream.column(), "}"); + else if (curPunc == "[") pushContext(state, stream.column(), "]"); + else if (curPunc == "(") pushContext(state, stream.column(), ")"); + else if (curPunc == "}") { + while (ctx.type == "statement") ctx = popContext(state); + if (ctx.type == "}") ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); + } + else if (curPunc == ctx.type) popContext(state); + else if (indentStatements && + (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") || + (ctx.type == "statement" && curPunc == "newstatement"))) { + pushContext(state, stream.column(), "statement", stream.current()); + } + + if (style == "variable" && + ((state.prevToken == "def" || + (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) && + isTopScope(state.context) && stream.match(/^\s*\(/, false))))) + style = "def"; + + if (hooks.token) { + var result = hooks.token(stream, state, style); + if (result !== undefined) style = result; + } + + if (style == "def" && parserConfig.styleDefs === false) style = "variable"; + + state.startOfLine = false; + state.prevToken = isDefKeyword ? "def" : style || curPunc; + maybeEOL(stream, state); + return style; + }, + + indent: function(state, textAfter) { + if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass; + var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); + if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; + if (parserConfig.dontIndentStatements) + while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info)) + ctx = ctx.prev + if (hooks.indent) { + var hook = hooks.indent(state, ctx, textAfter); + if (typeof hook == "number") return hook + } + var closing = firstChar == ctx.type; + var switchBlock = ctx.prev && ctx.prev.info == "switch"; + if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) { + while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev + return ctx.indented + } + if (ctx.type == "statement") + return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); + if (ctx.align && (!dontAlignCalls || ctx.type != ")")) + return ctx.column + (closing ? 0 : 1); + if (ctx.type == ")" && !closing) + return ctx.indented + statementIndentUnit; + + return ctx.indented + (closing ? 0 : indentUnit) + + (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0); + }, + + electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/, + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//", + fold: "brace" + }; +}); + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + function contains(words, word) { + if (typeof words === "function") { + return words(word); + } else { + return words.propertyIsEnumerable(word); + } + } + var cKeywords = "auto if break case register continue return default do sizeof " + + "static else struct switch extern typedef union for goto while enum const volatile"; + var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t"; + + function cppHook(stream, state) { + if (!state.startOfLine) return false + for (var ch, next = null; ch = stream.peek();) { + if (ch == "\\" && stream.match(/^.$/)) { + next = cppHook + break + } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) { + break + } + stream.next() + } + state.tokenize = next + return "meta" + } + + function pointerHook(_stream, state) { + if (state.prevToken == "type") return "type"; + return false; + } + + function cpp14Literal(stream) { + stream.eatWhile(/[\w\.']/); + return "number"; + } + + function cpp11StringHook(stream, state) { + stream.backUp(1); + // Raw strings. + if (stream.match(/(R|u8R|uR|UR|LR)/)) { + var match = stream.match(/"([^\s\\()]{0,16})\(/); + if (!match) { + return false; + } + state.cpp11RawStringDelim = match[1]; + state.tokenize = tokenRawString; + return tokenRawString(stream, state); + } + // Unicode strings/chars. + if (stream.match(/(u8|u|U|L)/)) { + if (stream.match(/["']/, /* eat */ false)) { + return "string"; + } + return false; + } + // Ignore this hook. + stream.next(); + return false; + } + + function cppLooksLikeConstructor(word) { + var lastTwo = /(\w+)::~?(\w+)$/.exec(word); + return lastTwo && lastTwo[1] == lastTwo[2]; + } + + // C#-style strings where "" escapes a quote. + function tokenAtString(stream, state) { + var next; + while ((next = stream.next()) != null) { + if (next == '"' && !stream.eat('"')) { + state.tokenize = null; + break; + } + } + return "string"; + } + + // C++11 raw string literal is "( anything )", where + // can be a string up to 16 characters long. + function tokenRawString(stream, state) { + // Escape characters that have special regex meanings. + var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&'); + var match = stream.match(new RegExp(".*?\\)" + delim + '"')); + if (match) + state.tokenize = null; + else + stream.skipToEnd(); + return "string"; + } + + function def(mimes, mode) { + if (typeof mimes == "string") mimes = [mimes]; + var words = []; + function add(obj) { + if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) + words.push(prop); + } + add(mode.keywords); + add(mode.types); + add(mode.builtin); + add(mode.atoms); + if (words.length) { + mode.helperType = mimes[0]; + CodeMirror.registerHelper("hintWords", mimes[0], words); + } + + for (var i = 0; i < mimes.length; ++i) + CodeMirror.defineMIME(mimes[i], mode); + } + + def(["text/x-csrc", "text/x-c", "text/x-chdr"], { + name: "clike", + keywords: words(cKeywords), + types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " + + "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " + + "uint32_t uint64_t"), + blockKeywords: words("case do else for if switch while struct"), + defKeywords: words("struct"), + typeFirstDefinitions: true, + atoms: words("null true false"), + hooks: {"#": cppHook, "*": pointerHook}, + modeProps: {fold: ["brace", "include"]} + }); + + def(["text/x-c++src", "text/x-c++hdr"], { + name: "clike", + keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " + + "static_cast typeid catch operator template typename class friend private " + + "this using const_cast inline public throw virtual delete mutable protected " + + "alignas alignof constexpr decltype nullptr noexcept thread_local final " + + "static_assert override"), + types: words(cTypes + " bool wchar_t"), + blockKeywords: words("catch class do else finally for if struct switch try while"), + defKeywords: words("class namespace struct enum union"), + typeFirstDefinitions: true, + atoms: words("true false null"), + dontIndentStatements: /^template$/, + isIdentifierChar: /[\w\$_~\xa1-\uffff]/, + hooks: { + "#": cppHook, + "*": pointerHook, + "u": cpp11StringHook, + "U": cpp11StringHook, + "L": cpp11StringHook, + "R": cpp11StringHook, + "0": cpp14Literal, + "1": cpp14Literal, + "2": cpp14Literal, + "3": cpp14Literal, + "4": cpp14Literal, + "5": cpp14Literal, + "6": cpp14Literal, + "7": cpp14Literal, + "8": cpp14Literal, + "9": cpp14Literal, + token: function(stream, state, style) { + if (style == "variable" && stream.peek() == "(" && + (state.prevToken == ";" || state.prevToken == null || + state.prevToken == "}") && + cppLooksLikeConstructor(stream.current())) + return "def"; + } + }, + namespaceSeparator: "::", + modeProps: {fold: ["brace", "include"]} + }); + + def("text/x-java", { + name: "clike", + keywords: words("abstract assert break case catch class const continue default " + + "do else enum extends final finally float for goto if implements import " + + "instanceof interface native new package private protected public " + + "return static strictfp super switch synchronized this throw throws transient " + + "try volatile while @interface"), + types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " + + "Integer Long Number Object Short String StringBuffer StringBuilder Void"), + blockKeywords: words("catch class do else finally for if switch try while"), + defKeywords: words("class interface package enum @interface"), + typeFirstDefinitions: true, + atoms: words("true false null"), + number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, + hooks: { + "@": function(stream) { + // Don't match the @interface keyword. + if (stream.match('interface', false)) return false; + + stream.eatWhile(/[\w\$_]/); + return "meta"; + } + }, + modeProps: {fold: ["brace", "import"]} + }); + + def("text/x-csharp", { + name: "clike", + keywords: words("abstract as async await base break case catch checked class const continue" + + " default delegate do else enum event explicit extern finally fixed for" + + " foreach goto if implicit in interface internal is lock namespace new" + + " operator out override params private protected public readonly ref return sealed" + + " sizeof stackalloc static struct switch this throw try typeof unchecked" + + " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + + " global group into join let orderby partial remove select set value var yield"), + types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" + + " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" + + " UInt64 bool byte char decimal double short int long object" + + " sbyte float string ushort uint ulong"), + blockKeywords: words("catch class do else finally for foreach if struct switch try while"), + defKeywords: words("class interface namespace struct var"), + typeFirstDefinitions: true, + atoms: words("true false null"), + hooks: { + "@": function(stream, state) { + if (stream.eat('"')) { + state.tokenize = tokenAtString; + return tokenAtString(stream, state); + } + stream.eatWhile(/[\w\$_]/); + return "meta"; + } + } + }); + + function tokenTripleString(stream, state) { + var escaped = false; + while (!stream.eol()) { + if (!escaped && stream.match('"""')) { + state.tokenize = null; + break; + } + escaped = stream.next() == "\\" && !escaped; + } + return "string"; + } + + def("text/x-scala", { + name: "clike", + keywords: words( + + /* scala */ + "abstract case catch class def do else extends final finally for forSome if " + + "implicit import lazy match new null object override package private protected return " + + "sealed super this throw trait try type val var while with yield _ " + + + /* package scala */ + "assert assume require print println printf readLine readBoolean readByte readShort " + + "readChar readInt readLong readFloat readDouble" + ), + types: words( + "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " + + "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " + + "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " + + "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " + + "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " + + + /* package java.lang */ + "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" + ), + multiLineStrings: true, + blockKeywords: words("catch class enum do else finally for forSome if match switch try while"), + defKeywords: words("class enum def object package trait type val var"), + atoms: words("true false null"), + indentStatements: false, + indentSwitch: false, + isOperatorChar: /[+\-*&%=<>!?|\/#:@]/, + hooks: { + "@": function(stream) { + stream.eatWhile(/[\w\$_]/); + return "meta"; + }, + '"': function(stream, state) { + if (!stream.match('""')) return false; + state.tokenize = tokenTripleString; + return state.tokenize(stream, state); + }, + "'": function(stream) { + stream.eatWhile(/[\w\$_\xa1-\uffff]/); + return "atom"; + }, + "=": function(stream, state) { + var cx = state.context + if (cx.type == "}" && cx.align && stream.eat(">")) { + state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev) + return "operator" + } else { + return false + } + } + }, + modeProps: {closeBrackets: {triples: '"'}} + }); + + function tokenKotlinString(tripleString){ + return function (stream, state) { + var escaped = false, next, end = false; + while (!stream.eol()) { + if (!tripleString && !escaped && stream.match('"') ) {end = true; break;} + if (tripleString && stream.match('"""')) {end = true; break;} + next = stream.next(); + if(!escaped && next == "$" && stream.match('{')) + stream.skipTo("}"); + escaped = !escaped && next == "\\" && !tripleString; + } + if (end || !tripleString) + state.tokenize = null; + return "string"; + } + } + + def("text/x-kotlin", { + name: "clike", + keywords: words( + /*keywords*/ + "package as typealias class interface this super val " + + "var fun for is in This throw return " + + "break continue object if else while do try when !in !is as? " + + + /*soft keywords*/ + "file import where by get set abstract enum open inner override private public internal " + + "protected catch finally out final vararg reified dynamic companion constructor init " + + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + + "external annotation crossinline const operator infix suspend" + ), + types: words( + /* package java.lang */ + "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" + ), + intendSwitch: false, + indentStatements: false, + multiLineStrings: true, + number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, + blockKeywords: words("catch class do else finally for if where try while enum"), + defKeywords: words("class val var object package interface fun"), + atoms: words("true false null this"), + hooks: { + '"': function(stream, state) { + state.tokenize = tokenKotlinString(stream.match('""')); + return state.tokenize(stream, state); + } + }, + modeProps: {closeBrackets: {triples: '"'}} + }); + + def(["x-shader/x-vertex", "x-shader/x-fragment"], { + name: "clike", + keywords: words("sampler1D sampler2D sampler3D samplerCube " + + "sampler1DShadow sampler2DShadow " + + "const attribute uniform varying " + + "break continue discard return " + + "for while do if else struct " + + "in out inout"), + types: words("float int bool void " + + "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " + + "mat2 mat3 mat4"), + blockKeywords: words("for while do if else struct"), + builtin: words("radians degrees sin cos tan asin acos atan " + + "pow exp log exp2 sqrt inversesqrt " + + "abs sign floor ceil fract mod min max clamp mix step smoothstep " + + "length distance dot cross normalize ftransform faceforward " + + "reflect refract matrixCompMult " + + "lessThan lessThanEqual greaterThan greaterThanEqual " + + "equal notEqual any all not " + + "texture1D texture1DProj texture1DLod texture1DProjLod " + + "texture2D texture2DProj texture2DLod texture2DProjLod " + + "texture3D texture3DProj texture3DLod texture3DProjLod " + + "textureCube textureCubeLod " + + "shadow1D shadow2D shadow1DProj shadow2DProj " + + "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " + + "dFdx dFdy fwidth " + + "noise1 noise2 noise3 noise4"), + atoms: words("true false " + + "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " + + "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " + + "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " + + "gl_FogCoord gl_PointCoord " + + "gl_Position gl_PointSize gl_ClipVertex " + + "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " + + "gl_TexCoord gl_FogFragCoord " + + "gl_FragCoord gl_FrontFacing " + + "gl_FragData gl_FragDepth " + + "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " + + "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " + + "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " + + "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " + + "gl_ProjectionMatrixInverseTranspose " + + "gl_ModelViewProjectionMatrixInverseTranspose " + + "gl_TextureMatrixInverseTranspose " + + "gl_NormalScale gl_DepthRange gl_ClipPlane " + + "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " + + "gl_FrontLightModelProduct gl_BackLightModelProduct " + + "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " + + "gl_FogParameters " + + "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " + + "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " + + "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " + + "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " + + "gl_MaxDrawBuffers"), + indentSwitch: false, + hooks: {"#": cppHook}, + modeProps: {fold: ["brace", "include"]} + }); + + def("text/x-nesc", { + name: "clike", + keywords: words(cKeywords + "as atomic async call command component components configuration event generic " + + "implementation includes interface module new norace nx_struct nx_union post provides " + + "signal task uses abstract extends"), + types: words(cTypes), + blockKeywords: words("case do else for if switch while struct"), + atoms: words("null true false"), + hooks: {"#": cppHook}, + modeProps: {fold: ["brace", "include"]} + }); + + def("text/x-objectivec", { + name: "clike", + keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " + + "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"), + types: words(cTypes), + atoms: words("YES NO NULL NILL ON OFF true false"), + hooks: { + "@": function(stream) { + stream.eatWhile(/[\w\$]/); + return "keyword"; + }, + "#": cppHook, + indent: function(_state, ctx, textAfter) { + if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented + } + }, + modeProps: {fold: "brace"} + }); + + def("text/x-squirrel", { + name: "clike", + keywords: words("base break clone continue const default delete enum extends function in class" + + " foreach local resume return this throw typeof yield constructor instanceof static"), + types: words(cTypes), + blockKeywords: words("case catch class else for foreach if switch try while"), + defKeywords: words("function local class"), + typeFirstDefinitions: true, + atoms: words("true false null"), + hooks: {"#": cppHook}, + modeProps: {fold: ["brace", "include"]} + }); + + // Ceylon Strings need to deal with interpolation + var stringTokenizer = null; + function tokenCeylonString(type) { + return function(stream, state) { + var escaped = false, next, end = false; + while (!stream.eol()) { + if (!escaped && stream.match('"') && + (type == "single" || stream.match('""'))) { + end = true; + break; + } + if (!escaped && stream.match('``')) { + stringTokenizer = tokenCeylonString(type); + end = true; + break; + } + next = stream.next(); + escaped = type == "single" && !escaped && next == "\\"; + } + if (end) + state.tokenize = null; + return "string"; + } + } + + def("text/x-ceylon", { + name: "clike", + keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" + + " exists extends finally for function given if import in interface is let module new" + + " nonempty object of out outer package return satisfies super switch then this throw" + + " try value void while"), + types: function(word) { + // In Ceylon all identifiers that start with an uppercase are types + var first = word.charAt(0); + return (first === first.toUpperCase() && first !== first.toLowerCase()); + }, + blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"), + defKeywords: words("class dynamic function interface module object package value"), + builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" + + " native optional sealed see serializable shared suppressWarnings tagged throws variable"), + isPunctuationChar: /[\[\]{}\(\),;\:\.`]/, + isOperatorChar: /[+\-*&%=<>!?|^~:\/]/, + numberStart: /[\d#$]/, + number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i, + multiLineStrings: true, + typeFirstDefinitions: true, + atoms: words("true false null larger smaller equal empty finished"), + indentSwitch: false, + styleDefs: false, + hooks: { + "@": function(stream) { + stream.eatWhile(/[\w\$_]/); + return "meta"; + }, + '"': function(stream, state) { + state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single"); + return state.tokenize(stream, state); + }, + '`': function(stream, state) { + if (!stringTokenizer || !stream.match('`')) return false; + state.tokenize = stringTokenizer; + stringTokenizer = null; + return state.tokenize(stream, state); + }, + "'": function(stream) { + stream.eatWhile(/[\w\$_\xa1-\uffff]/); + return "atom"; + }, + token: function(_stream, state, style) { + if ((style == "variable" || style == "type") && + state.prevToken == ".") { + return "variable-2"; + } + } + }, + modeProps: { + fold: ["brace", "import"], + closeBrackets: {triples: '"'} + } + }); + +}); \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/js/codemirror.lib.json-lint.js b/console/src/main/resources/static/console-fe/public/js/codemirror.lib.json-lint.js new file mode 100644 index 000000000..d395030b1 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/codemirror.lib.json-lint.js @@ -0,0 +1,445 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * 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. + */ + +/* Jison generated parser */ +var jsonlint = (function(){ +var parser = {trace: function trace() { }, +yy: {}, +symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1}, +terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"}, +productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]], +performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { + +var $0 = $$.length - 1; +switch (yystate) { +case 1: // replace escaped characters with actual character + this.$ = yytext.replace(/\\(\\|")/g, "$"+"1") + .replace(/\\n/g,'\n') + .replace(/\\r/g,'\r') + .replace(/\\t/g,'\t') + .replace(/\\v/g,'\v') + .replace(/\\f/g,'\f') + .replace(/\\b/g,'\b'); + +break; +case 2:this.$ = Number(yytext); +break; +case 3:this.$ = null; +break; +case 4:this.$ = true; +break; +case 5:this.$ = false; +break; +case 6:return this.$ = $$[$0-1]; +break; +case 13:this.$ = {}; +break; +case 14:this.$ = $$[$0-1]; +break; +case 15:this.$ = [$$[$0-2], $$[$0]]; +break; +case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; +break; +case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1]; +break; +case 18:this.$ = []; +break; +case 19:this.$ = $$[$0-1]; +break; +case 20:this.$ = [$$[$0]]; +break; +case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]); +break; +} +}, +table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}], +defaultActions: {16:[2,6]}, +parseError: function parseError(str, hash) { + throw new Error(str); +}, +parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], // semantic value stack + lstack = [], // location stack + table = this.table, + yytext = '', + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + + //this.reductionCount = this.shiftCount = 0; + + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + if (typeof this.lexer.yylloc == 'undefined') + this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + + if (typeof this.yy.parseError === 'function') + this.parseError = this.yy.parseError; + + function popStack (n) { + stack.length = stack.length - 2*n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + + function lex() { + var token; + token = self.lexer.lex() || 1; // $end = 1 + // if token isn't its numeric value, convert + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + } + + var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; + while (true) { + // retreive state number from top of stack + state = stack[stack.length-1]; + + // use default actions if available + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol == null) + symbol = lex(); + // read action for current state and first input + action = table[state] && table[state][symbol]; + } + + // handle parse error + _handle_error: + if (typeof action === 'undefined' || !action.length || !action[0]) { + + if (!recovering) { + // Report error + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'"+this.terminals_[p]+"'"); + } + var errStr = ''; + if (this.lexer.showPosition) { + errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; + } else { + errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + + (symbol == 1 /*EOF*/ ? "end of input" : + ("'"+(this.terminals_[symbol] || symbol)+"'")); + } + this.parseError(errStr, + {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + + // just recovered from another error + if (recovering == 3) { + if (symbol == EOF) { + throw new Error(errStr || 'Parsing halted.'); + } + + // discard current lookahead and grab another + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + symbol = lex(); + } + + // try to recover from error + while (1) { + // check for error recovery rule in this state + if ((TERROR.toString()) in table[state]) { + break; + } + if (state == 0) { + throw new Error(errStr || 'Parsing halted.'); + } + popStack(1); + state = stack[stack.length-1]; + } + + preErrorSymbol = symbol; // save the lookahead token + symbol = TERROR; // insert generic error symbol as new lookahead + state = stack[stack.length-1]; + action = table[state] && table[state][TERROR]; + recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); + } + + switch (action[0]) { + + case 1: // shift + //this.shiftCount++; + + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); // push state + symbol = null; + if (!preErrorSymbol) { // normal execution/no error + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { // error just occurred, resume old lookahead f/ before error + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + + case 2: // reduce + //this.reductionCount++; + + len = this.productions_[action[1]][1]; + + // perform semantic action + yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + yyval._$ = { + first_line: lstack[lstack.length-(len||1)].first_line, + last_line: lstack[lstack.length-1].last_line, + first_column: lstack[lstack.length-(len||1)].first_column, + last_column: lstack[lstack.length-1].last_column + }; + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + + if (typeof r !== 'undefined') { + return r; + } + + // pop off stack + if (len) { + stack = stack.slice(0,-1*len*2); + vstack = vstack.slice(0, -1*len); + lstack = lstack.slice(0, -1*len); + } + + stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) + vstack.push(yyval.$); + lstack.push(yyval._$); + // goto new state = table[STATE][NONTERMINAL] + newState = table[stack[stack.length-2]][stack[stack.length-1]]; + stack.push(newState); + break; + + case 3: // accept + return true; + } + + } + + return true; +}}; +/* Jison generated lexer */ +var lexer = (function(){ +var lexer = ({EOF:1, +parseError:function parseError(str, hash) { + if (this.yy.parseError) { + this.yy.parseError(str, hash); + } else { + throw new Error(str); + } + }, +setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + return this; + }, +input:function () { + var ch = this._input[0]; + this.yytext+=ch; + this.yyleng++; + this.match+=ch; + this.matched+=ch; + var lines = ch.match(/\n/); + if (lines) this.yylineno++; + this._input = this._input.slice(1); + return ch; + }, +unput:function (ch) { + this._input = ch + this._input; + return this; + }, +more:function () { + this._more = true; + return this; + }, +less:function (n) { + this._input = this.match.slice(n) + this._input; + }, +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + tempMatch, + index, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/\n.*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} + this.yytext += match[0]; + this.match += match[0]; + this.yyleng = this.yytext.length; + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + if (this._input === "") { + return this.EOF; + } else { + this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, +lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, +begin:function begin(condition) { + this.conditionStack.push(condition); + }, +popState:function popState() { + return this.conditionStack.pop(); + }, +_currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, +topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, +pushState:function begin(condition) { + this.begin(condition); + }}); +lexer.options = {}; +lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + +var YYSTATE=YY_START +switch($avoiding_name_collisions) { +case 0:/* skip whitespace */ +break; +case 1:return 6 +break; +case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4 +break; +case 3:return 17 +break; +case 4:return 18 +break; +case 5:return 23 +break; +case 6:return 24 +break; +case 7:return 22 +break; +case 8:return 21 +break; +case 9:return 10 +break; +case 10:return 11 +break; +case 11:return 8 +break; +case 12:return 14 +break; +case 13:return 'INVALID' +break; +} +}; +lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/]; +lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; + + +; +return lexer;})() +parser.lexer = lexer; +return parser; +})(); +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = jsonlint; +exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); } +exports.main = function commonjsMain(args) { + if (!args[1]) + throw new Error('Usage: '+args[0]+' FILE'); + if (typeof process !== 'undefined') { + var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); + } else { + var cwd = require("file").path(require("file").cwd()); + var source = cwd.join(args[1]).read({charset: "utf-8"}); + } + return exports.parser.parse(source); +} +if (typeof module !== 'undefined' && require.main === module) { + exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); +} +} \ No newline at end of file diff --git a/console/src/main/resources/static/console-fe/public/js/diff_match_patch.js b/console/src/main/resources/static/console-fe/public/js/diff_match_patch.js new file mode 100644 index 000000000..c41b51327 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/diff_match_patch.js @@ -0,0 +1,49 @@ +(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32} +diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a, +b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a}; +diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l= +u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]}; +diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)}; +diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;fd?a=a.substring(c-d):c=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null; +var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]}; +diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}}; +diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_); +return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]= +h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/; +diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;fb)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)}; +diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case 1:b[g]=''+j+"";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+""}}return b.join("")}; +diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h}; +diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&& +e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g); +if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;ie[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0, +c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c}; +diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&& +(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c)$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) +} + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + var jsKeywords = { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C, + "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C + }; + + // Extend the 'normal' keywords with the TypeScript language extensions + if (isTS) { + var type = {type: "variable", style: "type"}; + var tsKeywords = { + // object-like things + "interface": kw("class"), + "implements": C, + "namespace": C, + "module": kw("module"), + "enum": kw("module"), + + // scope modifiers + "public": kw("modifier"), + "private": kw("modifier"), + "protected": kw("modifier"), + "abstract": kw("modifier"), + + // types + "string": type, "number": type, "boolean": type, "any": type + }; + + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } + } + + return jsKeywords; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^@]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.eat(/x/i)) { + stream.eatWhile(/[\da-f]/i); + return ret("number", "number"); + } else if (ch == "0" && stream.eat(/o/i)) { + stream.eatWhile(/[0-7]/i); + return ret("number", "number"); + } else if (ch == "0" && stream.eat(/b/i)) { + stream.eatWhile(/[01]/i); + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (expressionAllowed(stream, state, 1)) { + readRegexp(stream); + stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eatWhile(isOperatorChar); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#") { + stream.skipToEnd(); + return ret("error", "error"); + } else if (isOperatorChar.test(ch)) { + if (ch != ">" || !state.lexical || state.lexical.type != ">") + stream.eatWhile(isOperatorChar); + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current() + if (state.lastType != ".") { + if (keywords.propertyIsEnumerable(word)) { + var kw = keywords[word] + return ret(kw.type, kw.style, word) + } + if (word == "async" && stream.match(/^\s*[\(\w]/, false)) + return ret("async", "keyword", word) + } + return ret("variable", "variable", word) + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/]/.test(ch)) { + return; + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function register(varname) { + function inList(list) { + for (var v = list; v; v = v.next) + if (v.name == varname) return true; + return false; + } + var state = cx.state; + cx.marked = "def"; + if (state.context) { + if (inList(state.localVars)) return; + state.localVars = {name: varname, next: state.localVars}; + } else { + if (inList(state.globalVars)) return; + if (parserConfig.globalVars) + state.globalVars = {name: varname, next: state.globalVars}; + } + } + + // Combinators + + var defaultVars = {name: "this", next: {name: "arguments"}}; + function pushcontext() { + cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; + cx.state.localVars = defaultVars; + } + function popcontext() { + cx.state.localVars = cx.state.context.vars; + cx.state.context = cx.state.context.prev; + } + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "{") return cont(pushlex("}"), block, poplex); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); + if (type == "variable") { + if (isTS && value == "type") { + cx.marked = "keyword" + return cont(typeexpr, expect("operator"), typeexpr, expect(";")); + } else { + return cont(pushlex("stat"), maybelabel); + } + } + if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), + block, poplex, poplex); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), + statement, poplex, popcontext); + if (type == "class") return cont(pushlex("form"), className, poplex); + if (type == "export") return cont(pushlex("stat"), afterExport, poplex); + if (type == "import") return cont(pushlex("stat"), afterImport, poplex); + if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + if (type == "async") return cont(statement) + if (value == "@") return cont(expression, statement) + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function expression(type) { + return expressionInner(type, false); + } + function expressionNoComma(type) { + return expressionInner(type, true); + } + function parenExpr(type) { + if (type != "(") return pass() + return cont(pushlex(")"), expression, expect(")"), poplex) + } + function expressionInner(type, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "class") return cont(pushlex("form"), classExpression, poplex); + if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") return pass(quasi, maybeop); + if (type == "new") return cont(maybeTarget(noComma)); + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + function maybeexpressionNoComma(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expressionNoComma); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(expression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value)) return cont(me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(expression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybeTarget(noComma) { + return function(type) { + if (type == ".") return cont(noComma ? targetNoComma : target); + else return pass(noComma ? expressionNoComma : expression); + }; + } + function target(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } + } + function targetNoComma(_, value) { + if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (type == "modifier") { + return cont(objprop) + } else if (type == "[") { + return cont(expression, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expression, afterprop); + } else if (type == ":") { + return pass(afterprop) + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end, sep) { + function proceed(type, value) { + if (sep ? sep.indexOf(type) > -1 : type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(function(type, value) { + if (type == end || value == end) return pass() + return pass(what) + }, proceed); + } + if (type == end || value == end) return cont(); + return cont(expect(end)); + } + return function(type, value) { + if (type == end || value == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type, value) { + if (isTS) { + if (type == ":") return cont(typeexpr); + if (value == "?") return cont(maybetype); + } + } + function typeexpr(type) { + if (type == "variable") {cx.marked = "type"; return cont(afterType);} + if (type == "string" || type == "number" || type == "atom") return cont(afterType); + if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (value == "?") { + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } else if (type == "[") { + return cont(expression, maybetype, expect("]"), typeprop) + } + } + function typearg(type) { + if (type == "variable") return cont(typearg) + else if (type == ":") return cont(typeexpr) + } + function afterType(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + if (value == "|" || type == ".") return cont(typeexpr) + if (type == "[") return cont(expect("]"), afterType) + if (value == "extends") return cont(typeexpr) + } + function vardef() { + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (type == "modifier") return cont(pattern) + if (type == "variable") { register(value); return cont(); } + if (type == "spread") return cont(pattern); + if (type == "[") return contCommasep(pattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + if (type == "spread") return cont(pattern); + if (type == "}") return pass(); + return cont(expect(":"), pattern, maybeAssign); + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type) { + if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, expect(";"), forspec2); + if (type == ";") return cont(forspec2); + if (type == "variable") return cont(formaybeinof); + return pass(expression, expect(";"), forspec2); + } + function formaybeinof(_type, value) { + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } + return cont(maybeoperatorComma, forspec2); + } + function forspec2(type, value) { + if (type == ";") return cont(forspec3); + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } + return pass(expression, expect(";"), forspec3); + } + function forspec3(type) { + if (type != ")") cont(expression); + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef) + } + function funarg(type) { + if (type == "spread") return cont(funarg); + return pass(pattern, maybetype, maybeAssign); + } + function classExpression(type, value) { + // Class expressions may have an optional name. + if (type == "variable") return className(type, value); + return classNameAfter(type, value); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter) + if (value == "extends" || value == "implements" || (isTS && type == ",")) + return cont(isTS ? typeexpr : expression, classNameAfter); + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "variable" || cx.style == "keyword") { + if ((value == "async" || value == "static" || value == "get" || value == "set" || + (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) && + cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) { + cx.marked = "keyword"; + return cont(classBody); + } + cx.marked = "property"; + return cont(isTS ? classfield : functiondef, classBody); + } + if (type == "[") + return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody) + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == ";") return cont(classBody); + if (type == "}") return cont(); + if (value == "@") return cont(expression, classBody) + } + function classfield(type, value) { + if (value == "?") return cont(classfield) + if (type == ":") return cont(typeexpr, maybeAssign) + if (value == "=") return cont(expressionNoComma) + return pass(functiondef) + } + function afterExport(type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); + return pass(statement); + } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } + function afterImport(type) { + if (type == "string") return cont(); + return pass(importSpec, maybeMoreImports, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(commasep(expressionNoComma, "]")); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && {vars: parserConfig.localVars}, + indented: basecolumn || 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse) break; + } + while ((lexical.type == "stat" || lexical.type == "form") && + (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && + (top == maybeoperatorComma || top == maybeoperatorNoComma) && + !/^[,\.=+\-*:?[\(]/.test(textAfter)))) + lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + skipExpression: function(state) { + var top = state.cc[state.cc.length - 1] + if (top == expression || top == expressionNoComma) state.cc.pop() + } + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); + +}); diff --git a/console/src/main/resources/static/console-fe/public/js/jquery.js b/console/src/main/resources/static/console-fe/public/js/jquery.js new file mode 100644 index 000000000..bcd3956c8 --- /dev/null +++ b/console/src/main/resources/static/console-fe/public/js/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), +a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b), +null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" + + + + + + + + + + + + + + + diff --git a/console/src/main/resources/static/js/main.js b/console/src/main/resources/static/js/main.js new file mode 100644 index 000000000..a1f737fbc --- /dev/null +++ b/console/src/main/resources/static/js/main.js @@ -0,0 +1,88 @@ +!function(n){var a={};function o(e){if(a[e])return a[e].exports;var t=a[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.m=n,o.c=a,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)o.d(n,a,function(e){return t[e]}.bind(null,a));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=330)}([function(e,t,n){"use strict";e.exports=n(373)},function(e,t,n){"use strict";n.d(t,"a",function(){return x}),n.d(t,"b",function(){return C}),n.d(t,"d",function(){return E}),n.d(t,"c",function(){return D});var a=n(70),d=n.n(a),o=n(3),r=n.n(o),i=n(33),f=n.n(i),s=n(79),l=n.n(s),p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u=function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var n=[],a=!0,o=!1,r=void 0;try{for(var i,s=e[Symbol.iterator]();!(a=(i=s.next()).done)&&(n.push(i.value),!t||n.length!==t);a=!0);}catch(e){o=!0,r=e}finally{try{!a&&s.return&&s.return()}finally{if(o)throw r}}return n}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")};function h(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t>>0,a=0;aSe(e)?(r=e+1,u-Se(e)):(r=e,u),{year:r,dayOfYear:i}}function Ke(e,t,n){var a,o,r=We(e.year(),t,n),i=Math.floor((e.dayOfYear()-r-1)/7)+1;return i<1?(o=e.year()-1,a=i+Be(o,t,n)):i>Be(e.year(),t,n)?(a=i-Be(e.year(),t,n),o=e.year()+1):(o=e.year(),a=i),{week:a,year:o}}function Be(e,t,n){var a=We(e,t,n),o=We(e+1,t,n);return(Se(e)-a+o)/7}V("w",["ww",2],"wo","week"),V("W",["WW",2],"Wo","isoWeek"),Y("week","w"),Y("isoWeek","W"),I("week",5),I("isoWeek",5),ue("w",Q),ue("ww",Q,q),ue("W",Q),ue("WW",Q,q),he(["w","ww","W","WW"],function(e,t,n,a){t[a.substr(0,1)]=k(e)}),V("d",0,"do","day"),V("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),V("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),V("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),V("e",0,0,"weekday"),V("E",0,0,"isoWeekday"),Y("day","d"),Y("weekday","e"),Y("isoWeekday","E"),I("day",11),I("weekday",11),I("isoWeekday",11),ue("d",Q),ue("e",Q),ue("E",Q),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,a){var o=n._locale.weekdaysParse(e,a,n._strict);null!=o?t.d=o:_(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,a){t[a]=k(e)});var Ue="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),qe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ge="Su_Mo_Tu_We_Th_Fr_Sa".split("_");function Je(e,t,n){var a,o,r,i=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],a=0;a<7;++a)r=f([2e3,1]).day(a),this._minWeekdaysParse[a]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[a]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[a]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(o=Le.call(this._weekdaysParse,i))?o:null:"ddd"===t?-1!==(o=Le.call(this._shortWeekdaysParse,i))?o:null:-1!==(o=Le.call(this._minWeekdaysParse,i))?o:null:"dddd"===t?-1!==(o=Le.call(this._weekdaysParse,i))?o:-1!==(o=Le.call(this._shortWeekdaysParse,i))?o:-1!==(o=Le.call(this._minWeekdaysParse,i))?o:null:"ddd"===t?-1!==(o=Le.call(this._shortWeekdaysParse,i))?o:-1!==(o=Le.call(this._weekdaysParse,i))?o:-1!==(o=Le.call(this._minWeekdaysParse,i))?o:null:-1!==(o=Le.call(this._minWeekdaysParse,i))?o:-1!==(o=Le.call(this._weekdaysParse,i))?o:-1!==(o=Le.call(this._shortWeekdaysParse,i))?o:null}var $e=se,Qe=se,Xe=se;function Ze(){function e(e,t){return t.length-e.length}var t,n,a,o,r,i=[],s=[],l=[],u=[];for(t=0;t<7;t++)n=f([2e3,1]).day(t),a=this.weekdaysMin(n,""),o=this.weekdaysShort(n,""),r=this.weekdays(n,""),i.push(a),s.push(o),l.push(r),u.push(a),u.push(o),u.push(r);for(i.sort(e),s.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)s[t]=de(s[t]),l[t]=de(l[t]),u[t]=de(u[t]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+i.join("|")+")","i")}function et(){return this.hours()%12||12}function tt(e,t){V(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function nt(e,t){return t._meridiemParse}V("H",["HH",2],0,"hour"),V("h",["hh",2],0,et),V("k",["kk",2],0,function(){return this.hours()||24}),V("hmm",0,0,function(){return""+et.apply(this)+H(this.minutes(),2)}),V("hmmss",0,0,function(){return""+et.apply(this)+H(this.minutes(),2)+H(this.seconds(),2)}),V("Hmm",0,0,function(){return""+this.hours()+H(this.minutes(),2)}),V("Hmmss",0,0,function(){return""+this.hours()+H(this.minutes(),2)+H(this.seconds(),2)}),tt("a",!0),tt("A",!1),Y("hour","h"),I("hour",13),ue("a",nt),ue("A",nt),ue("H",Q),ue("h",Q),ue("k",Q),ue("HH",Q,q),ue("hh",Q,q),ue("kk",Q,q),ue("hmm",X),ue("hmmss",Z),ue("Hmm",X),ue("Hmmss",Z),pe(["H","HH"],_e),pe(["k","kk"],function(e,t,n){var a=k(e);t[_e]=24===a?0:a}),pe(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),pe(["h","hh"],function(e,t,n){t[_e]=k(e),_(n).bigHour=!0}),pe("hmm",function(e,t,n){var a=e.length-2;t[_e]=k(e.substr(0,a)),t[ve]=k(e.substr(a)),_(n).bigHour=!0}),pe("hmmss",function(e,t,n){var a=e.length-4,o=e.length-2;t[_e]=k(e.substr(0,a)),t[ve]=k(e.substr(a,2)),t[be]=k(e.substr(o)),_(n).bigHour=!0}),pe("Hmm",function(e,t,n){var a=e.length-2;t[_e]=k(e.substr(0,a)),t[ve]=k(e.substr(a))}),pe("Hmmss",function(e,t,n){var a=e.length-4,o=e.length-2;t[_e]=k(e.substr(0,a)),t[ve]=k(e.substr(a,2)),t[be]=k(e.substr(o))});var at,ot=Ce("Hours",!0),rt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ne,monthsShort:je,week:{dow:0,doy:6},weekdays:Ue,weekdaysMin:Ge,weekdaysShort:qe,meridiemParse:/[ap]\.?m?\.?/i},it={},st={};function lt(e){return e?e.toLowerCase().replace("_","-"):e}function ut(e){var t=null;if(!it[e]&&void 0!==Un&&Un&&Un.exports)try{t=at._abbr,qn(386)("./"+e),ct(t)}catch(e){}return it[e]}function ct(e,t){var n;return e&&((n=r(t)?ft(e):dt(e,t))?at=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),at._abbr}function dt(e,t){if(null===t)return delete it[e],null;var n,a=rt;if(t.abbr=e,null!=it[e])x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),a=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])a=it[t.parentLocale]._config;else{if(null==(n=ut(t.parentLocale)))return st[t.parentLocale]||(st[t.parentLocale]=[]),st[t.parentLocale].push({name:e,config:t}),null;a=n._config}return it[e]=new D(E(a,t)),st[e]&&st[e].forEach(function(e){dt(e.name,e.config)}),ct(e),it[e]}function ft(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return at;if(!s(e)){if(t=ut(e))return t;e=[e]}return function(e){for(var t,n,a,o,r=0;r=t&&S(o,n,!0)>=t-1)break;t--}r++}return at}(e)}function pt(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[ge]<0||11Oe(n[me],n[ge])?ye:n[_e]<0||24Be(n,r,i)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(s=Ve(n,a,o,r,i),e._a[me]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(r=ht(e._a[me],a[me]),(e._dayOfYear>Se(r)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Fe(r,0,e._dayOfYear),e._a[ge]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=l[t]=a[t];for(;t<7;t++)e._a[t]=l[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[_e]&&0===e._a[ve]&&0===e._a[be]&&0===e._a[Me]&&(e._nextDay=!0,e._a[_e]=0),e._d=(e._useUTC?Fe:function(e,t,n,a,o,r,i){var s=new Date(e,t,n,a,o,r,i);return e<100&&0<=e&&isFinite(s.getFullYear())&&s.setFullYear(e),s}).apply(null,l),o=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[_e]=24),e._w&&void 0!==e._w.d&&e._w.d!==o&&(_(e).weekdayMismatch=!0)}}var gt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,yt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/Z|[+-]\d\d(?::?\d\d)?/,vt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],bt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Mt=/^\/?Date\((\-?\d+)/i;function wt(e){var t,n,a,o,r,i,s=e._i,l=gt.exec(s)||yt.exec(s);if(l){for(_(e).iso=!0,t=0,n=vt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},fn.isLocal=function(){return!!this.isValid()&&!this._isUTC},fn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},fn.isUtc=Vt,fn.isUTC=Vt,fn.zoneAbbr=function(){return this._isUTC?"UTC":""},fn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},fn.dates=n("dates accessor is deprecated. Use date instead.",rn),fn.months=n("months accessor is deprecated. Use month instead",Ae),fn.years=n("years accessor is deprecated. Use year instead",xe),fn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),fn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!r(this._isDSTShifted))return this._isDSTShifted;var e={};if(v(e,this),(e=Ct(e))._a){var t=e._isUTC?f(e._a):Dt(e._a);this._isDSTShifted=this.isValid()&&0","Select");var n=u(e,t);return e.onInputUpdate&&(n.onSearch=e.onInputUpdate,n.showSearch=!0),n}}),t.default=a.default.config(o.default,{transform:u}),e.exports=t.default},function(e,t,n){"use strict";n(332)},function(e,t,n){"use strict";n(393)},function(e,t,n){"use strict";n(50),n(53),n(21),n(23),n(448)},function(e,t,n){"use strict";n(23),n(453)},function(e,t,n){"use strict";e.exports=function(){}},function(e,t,n){"use strict";n(65),n(21),n(468)},function(e,t,n){"use strict";e.exports=function(e,t,n,a,o,r,i,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[n,a,o,r,i,s],c=0;(l=new Error(t.replace(/%s/g,function(){return u[c++]}))).name="Invariant Violation"}throw l.framesToPop=1,l}}},function(e,t,n){"use strict";t.__esModule=!0;var a=i(n(9)),o=i(n(297)),r=i(n(450));function i(e){return e&&e.__esModule?e:{default:e}}o.default.show=r.default.show,o.default.success=r.default.success,o.default.warning=r.default.warning,o.default.error=r.default.error,o.default.notice=r.default.notice,o.default.help=r.default.help,o.default.loading=r.default.loading,o.default.hide=r.default.hide,t.default=a.default.config(o.default,{componentName:"Message"}),e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0;var l=i(n(5)),u=i(n(13)),a=i(n(9)),o=i(n(471)),r=i(n(472));function i(e){return e&&e.__esModule?e:{default:e}}var s={Row:a.default.config(o.default,{transform:function(e,t){if("type"in e){t("type","fixed | wrap | gutter","Row");var n=e,a=n.type,o=(0,u.default)(n,["type"]),r=Array.isArray(a)?a:[a],i=void 0;-1+~]|"+I+")"+I+"*"),K=new RegExp("="+I+"*([^\\]'\"]*?)"+I+"*\\]","g"),B=new RegExp(R),U=new RegExp("^"+A+"$"),q={ID:new RegExp("^#("+A+")"),CLASS:new RegExp("^\\.("+A+")"),TAG:new RegExp("^("+A+"|[*])"),ATTR:new RegExp("^"+H),PSEUDO:new RegExp("^"+R),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+I+"*(even|odd|(([+-]|)(\\d*)n|)"+I+"*(?:([+-]|)"+I+"*(\\d+)|))"+I+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+I+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+I+"*((?:-\\d)?\\d*)"+I+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,X=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+I+"?|("+I+")|.)","ig"),ee=function(e,t,n){var a="0x"+t-65536;return a!=a||n?t:a<0?String.fromCharCode(a+65536):String.fromCharCode(a>>10|55296,1023&a|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ae=function(){w()},oe=_e(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Y.apply(t=N.call(_.childNodes),_.childNodes),t[_.childNodes.length].nodeType}catch(e){Y={apply:t.length?function(e,t){O.apply(e,N.call(t))}:function(e,t){for(var n=e.length,a=0;e[n++]=t[a++];);e.length=n-1}}}function re(e,t,n,a){var o,r,i,s,l,u,c,d=t&&t.ownerDocument,f=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==f&&9!==f&&11!==f)return n;if(!a&&((t?t.ownerDocument||t:_)!==k&&w(t),t=t||k,S)){if(11!==f&&(l=Q.exec(e)))if(o=l[1]){if(9===f){if(!(i=t.getElementById(o)))return n;if(i.id===o)return n.push(i),n}else if(d&&(i=d.getElementById(o))&&y(t,i)&&i.id===o)return n.push(i),n}else{if(l[2])return Y.apply(n,t.getElementsByTagName(e)),n;if((o=l[3])&&p.getElementsByClassName&&t.getElementsByClassName)return Y.apply(n,t.getElementsByClassName(o)),n}if(p.qsa&&!x[e+" "]&&(!g||!g.test(e))){if(1!==f)d=t,c=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(te,ne):t.setAttribute("id",s=T),r=(u=h(e)).length;r--;)u[r]="#"+s+" "+ye(u[r]);c=u.join(","),d=X.test(e)&&me(t.parentNode)||t}if(c)try{return Y.apply(n,d.querySelectorAll(c)),n}catch(e){}finally{s===T&&t.removeAttribute("id")}}}return m(e.replace(F,"$1"),t,n,a)}function ie(){var a=[];return function e(t,n){return a.push(t+" ")>b.cacheLength&&delete e[a.shift()],e[t+" "]=n}}function se(e){return e[T]=!0,e}function le(e){var t=k.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ue(e,t){for(var n=e.split("|"),a=n.length;a--;)b.attrHandle[n[a]]=t}function ce(e,t){var n=t&&e,a=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(a)return a;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function fe(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function pe(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&oe(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function he(i){return se(function(r){return r=+r,se(function(e,t){for(var n,a=i([],e.length,r),o=a.length;o--;)e[n=a[o]]&&(e[n]=!(t[n]=e[n]))})})}function me(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in p=re.support={},o=re.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},w=re.setDocument=function(e){var t,n,a=e?e.ownerDocument||e:_;return a!==k&&9===a.nodeType&&a.documentElement&&(i=(k=a).documentElement,S=!o(k),_!==k&&(n=k.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",ae,!1):n.attachEvent&&n.attachEvent("onunload",ae)),p.attributes=le(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=le(function(e){return e.appendChild(k.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=$.test(k.getElementsByClassName),p.getById=le(function(e){return i.appendChild(e).id=T,!k.getElementsByName||!k.getElementsByName(T).length}),p.getById?(b.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if(void 0!==t.getElementById&&S){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(Z,ee);return function(e){var t=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if(void 0!==t.getElementById&&S){var n,a,o,r=t.getElementById(e);if(r){if((n=r.getAttributeNode("id"))&&n.value===e)return[r];for(o=t.getElementsByName(e),a=0;r=o[a++];)if((n=r.getAttributeNode("id"))&&n.value===e)return[r]}return[]}}),b.find.TAG=p.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,a=[],o=0,r=t.getElementsByTagName(e);if("*"!==e)return r;for(;n=r[o++];)1===n.nodeType&&a.push(n);return a},b.find.CLASS=p.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&S)return t.getElementsByClassName(e)},s=[],g=[],(p.qsa=$.test(k.querySelectorAll))&&(le(function(e){i.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&g.push("[*^$]="+I+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||g.push("\\["+I+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+T+"-]").length||g.push("~="),e.querySelectorAll(":checked").length||g.push(":checked"),e.querySelectorAll("a#"+T+"+*").length||g.push(".#.+[+~]")}),le(function(e){e.innerHTML="";var t=k.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&g.push("name"+I+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&g.push(":enabled",":disabled"),i.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(p.matchesSelector=$.test(c=i.matches||i.webkitMatchesSelector||i.mozMatchesSelector||i.oMatchesSelector||i.msMatchesSelector))&&le(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",R)}),g=g.length&&new RegExp(g.join("|")),s=s.length&&new RegExp(s.join("|")),t=$.test(i.compareDocumentPosition),y=t||$.test(i.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,a=t&&t.parentNode;return e===a||!(!a||1!==a.nodeType||!(n.contains?n.contains(a):e.compareDocumentPosition&&16&e.compareDocumentPosition(a)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},C=t?function(e,t){if(e===t)return u=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e===k||e.ownerDocument===_&&y(_,e)?-1:t===k||t.ownerDocument===_&&y(_,t)?1:l?j(l,e)-j(l,t):0:4&n?-1:1)}:function(e,t){if(e===t)return u=!0,0;var n,a=0,o=e.parentNode,r=t.parentNode,i=[e],s=[t];if(!o||!r)return e===k?-1:t===k?1:o?-1:r?1:l?j(l,e)-j(l,t):0;if(o===r)return ce(e,t);for(n=e;n=n.parentNode;)i.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;i[a]===s[a];)a++;return a?ce(i[a],s[a]):i[a]===_?-1:s[a]===_?1:0}),k},re.matches=function(e,t){return re(e,null,null,t)},re.matchesSelector=function(e,t){if((e.ownerDocument||e)!==k&&w(e),t=t.replace(K,"='$1']"),p.matchesSelector&&S&&!x[t+" "]&&(!s||!s.test(t))&&(!g||!g.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||re.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&re.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&B.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=f[e+" "];return t||(t=new RegExp("(^|"+I+")"+e+"("+I+"|$)"))&&f(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,a,o){return function(e){var t=re.attr(e,n);return null==t?"!="===a:!a||(t+="","="===a?t===o:"!="===a?t!==o:"^="===a?o&&0===t.indexOf(o):"*="===a?o&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function E(e,n,a){return _(n)?T.grep(e,function(e,t){return!!n.call(e,t,e)!==a}):n.nodeType?T.grep(e,function(e){return e===n!==a}):"string"!=typeof n?T.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(T.fn.init=function(e,t,n){var a,o;if(!e)return this;if(n=n||D,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):_(e)?void 0!==n.ready?n.ready(e):e(T):T.makeArray(e,this);if(!(a="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:O.exec(e))||!a[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(a[1]){if(t=t instanceof T?t[0]:t,T.merge(this,T.parseHTML(a[1],t&&t.nodeType?t.ownerDocument||t:S,!0)),C.test(a[1])&&T.isPlainObject(t))for(a in t)_(this[a])?this[a](t[a]):this.attr(a,t[a]);return this}return(o=S.getElementById(a[2]))&&(this[0]=o,this.length=1),this}).prototype=T.fn,D=T(S);var Y=/^(?:parents|prev(?:Until|All))/,N={children:!0,contents:!0,next:!0,prev:!0};function j(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}T.fn.extend({has:function(e){var t=T(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]+)/i,ce=/^$|^module$|\/(?:java|ecma)script/i,de={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function fe(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&x(e,t)?T.merge([e],n):n}function pe(e,t){for(var n=0,a=e.length;nx",y.noCloneChecked=!!he.cloneNode(!0).lastChild.defaultValue;var _e=S.documentElement,ve=/^key/,be=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Me=/^([^.]*)(?:\.(.+)|)/;function we(){return!0}function ke(){return!1}function Se(){try{return S.activeElement}catch(e){}}function Te(e,t,n,a,o,r){var i,s;if("object"==typeof t){for(s in"string"!=typeof n&&(a=a||n,n=void 0),t)Te(e,s,n,a,t[s],r);return e}if(null==a&&null==o?(o=n,a=n=void 0):null==o&&("string"==typeof n?(o=a,a=void 0):(o=a,a=n,n=void 0)),!1===o)o=ke;else if(!o)return e;return 1===r&&(i=o,(o=function(e){return T().off(e),i.apply(this,arguments)}).guid=i.guid||(i.guid=T.guid++)),e.each(function(){T.event.add(this,t,o,a,n)})}T.event={global:{},add:function(t,e,n,a,o){var r,i,s,l,u,c,d,f,p,h,m,g=J.get(t);if(g)for(n.handler&&(n=(r=n).handler,o=r.selector),o&&T.find.matchesSelector(_e,o),n.guid||(n.guid=T.guid++),(l=g.events)||(l=g.events={}),(i=g.handle)||(i=g.handle=function(e){return void 0!==T&&T.event.triggered!==e.type?T.event.dispatch.apply(t,arguments):void 0}),u=(e=(e||"").match(P)||[""]).length;u--;)p=m=(s=Me.exec(e[u])||[])[1],h=(s[2]||"").split(".").sort(),p&&(d=T.event.special[p]||{},p=(o?d.delegateType:d.bindType)||p,d=T.event.special[p]||{},c=T.extend({type:p,origType:m,data:a,handler:n,guid:n.guid,selector:o,needsContext:o&&T.expr.match.needsContext.test(o),namespace:h.join(".")},r),(f=l[p])||((f=l[p]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(t,a,h,i)||t.addEventListener&&t.addEventListener(p,i)),d.add&&(d.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),o?f.splice(f.delegateCount++,0,c):f.push(c),T.event.global[p]=!0)},remove:function(e,t,n,a,o){var r,i,s,l,u,c,d,f,p,h,m,g=J.hasData(e)&&J.get(e);if(g&&(l=g.events)){for(u=(t=(t||"").match(P)||[""]).length;u--;)if(p=m=(s=Me.exec(t[u])||[])[1],h=(s[2]||"").split(".").sort(),p){for(d=T.event.special[p]||{},f=l[p=(a?d.delegateType:d.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=r=f.length;r--;)c=f[r],!o&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||a&&a!==c.selector&&("**"!==a||!c.selector)||(f.splice(r,1),c.selector&&f.delegateCount--,d.remove&&d.remove.call(e,c));i&&!f.length&&(d.teardown&&!1!==d.teardown.call(e,h,g.handle)||T.removeEvent(e,p,g.handle),delete l[p])}else for(p in l)T.event.remove(e,p+t[u],n,a,!0);T.isEmptyObject(l)&&J.remove(e,"handle events")}},dispatch:function(e){var t,n,a,o,r,i,s=T.event.fix(e),l=new Array(arguments.length),u=(J.get(this,"events")||{})[s.type]||[],c=T.event.special[s.type]||{};for(l[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,xe=/\s*$/g;function De(e,t){return x(e,"table")&&x(11!==t.nodeType?t:t.firstChild,"tr")&&T(e).children("tbody")[0]||e}function Oe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ye(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Ne(e,t){var n,a,o,r,i,s,l,u;if(1===t.nodeType){if(J.hasData(e)&&(r=J.access(e),i=J.set(t,r),u=r.events))for(o in delete i.handle,i.events={},u)for(n=0,a=u[o].length;n")},clone:function(e,t,n){var a,o,r,i,s,l,u,c=e.cloneNode(!0),d=T.contains(e.ownerDocument,e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||T.isXMLDoc(e)))for(i=fe(c),a=0,o=(r=fe(e)).length;a").prop({charset:n.scriptCharset,src:n.url}).on("load error",o=function(e){a.remove(),o=null,e&&t("error"===e.type?404:200,e.type)}),S.head.appendChild(a[0])},abort:function(){o&&o()}}});var Wt,Vt=[],Kt=/(=)\?(?=&|$)|\?\?/;T.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Vt.pop()||T.expando+"_"+bt++;return this[e]=!0,e}}),T.ajaxPrefilter("json jsonp",function(e,t,n){var a,o,r,i=!1!==e.jsonp&&(Kt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Kt.test(e.data)&&"data");if(i||"jsonp"===e.dataTypes[0])return a=e.jsonpCallback=_(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,i?e[i]=e[i].replace(Kt,"$1"+a):!1!==e.jsonp&&(e.url+=(Mt.test(e.url)?"&":"?")+e.jsonp+"="+a),e.converters["script json"]=function(){return r||T.error(a+" was not called"),r[0]},e.dataTypes[0]="json",o=k[a],k[a]=function(){r=arguments},n.always(function(){void 0===o?T(k).removeProp(a):k[a]=o,e[a]&&(e.jsonpCallback=t.jsonpCallback,Vt.push(a)),r&&_(o)&&o(r[0]),r=o=void 0}),"script"}),y.createHTMLDocument=((Wt=S.implementation.createHTMLDocument("").body).innerHTML="
",2===Wt.childNodes.length),T.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((a=(t=S.implementation.createHTMLDocument("")).createElement("base")).href=S.location.href,t.head.appendChild(a)):t=S),r=!n&&[],(o=C.exec(e))?[t.createElement(o[1])]:(o=ye([e],t,r),r&&r.length&&T(r).remove(),T.merge([],o.childNodes)));var a,o,r},T.fn.load=function(e,t,n){var a,o,r,i=this,s=e.indexOf(" ");return-1").append(T.parseHTML(e)).find(a):e)}).always(n&&function(e,t){i.each(function(){n.apply(this,r||[e.responseText,t,e])})}),this},T.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){T.fn[t]=function(e){return this.on(t,e)}}),T.expr.pseudos.animated=function(t){return T.grep(T.timers,function(e){return t===e.elem}).length},T.offset={setOffset:function(e,t,n){var a,o,r,i,s,l,u=T.css(e,"position"),c=T(e),d={};"static"===u&&(e.style.position="relative"),s=c.offset(),r=T.css(e,"top"),l=T.css(e,"left"),o=("absolute"===u||"fixed"===u)&&-1<(r+l).indexOf("auto")?(i=(a=c.position()).top,a.left):(i=parseFloat(r)||0,parseFloat(l)||0),_(t)&&(t=t.call(e,n,T.extend({},s))),null!=t.top&&(d.top=t.top-s.top+i),null!=t.left&&(d.left=t.left-s.left+o),"using"in t?t.using.call(e,d):c.css(d)}},T.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){T.offset.setOffset(this,t,e)});var e,n,a=this[0];return a?a.getClientRects().length?(e=a.getBoundingClientRect(),n=a.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,a=this[0],o={top:0,left:0};if("fixed"===T.css(a,"position"))t=a.getBoundingClientRect();else{for(t=this.offset(),n=a.ownerDocument,e=a.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===T.css(e,"position");)e=e.parentNode;e&&e!==a&&1===e.nodeType&&((o=T(e).offset()).top+=T.css(e,"borderTopWidth",!0),o.left+=T.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-T.css(a,"marginTop",!0),left:t.left-o.left-T.css(a,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===T.css(e,"position");)e=e.offsetParent;return e||_e})}}),T.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,o){var r="pageYOffset"===o;T.fn[t]=function(e){return W(this,function(e,t,n){var a;if(v(e)?a=e:9===e.nodeType&&(a=e.defaultView),void 0===n)return a?a[o]:e[t];a?a.scrollTo(r?a.pageXOffset:n,r?n:a.pageYOffset):e[t]=n},t,e,arguments.length)}}),T.each(["top","left"],function(e,n){T.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=Re(e,n),Ie.test(t)?T(e).position()[n]+"px":t})}),T.each({Height:"height",Width:"width"},function(i,s){T.each({padding:"inner"+i,content:s,"":"outer"+i},function(a,r){T.fn[r]=function(e,t){var n=arguments.length&&(a||"boolean"!=typeof e),o=a||(!0===e||!0===t?"margin":"border");return W(this,function(e,t,n){var a;return v(e)?0===r.indexOf("outer")?e["inner"+i]:e.document.documentElement["client"+i]:9===e.nodeType?(a=e.documentElement,Math.max(e.body["scroll"+i],a["scroll"+i],e.body["offset"+i],a["offset"+i],a["client"+i])):void 0===n?T.css(e,t,o):T.style(e,t,n,o)},s,n?e:void 0,n)}})}),T.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){T.fn[n]=function(e,t){return 0, or explicitly pass "'+h+'" as a prop to "'+o+'".'),n.initSelector(),n.initSubscription(),n}w(e,a);var t=e.prototype;return t.getChildContext=function(){var e,t=this.propsMode?null:this.subscription;return(e={})[_]=t||this.context[_],e},t.componentDidMount=function(){f&&(this.subscription.trySubscribe(),this.selector.run(this.props),this.selector.shouldComponentUpdate&&this.forceUpdate())},t.componentWillReceiveProps=function(e){this.selector.run(e)},t.shouldComponentUpdate=function(){return this.selector.shouldComponentUpdate},t.componentWillUnmount=function(){this.subscription&&this.subscription.tryUnsubscribe(),this.subscription=null,this.notifyNestedSubs=P,this.store=null,this.selector.run=P,this.selector.shouldComponentUpdate=!1},t.getWrappedInstance=function(){return D()(g,"To access the wrapped instance, you need to specify { withRef: true } in the options argument of the "+l+"() call."),this.wrappedInstance},t.setWrappedInstance=function(e){this.wrappedInstance=e},t.initSelector=function(){var n,a,o,e=i(this.store.dispatch,r);this.selector=(n=e,a=this.store,o={run:function(e){try{var t=n(a.getState(),e);(t!==o.props||o.error)&&(o.shouldComponentUpdate=!0,o.props=t,o.error=null)}catch(e){o.shouldComponentUpdate=!0,o.error=e}}}),this.selector.run(this.props)},t.initSubscription=function(){if(f){var e=(this.propsMode?this.props:this.context)[_];this.subscription=new Y(this.store,e,this.onStateChange.bind(this)),this.notifyNestedSubs=this.subscription.notifyNestedSubs.bind(this.subscription)}},t.onStateChange=function(){this.selector.run(this.props),this.selector.shouldComponentUpdate?(this.componentDidUpdate=this.notifyNestedSubsOnComponentDidUpdate,this.setState(j)):this.notifyNestedSubs()},t.notifyNestedSubsOnComponentDidUpdate=function(){this.componentDidUpdate=void 0,this.notifyNestedSubs()},t.isSubscribed=function(){return Boolean(this.subscription)&&this.subscription.isSubscribed()},t.addExtraProps=function(e){if(!(g||c||this.propsMode&&this.subscription))return e;var t=x({},e);return g&&(t.ref=this.setWrappedInstance),c&&(t[c]=this.renderCount++),this.propsMode&&this.subscription&&(t[_]=this.subscription),t},t.render=function(){var e=this.selector;if(e.shouldComponentUpdate=!1,e.error)throw e.error;return Object(k.createElement)(n,this.addExtraProps(e.props))},e}(k.Component);return t.WrappedComponent=n,t.displayName=o,t.childContextTypes=M,t.contextTypes=b,t.propTypes=b,E()(t,n)}}var c=Object.prototype.hasOwnProperty;function d(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function v(e,t){if(d(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),a=Object.keys(t);if(n.length!==a.length)return!1;for(var o=0;othis.menuNode.clientHeight)){var e=this.menuNode.clientHeight+this.menuNode.scrollTop,t=this.itemNode.offsetTop+this.itemNode.offsetHeight;e or withRouter() outside a ");var l=t.route,u=(a||l.location).pathname;return Object(f.a)(u,{path:o,strict:r,exact:i,sensitive:s},l.match)},i.prototype.componentWillMount=function(){o()(!(this.props.component&&this.props.render),"You should not use and in the same route; will be ignored"),o()(!(this.props.component&&this.props.children&&!h(this.props.children)),"You should not use and in the same route; will be ignored"),o()(!(this.props.render&&this.props.children&&!h(this.props.children)),"You should not use and in the same route; will be ignored")},i.prototype.componentWillReceiveProps=function(e,t){o()(!(e.location&&!this.props.location),' elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'),o()(!(!e.location&&this.props.location),' elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'),this.setState({match:this.computeMatch(e,t.router)})},i.prototype.render=function(){var e=this.state.match,t=this.props,n=t.children,a=t.component,o=t.render,r=this.context.router,i=r.history,s=r.route,l=r.staticContext,u={match:e,location:this.props.location||s.location,history:i,staticContext:l};return a?e?d.a.createElement(a,u):null:o?e?o(u):null:"function"==typeof n?n(u):n&&!h(n)?d.a.Children.only(n):null},i}(d.a.Component);m.propTypes={computedMatch:l.a.object,path:l.a.string,exact:l.a.bool,strict:l.a.bool,sensitive:l.a.bool,component:l.a.func,render:l.a.func,children:l.a.oneOfType([l.a.func,l.a.node]),location:l.a.object},m.contextTypes={router:l.a.shape({history:l.a.object.isRequired,route:l.a.object.isRequired,staticContext:l.a.object})},m.childContextTypes={router:l.a.object.isRequired},t.a=m},function(e,t,n){"use strict";var a=n(94),y=n.n(a),_={},v=0;t.a=function(e){var t=1document.F=Object<\/script>"),e.close(),c=e.F;n--;)delete c[u][i[n]];return c()};e.exports=Object.create||function(e,t){var n;return null!==e?(l[u]=o(e),n=new l,l[u]=null,n[s]=e):n=c(),void 0===t?n:r(n,t)}},function(e,t,n){var a=n(58).f,o=n(52),r=n(62)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,r)&&a(e,r,{configurable:!0,value:t})}},function(e,t,n){t.f=n(62)},function(e,t,n){var a=n(46),o=n(51),r=n(82),i=n(109),s=n(58).f;e.exports=function(e){var t=o.Symbol||(o.Symbol=r?{}:a.Symbol||{});"_"==e.charAt(0)||e in t||s(t,e,{value:i.f(e)})}},function(e,t,n){"use strict";t.__esModule=!0,t.default=function(t,e){if(!t)return null;if("string"==typeof t)return document.getElementById(t);"function"==typeof t&&(t=t(e));if(!t)return null;try{return(0,a.findDOMNode)(t)}catch(e){return t}};var a=n(18);e.exports=t.default},function(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var a,o,h=f(n(5)),r=f(n(4)),i=f(n(6)),s=f(n(7)),l=n(0),m=f(l),u=f(n(2)),g=f(n(12)),c=f(n(17)),d=n(10),y=f(n(78));function f(e){return e&&e.__esModule?e:{default:e}}var _=d.func.bindCtx,v=d.obj.pickOthers,p=(o=a=function(n){function p(e){(0,r.default)(this,p);var t=(0,i.default)(this,n.call(this,e));return _(t,["handleKeyDown","handleClick"]),t}return(0,s.default)(p,n),p.prototype.getSelected=function(){var e=this.props,t=e._key,n=e.root,a=e.selected,o=n.props.selectMode,r=n.state.selectedKeys;return a||!!o&&-1o;)i(a,n=t[o++])&&(~l(r,n)||r.push(n));return r}},function(e,t,n){var a=n(131);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==a(e)?e.split(""):Object(e)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){var a=n(100);e.exports=function(e){return Object(a(e))}},function(e,t,n){"use strict";var v=n(82),b=n(73),M=n(134),w=n(57),k=n(106),S=n(348),T=n(108),L=n(351),x=n(62)("iterator"),C=!([].keys&&"next"in[].keys()),E="values",D=function(){return this};e.exports=function(e,t,n,a,o,r,i){S(n,t,a);var s,l,u,c=function(e){if(!C&&e in h)return h[e];switch(e){case"keys":case E:return function(){return new n(this,e)}}return function(){return new n(this,e)}},d=t+" Iterator",f=o==E,p=!1,h=e.prototype,m=h[x]||h["@@iterator"]||o&&h[o],g=m||c(o),y=o?f?c("entries"):g:void 0,_="Array"==t&&h.entries||m;if(_&&(u=L(_.call(new e)))!==Object.prototype&&u.next&&(T(u,d,!0),v||"function"==typeof u[x]||w(u,x,D)),f&&m&&m.name!==E&&(p=!0,g=function(){return m.call(this)}),v&&!i||!C&&!p&&h[x]||w(h,x,g),k[t]=g,k[d]=D,o)if(s={values:f?g:c(E),keys:r?g:c("keys"),entries:y},i)for(l in s)l in h||M(h,l,s[l]);else b(b.P+b.F*(C||p),t,s);return s}},function(e,t,n){e.exports=n(57)},function(e,t,n){var a=n(129),o=n(104).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return a(e,o)}},function(e,t,n){var a=n(84),o=n(80),r=n(61),i=n(99),s=n(52),l=n(127),u=Object.getOwnPropertyDescriptor;t.f=n(60)?u:function(e,t){if(e=r(e),t=i(t,!0),l)try{return u(e,t)}catch(e){}if(s(e,t))return o(!a.f.call(e,t),e[t])}},function(e,t,n){"use strict"; +/* +object-assign +(c) Sindre Sorhus +@license MIT +*/var l=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var a={};return"abcdefghijklmnopqrst".split("").forEach(function(e){a[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},a)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,a,o=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),r=1;re.clientHeight&&0a.length&&a.every(function(e,t){return e===n[t]})},y.prototype.handleOpen=function(t,e,n,a){var o=this,r=void 0,i=this.props,s=i.mode,l=i.openMode,u=this.state.openKeys,c=u.indexOf(t);e&&-1===c?"inline"===s?"single"===l?(r=u.filter(function(e){return!o.isSibling(o.k2n[t].pos,o.k2n[e].pos)})).push(t):r=u.concat(t):(r=u.filter(function(e){return o.isAncestor(o.k2n[t].pos,o.k2n[e].pos)})).push(t):!e&&-1this.popupNode.offsetWidth&&y(this.popupNode,"width",p.offsetWidth+"px")}"outside"!==u||"hoz"===l&&1===n||y(this.popupNode,"height",f.offsetHeight+"px");var h=this.popupProps;h.onOpen&&h.onOpen()},E.prototype.handlePopupClose=function(){var e=this.props.root.popupNodes,t=e.indexOf(this.popupNode);-1e.slidesToShow&&(t=e.slideWidth*e.slidesToShow*-1,o=e.slideHeight*e.slidesToShow*-1),e.slideCount%e.slidesToScroll!=0){var r=e.slideIndex+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow;if(e.rtl)r=(e.slideIndex>=e.slideCount?e.slideCount-e.slideIndex:e.slideIndex)+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow;r&&(o=e.slideIndex>e.slideCount?(t=(e.slidesToShow-(e.slideIndex-e.slideCount))*e.slideWidth*-1,(e.slidesToShow-(e.slideIndex-e.slideCount))*e.slideHeight*-1):(t=e.slideCount%e.slidesToScroll*e.slideWidth*-1,e.slideCount%e.slidesToScroll*e.slideHeight*-1))}}else e.slideCount%e.slidesToScroll!=0&&e.slideIndex+e.slidesToScroll>e.slideCount&&e.slideCount>e.slidesToShow&&(t=(e.slidesToShow-e.slideCount%e.slidesToScroll)*e.slideWidth);if(e.centerMode&&(e.infinite?t+=e.slideWidth*Math.floor(e.slidesToShow/2):t=e.slideWidth*Math.floor(e.slidesToShow/2)),n=e.vertical?e.slideIndex*e.slideHeight*-1+o:e.slideIndex*e.slideWidth*-1+t,!0===e.variableWidth){var i=void 0;n=(a=e.slideCount<=e.slidesToShow||!1===e.infinite?s.default.findDOMNode(e.trackRef).childNodes[e.slideIndex]:(i=e.slideIndex+e.slidesToShow,s.default.findDOMNode(e.trackRef).childNodes[i]))?-1*a.offsetLeft:0,!0===e.centerMode&&(a=!1===e.infinite?s.default.findDOMNode(e.trackRef).children[e.slideIndex]:s.default.findDOMNode(e.trackRef).children[e.slideIndex+e.slidesToShow+1])&&(n=-1*a.offsetLeft+(e.listWidth-a.offsetWidth)/2)}return n}},function(e,t,n){"use strict";n(542)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.routerMiddleware=t.routerActions=t.goForward=t.goBack=t.go=t.replace=t.push=t.CALL_HISTORY_METHOD=t.routerReducer=t.LOCATION_CHANGE=t.syncHistoryWithStore=void 0;var a=n(270);Object.defineProperty(t,"LOCATION_CHANGE",{enumerable:!0,get:function(){return a.LOCATION_CHANGE}}),Object.defineProperty(t,"routerReducer",{enumerable:!0,get:function(){return a.routerReducer}});var o=n(271);Object.defineProperty(t,"CALL_HISTORY_METHOD",{enumerable:!0,get:function(){return o.CALL_HISTORY_METHOD}}),Object.defineProperty(t,"push",{enumerable:!0,get:function(){return o.push}}),Object.defineProperty(t,"replace",{enumerable:!0,get:function(){return o.replace}}),Object.defineProperty(t,"go",{enumerable:!0,get:function(){return o.go}}),Object.defineProperty(t,"goBack",{enumerable:!0,get:function(){return o.goBack}}),Object.defineProperty(t,"goForward",{enumerable:!0,get:function(){return o.goForward}}),Object.defineProperty(t,"routerActions",{enumerable:!0,get:function(){return o.routerActions}});var r=s(n(394)),i=s(n(395));function s(e){return e&&e.__esModule?e:{default:e}}t.syncHistoryWithStore=r.default,t.routerMiddleware=i.default},function(e,t,n){"use strict";function a(o){return function(e){var n=e.dispatch,a=e.getState;return function(t){return function(e){return"function"==typeof e?e(n,a,o):t(e)}}}}var o=a();o.withExtraArgument=a,t.a=o},function(e,t,n){"use strict";var a=n(121),d={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},f={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},p={};p[a.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0};var h=Object.defineProperty,m=Object.getOwnPropertyNames,g=Object.getOwnPropertySymbols,y=Object.getOwnPropertyDescriptor,_=Object.getPrototypeOf,v=Object.prototype;e.exports=function e(t,n,a){if("string"==typeof n)return t;if(v){var o=_(n);o&&o!==v&&e(t,o,a)}var r=m(n);g&&(r=r.concat(g(n)));for(var i=p[t.$$typeof]||d,s=p[n.$$typeof]||d,l=0;l\n com.alibaba.nacos\n nacos-client\n ${version}\n \n*/\npackage com.alibaba.nacos.example;\n\nimport java.util.Properties;\nimport java.util.concurrent.Executor;\nimport com.alibaba.nacos.api.NacosFactory;\nimport com.alibaba.nacos.api.config.ConfigService;\nimport com.alibaba.nacos.api.config.listener.Listener;\nimport com.alibaba.nacos.api.exception.NacosException;\n\n/**\n * Config service example\n * \n * @author Nacos\n *\n */\npublic class ConfigExample {\n\n\tpublic static void main(String[] args) throws NacosException, InterruptedException {\n\t\tString serverAddr = "localhost";\n\t\tString dataId = "'+e.dataId+'";\n\t\tString group = "'+e.group+'";\n\t\tProperties properties = new Properties();\n\t\tproperties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);\n\t\tConfigService configService = NacosFactory.createConfigService(properties);\n\t\tString content = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\t\tconfigService.addListener(dataId, group, new Listener() {\n\t\t\t@Override\n\t\t\tpublic void receiveConfigInfo(String configInfo) {\n\t\t\t\tSystem.out.println("recieve:" + configInfo);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Executor getExecutor() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t\t\n\t\tboolean isPublishOk = configService.publishConfig(dataId, group, "content");\n\t\tSystem.out.println(isPublishOk);\n\t\t\n\t\tThread.sleep(3000);\n\t\tcontent = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\n\t\tboolean isRemoveOk = configService.removeConfig(dataId, group);\n\t\tSystem.out.println(isRemoveOk);\n\t\tThread.sleep(3000);\n\n\t\tcontent = configService.getConfig(dataId, group, 5000);\n\t\tSystem.out.println(content);\n\t\tThread.sleep(300000);\n\n\t}\n}\n'}},{key:"getNodejsCode",value:function(e){return"TODO"}},{key:"getCppCode",value:function(e){return"TODO"}},{key:"getShellCode",value:function(e){return"TODO"}},{key:"getPythonCode",value:function(e){return"TODO"}},{key:"openDialog",value:function(e){var t=this;this.setState({dialogvisible:!0}),this.record=e,setTimeout(function(){t.getData()})}},{key:"closeDialog",value:function(){this.setState({dialogvisible:!1})}},{key:"createCodeMirror",value:function(e,t){var n=this.refs.codepreview;n&&(n.innerHTML="",this.cm=window.CodeMirror(n,{value:t,mode:e,height:400,width:500,lineNumbers:!0,theme:"xq-light",lint:!0,tabMode:"indent",autoMatchParens:!0,textWrapping:!0,gutters:["CodeMirror-lint-markers"],extraKeys:{F1:function(e){e.setOption("fullScreen",!e.getOption("fullScreen"))},Esc:function(e){e.getOption("fullScreen")&&e.setOption("fullScreen",!1)}}}))}},{key:"changeTab",value:function(e,t){var n=this;setTimeout(function(){n[e]=!0,n.createCodeMirror("text/javascript",t)})}},{key:"render",value:function(){var e=this.props.locale,t=void 0===e?{}:e,n=I.a.createElement("div",null);return I.a.createElement("div",null,I.a.createElement(C.a,{title:t.sampleCode,style:{width:"80%"},visible:this.state.dialogvisible,footer:n,onClose:this.closeDialog.bind(this)},I.a.createElement("div",{style:{height:500}},I.a.createElement(i.a,{tip:t.loading,style:{width:"100%"},visible:this.state.loading},I.a.createElement(J.a,{shape:"text",style:{height:40,paddingBottom:10}},I.a.createElement(ee,{title:"Java",key:1,onClick:this.changeTab.bind(this,"commoneditor1",this.defaultCode)}),I.a.createElement(ee,{title:"Spring Boot",key:2,onClick:this.changeTab.bind(this,"commoneditor2",this.sprigboot_code)}),I.a.createElement(ee,{title:"Spring Cloud",key:21,onClick:this.changeTab.bind(this,"commoneditor21",this.sprigcloud_code)}),I.a.createElement(ee,{title:"Node.js",key:3,onClick:this.changeTab.bind(this,"commoneditor3",this.nodejsCode)}),I.a.createElement(ee,{title:"C++",key:4,onClick:this.changeTab.bind(this,"commoneditor4",this.cppCode)}),I.a.createElement(ee,{title:"Shell",key:5,onClick:this.changeTab.bind(this,"commoneditor5",this.shellCode)}),I.a.createElement(ee,{title:"Python",key:6,onClick:this.changeTab.bind(this,"commoneditor6",this.pythonCode)})),I.a.createElement("div",{ref:"codepreview"})))))}}]),n}(),K.displayName="ShowCodeing",V=B))||V,ne=(n(65),n(31)),ae=n.n(ne),oe=(n(527),function(){function a(e,t){for(var n=0;nl?T.a.createElement(A.a,{className:"pagination",total:s.count,pageSize:l,onChange:function(e){return a.onChangePage(e)}}):null,T.a.createElement(U,{ref:this.editInstanceDialog,serviceName:r,clusterName:n,openLoading:function(){return a.openLoading()},closeLoading:function(){return a.closeLoading()},getInstanceList:function(){return a.getInstanceList()}})):null}}]),n}(),K.displayName="InstanceTable",K.propTypes={clusterName:F.a.string,serviceName:F.a.string},V=B))||V,X=function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var n=[],a=!0,o=!1,r=void 0;try{for(var i,s=e[Symbol.iterator]();!(a=(i=s.next()).done)&&(n.push(i.value),!t||n.length!==t);a=!0);}catch(e){o=!0,r=e}finally{try{!a&&s.return&&s.return()}finally{if(o)throw r}}return n}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")},Z=(n(543),function(){function a(e,t){for(var n=0;nthis.state.pageSize&&E.a.createElement("div",{style:{marginTop:10,textAlign:"right"}},E.a.createElement(y.a,{current:this.state.currentPage,total:this.state.total,pageSize:this.state.pageSize,onChange:function(e){return a.setState({currentPage:e},function(){return a.queryServiceList()})}}))),E.a.createElement(Y.a,{ref:this.editServiceDialog,openLoading:function(){return a.openLoading()},closeLoading:function(){return a.closeLoading()},queryServiceList:function(){return a.setState({currentPage:1},function(){return a.queryServiceList()})}}))}}]),n}(),o.displayName="ServiceList",a=r))||a;t.a=H},function(e,t,n){"use strict";n(32);var a,o,r,i=n(19),s=n.n(i),l=(n(66),n(38)),u=n.n(l),c=(n(63),n(16)),d=n.n(c),f=(n(28),n(8)),p=n.n(f),h=(n(26),n(11)),m=n.n(h),g=(n(36),n(20)),y=n.n(g),_=(n(24),n(9)),v=n.n(_),b=n(0),M=n.n(b),w=n(41),k=n(1),S=(n(511),function(){function a(e,t){for(var n=0;n=t.length?{value:void 0,done:!0}:(e=a(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var l=n(101),u=n(100);e.exports=function(s){return function(e,t){var n,a,o=String(u(e)),r=l(t),i=o.length;return r<0||i<=r?s?"":void 0:(n=o.charCodeAt(r))<55296||56319=e.length?(this._t=void 0,o(1)):o(0,"keys"==t?n:"values"==t?e[n]:[n,e[n]])},"values"),r.Arguments=r.Array,a("keys"),a("values"),a("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){e.exports={default:n(357),__esModule:!0}},function(e,t,n){n(358),n(363),n(364),n(365),e.exports=n(51).Symbol},function(e,t,n){"use strict";var a=n(46),i=n(52),o=n(60),r=n(73),s=n(134),l=n(359).KEY,u=n(75),c=n(103),d=n(108),f=n(83),p=n(62),h=n(109),m=n(110),g=n(360),y=n(361),_=n(74),v=n(59),b=n(61),M=n(99),w=n(80),k=n(107),S=n(362),T=n(136),L=n(58),x=n(81),C=T.f,E=L.f,D=S.f,O=a.Symbol,Y=a.JSON,N=Y&&Y.stringify,j="prototype",P=p("_hidden"),I=p("toPrimitive"),A={}.propertyIsEnumerable,H=c("symbol-registry"),R=c("symbols"),z=c("op-symbols"),F=Object[j],W="function"==typeof O,V=a.QObject,K=!V||!V[j]||!V[j].findChild,B=o&&u(function(){return 7!=k(E({},"a",{get:function(){return E(this,"a",{value:7}).a}})).a})?function(e,t,n){var a=C(F,t);a&&delete F[t],E(e,t,n),a&&e!==F&&E(F,t,a)}:E,U=function(e){var t=R[e]=k(O[j]);return t._k=e,t},q=W&&"symbol"==typeof O.iterator?function(e){return"symbol"==typeof e}:function(e){return e instanceof O},G=function(e,t,n){return e===F&&G(z,t,n),_(e),t=M(t,!0),_(n),i(R,t)?(n.enumerable?(i(e,P)&&e[P][t]&&(e[P][t]=!1),n=k(n,{enumerable:w(0,!1)})):(i(e,P)||E(e,P,w(1,{})),e[P][t]=!0),B(e,t,n)):E(e,t,n)},J=function(e,t){_(e);for(var n,a=g(t=b(t)),o=0,r=a.length;oo;)i(R,t=n[o++])||t==P||t==l||a.push(t);return a},Z=function(e){for(var t,n=e===F,a=D(n?z:b(e)),o=[],r=0;a.length>r;)!i(R,t=a[r++])||n&&!i(F,t)||o.push(R[t]);return o};W||(s((O=function(){if(this instanceof O)throw TypeError("Symbol is not a constructor!");var t=f(0te;)p(ee[te++]);for(var ne=x(p.store),ae=0;ne.length>ae;)m(ne[ae++]);r(r.S+r.F*!W,"Symbol",{for:function(e){return i(H,e+="")?H[e]:H[e]=O(e)},keyFor:function(e){if(!q(e))throw TypeError(e+" is not a symbol!");for(var t in H)if(H[t]===e)return t},useSetter:function(){K=!0},useSimple:function(){K=!1}}),r(r.S+r.F*!W,"Object",{create:function(e,t){return void 0===t?k(e):J(k(e),t)},defineProperty:G,defineProperties:J,getOwnPropertyDescriptor:Q,getOwnPropertyNames:X,getOwnPropertySymbols:Z}),Y&&r(r.S+r.F*(!W||u(function(){var e=O();return"[null]"!=N([e])||"{}"!=N({a:e})||"{}"!=N(Object(e))})),"JSON",{stringify:function(e){for(var t,n,a=[e],o=1;arguments.length>o;)a.push(arguments[o++]);if(n=t=a[1],(v(t)||void 0!==e)&&!q(e))return y(t)||(t=function(e,t){if("function"==typeof n&&(t=n.call(this,e,t)),!q(t))return t}),a[1]=t,N.apply(Y,a)}}),O[j][I]||n(57)(O[j],I,O[j].valueOf),d(O,"Symbol"),d(Math,"Math",!0),d(a.JSON,"JSON",!0)},function(e,t,n){var a=n(83)("meta"),o=n(59),r=n(52),i=n(58).f,s=0,l=Object.isExtensible||function(){return!0},u=!n(75)(function(){return l(Object.preventExtensions({}))}),c=function(e){i(e,a,{value:{i:"O"+ ++s,w:{}}})},d=e.exports={KEY:a,NEED:!1,fastKey:function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!r(e,a)){if(!l(e))return"F";if(!t)return"E";c(e)}return e[a].i},getWeak:function(e,t){if(!r(e,a)){if(!l(e))return!0;if(!t)return!1;c(e)}return e[a].w},onFreeze:function(e){return u&&d.NEED&&l(e)&&!r(e,a)&&c(e),e}}},function(e,t,n){var s=n(81),l=n(105),u=n(84);e.exports=function(e){var t=s(e),n=l.f;if(n)for(var a,o=n(e),r=u.f,i=0;o.length>i;)r.call(e,a=o[i++])&&t.push(a);return t}},function(e,t,n){var a=n(131);e.exports=Array.isArray||function(e){return"Array"==a(e)}},function(e,t,n){var a=n(61),o=n(135).f,r={}.toString,i="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){return i&&"[object Window]"==r.call(e)?function(e){try{return o(e)}catch(e){return i.slice()}}(e):o(a(e))}},function(e,t){},function(e,t,n){n(110)("asyncIterator")},function(e,t,n){n(110)("observable")},function(e,t,n){e.exports={default:n(367),__esModule:!0}},function(e,t,n){n(368),e.exports=n(51).Object.setPrototypeOf},function(e,t,n){var a=n(73);a(a.S,"Object",{setPrototypeOf:n(369).set})},function(e,t,o){var n=o(59),a=o(74),r=function(e,t){if(a(e),!n(t)&&null!==t)throw TypeError(t+": can't set as prototype!")};e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,n,a){try{(a=o(126)(Function.call,o(136).f(Object.prototype,"__proto__").set,2))(e,[]),n=!(e instanceof Array)}catch(e){n=!0}return function(e,t){return r(e,t),n?e.__proto__=t:a(e,t),e}}({},!1):void 0),check:r}},function(e,t,n){e.exports={default:n(371),__esModule:!0}},function(e,t,n){n(372);var a=n(51).Object;e.exports=function(e,t){return a.create(e,t)}},function(e,t,n){var a=n(73);a(a.S,"Object",{create:n(107)})},function(e,t,n){"use strict"; +/** @license React v16.6.1 + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var c=n(137),a="function"==typeof Symbol&&Symbol.for,d=a?Symbol.for("react.element"):60103,u=a?Symbol.for("react.portal"):60106,o=a?Symbol.for("react.fragment"):60107,r=a?Symbol.for("react.strict_mode"):60108,i=a?Symbol.for("react.profiler"):60114,s=a?Symbol.for("react.provider"):60109,l=a?Symbol.for("react.context"):60110,f=a?Symbol.for("react.concurrent_mode"):60111,p=a?Symbol.for("react.forward_ref"):60112,h=a?Symbol.for("react.suspense"):60113,m=a?Symbol.for("react.memo"):60115,g=a?Symbol.for("react.lazy"):60116,y="function"==typeof Symbol&&Symbol.iterator;function _(e){for(var t=arguments.length-1,n="https://reactjs.org/docs/error-decoder.html?invariant="+e,a=0;a