Remove the 'ng' suffix of sub projects.

This commit is contained in:
yukon
2017-03-16 09:58:57 +08:00
commit b29f52bdfb
1052 changed files with 260816 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/.idea/
/target/
*.iml

16
.travis.yml Normal file
View File

@@ -0,0 +1,16 @@
notifications:
email:
recipients:
- styletang.me@gmail.com
on_success: change
on_failure: always
language: java
jdk:
# - oraclejdk8
- oraclejdk7
script:
- travis_retry mvn -B clean
- travis_retry mvn -B package findbugs:findbugs jacoco:report coveralls:report
#after_success:
# - mvn sonar:sonar

579
LICENSE Normal file
View File

@@ -0,0 +1,579 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (properties) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
-----------------------------------------------------------------------------
rocket-console
-------------------------------------------
develop base on didapinche's rocket-console
https://github.com/didapinchegit/rocket-console
Apache License
Version 2.0, January 2004
angular
-------------------------------------------
https://github.com/angular/angular
The MIT License
Copyright (c) 2014-2017 Google, Inc. http://angular.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
angular-material
-----------------------------------------
https://github.com/angular/material
The MIT License
Copyright (c) 2014-2017 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
bootstrap
-------------------------------------------
http://getbootstrap.com/
The MIT License (MIT)
Copyright (c) 2011-2017 Twitter, Inc.
Copyright (c) 2011-2017 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
bootstrap-material-design
-------------------------------------------------
The MIT License (MIT)
Copyright (c) 2015-2016, Federico Zivolo and contributors - https://github.com/FezVrasta/bootstrap-material-design
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Acknowledgements:
Some original Bootstrap code and documentation http://getbootstrap.com Copyright (c) 2011-2015 Twitter, Inc
Some original MDL code http://www.getmdl.io/ Copyright 2015 Google Inc. All Rights Reserved.
angular-chosen
--------------------------------------
The MIT License
Copyright (c) 2013 Localytics http://www.localytics.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
bootstrap-datetimepicker
----------------------------------------------------------
https://github.com/Eonasdan/bootstrap-datetimepicker
The MIT License (MIT)
Copyright (c) 2015 Jonathan Peterson (@Eonasdan)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
echarts
----------------------------------
https://github.com/ecomfe/echarts
Copyright (c) 2017, Baidu Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
html5-boilerplate
------------------------------------------
https://github.com/h5bp/html5-boilerplate
Copyright (c) HTML5 Boilerplate
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Font-Awesome
----------------------
https://github.com/FortAwesome/Font-Awesome
License
The Font Awesome font is licensed under the SIL OFL 1.1:
http://scripts.sil.org/OFL
Font Awesome CSS, LESS, and Sass files are licensed under the MIT License:
https://opensource.org/licenses/mit-license.html
The Font Awesome documentation is licensed under the CC BY 3.0 License:
http://creativecommons.org/licenses/by/3.0/
Attribution is no longer required as of Font Awesome 3.0, but much appreciated:
Font Awesome by Dave Gandy - http://fontawesome.io
Full details: http://fontawesome.io/license/
jquery
---------------------
Copyright JS Foundation and other contributors, https://js.foundation/
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery
The following license applies to all parts of this software except as
documented below:
====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====
All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.
12.json-bigint
https://github.com/sidorares/json-bigint
The MIT License (MIT)
Copyright (c) 2013 Andrey Sidorov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Ladda
---------------
https://github.com/hakimel/Ladda
Copyright (C) 2016 Hakim El Hattab, http://hakim.se
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
ngDialog
------------
http://github.com/likeastore/ngDialog
MIT Licensed
Copyright (c) 2013-2015, Likeastore.com info@likeastore.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
notification
---------------------
https://github.com/alexcrack/angular-ui-notification
The MIT License (MIT)
Copyright (c) 2014 Alexey Avramchik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
bootstrap-validator
---------------------------
https://github.com/1000hz/bootstrap-validator
The MIT License (MIT)
Copyright (c) 2016 Cina Saffary
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

5
NOTICE Normal file
View File

@@ -0,0 +1,5 @@
Apache RocketMQ Console
Copyright 2016-2017 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

61
README.md Normal file
View File

@@ -0,0 +1,61 @@
## RocketMQ-Console-Ng[![Build Status](https://travis-ci.org/rocketmq/rocketmq-console-ng.svg?branch=master)](https://travis-ci.org/rocketmq/rocketmq-console-ng) [![Coverage Status](https://coveralls.io/repos/github/rocketmq/rocketmq-console-ng/badge.svg?branch=master)](https://coveralls.io/github/rocketmq/rocketmq-console-ng?branch=master)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
## How To Install
### With Docker
* get docker image
```
mvn clean package -Dmaven.test.skip=true docker:build
```
or
```
docker pull styletang/rocketmq-console-ng
```
* run it (change namesvrAddr and port yourself)
```
docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 -t styletang/rocketmq-console-ng
```
### Without Docker
require java 1.7
```
mvn spring-boot:run
```
or
```
mvn clean package -Dmaven.test.skip=true
java -jar target/rocketmq-console-ng-1.0.0.jar
```
#### Tips
* if you download package slow,you can change maven's mirror(maven's settings.xml)
```
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
```
* if you use the rocketmq < 3.5.8,please add -Dcom.rocketmq.sendMessageWithVIPChannel=false when you start rocketmq-console-ng(or you can change it in ops page)
* change the rocketmq.config.namesrvAddr in resource/application.properties.(or you can change it in ops page)
## UserGuide
[English](https://github.com/rocketmq/rocketmq-console-ng/blob/master/doc/1_0_0/UserGuide_EN.md)
[中文](https://github.com/rocketmq/rocketmq-console-ng/blob/master/doc/1_0_0/UserGuide_CN.md)
## Communicate With Us
* QQ Group:535273860
* You can communicate with us use QQ.(or send us issue / pull request)
* You can join us and make a contribute for rocketmq-console-ng.

102
doc/1_0_0/Milestone.md Normal file
View File

@@ -0,0 +1,102 @@
#Deploy Plan
we will deploy the first rocketmq-console-ng use rocketmq-tools 3.5.8(or 4.0.0),base on [rocket-console](https://github.com/didapinchegit/rocket-console)thanks didapinche.com
## Framework
* 0. we use spring-boot + bootstrap + angularjs
## something to improve
* 0. clean code (checkStyle codeStyle to be done)
* 1. international
* 2. compress fe'resource
* 3. navigation bar can improve
* 4. write operation need confirm,action show the detail result
* 5. layout/UI should improve
* 6. change to spring-boot
## something to fix
* query Message by topic and time is not accurate will lost some message
* consumer can consume the message when topic has been deleted
* can't show producerList,we can only query a online producer use topic and groupName,not easy to use.
* resetOffset should be improve,online consumer can return the reset result but offline's can't
* we can't set clusterName when create topic or consumer
* when create a new consumer,if not be consumed,can't be found in consumerList
## something to add
* 1. dashboard
# Roadmap
## Improve
- [x] clean code (checkStyle codeStyle to be done) -- StyleTang
- [x] international -- Deploy by [tcrow](https://github.com/tcrow)
- [x] layout/UI -- Deploy by [tcrow](https://github.com/tcrow)
- [x] compress fe'resource
- [x] navigation bar can improve
- [x] write operation need confirm,action show the detail result || already have
- [x] layout/UI should improve
- [x] change to spring-boot -- Deploy by syzjava
- [x] change to bootstrap angularjs -- Deploy by [tcrow](https://github.com/tcrow)
- [x] improve search message --StyleTang
## Fix
- [x] query Message by topic and time is not accurate will lost some message -- StyleTang (need test)
- [x] consumer can consume the message when topic has been deleted // offset be clear.if have problem,reopen it.
- [ ] can't show producerList,we can only query a online producer use topic and groupName,not easy to use. [need this issues](https://issues.apache.org/jira/browse/ROCKETMQ-49)(next milestone)
- [ ] resetOffset should be improve,online consumer can return the reset result but offline's can't //this version(3.5.8) may be can't fix (next milestone)
- [x] we can't set clusterName when create topic or consumer -- StyleTang
- [x] when create a new consumer,if not be consumed,can't be found in consumerList //it Fixed,But this page is too slow,need improve --StyleTang
- [x] message view page,resend message (version >=3.5.8) have bug -- StyleTang
## Add
- [ ] DashboardController -- Deploy by [tcrow](https://github.com/tcrow)
- [x] rocketmq topic tps 5m line chart
- [x] rocketmq topic top10 table
- [x] broker load 5m line chart
- [x] broker load top10 table
- [ ] topic exception table(next milestone)
## Already Have (Deploy by StyleTang) But Can Improve
### Cluster
- [x] ClusterController
- [x] Cluster OverView
- [x] Broker Status
- [x] Broker Config
### Topic
- [x] TopicController
- [x] TopicList
- [x] Topic Status
- [x] Topic Router
- [x] View Topic Config
- [x] Topci Add / Update
- [X] Send A Test Topic
- [x] Reset ConsumerGroup's Offset Under This Topic
- [x] Delete This Topic
### Producer
- [x] ProducerController
- [x] Producer Client Info
### Consumer
- [x] ConsumerController
- [x] ConsumerList
- [x] Consumer Client Info
- [x] Topic Consume Status Under This Consumer Group
- [x] View Consumer Config
- [x] Consumer Add / Update
- [x] Delete This Consumer
### Message
- [x] MessageController
- [x] Query By Topic And Time
- [x] Query By Topic And Key
- [x] Query By MessageId(OffsetMessageId)
- [x] A Nice Message Detail View
- [x] Message Consume Status
- [x] Resend Message To A Consume Group

64
doc/1_0_0/UserGuide_CN.md Normal file
View File

@@ -0,0 +1,64 @@
# RocketMQ使用文档
## 运维页面
* 你可以修改这个服务使用的navesvr的地址
* 你可以修改这个服务是否使用VIPChannel(如果你的mq server版本小于3.5.8,请设置不使用)
## 驾驶舱
* 查看broker的消息量总量/5分钟图
* 查看单一主题的消息量(总量/趋势图)
## 集群页面
* 查看集群的分布情况
* cluster与broker关系
* broker
* 查看broker具体信息/运行信息
* 查看broker配置信息
## 主题页面
* 展示所有的主题,可以通过搜索框进行过滤
* 筛选 普通/重试/死信 主题
* 添加/更新主题
* clusterName 创建在哪几个cluster上
* brokerName 创建在哪几个broker上
* topicName 主题名
* writeQueueNums 写队列数量
* readQueueNums 读队列数量
* perm //2是写 4是读 6是读写
* 状态 查询消息投递状态投递到哪些broker/哪些queue/多少量等)
* 路由 查看消息的路由现在你发这个主题的消息会发往哪些broker对应broker的queue信息
* CONSUMER管理这个topic都被哪些group消费了消费情况何如
* topic配置查看变更当前的配置
* 发送消息(向这个主题发送一个测试消息)
* 重置消费位点(分为在线和不在线两种情况,不过都需要检查重置是否成功)
* 删除主题 会删除掉所有broker以及namesvr上的主题配置和路由信息
## 消费者页面
* 展示所有的消费组,可以通过搜索框进行过滤
* 刷新页面/每隔五秒定时刷新页面
* 按照订阅组/数量/TPS/延迟 进行排序
* 添加/更新消费组
* clusterName 创建咋哪几个集群上
* brokerName 创建在哪几个broker上
* groupName 消费组名字
* consumeEnable //是否可以消费 FALSE的话将无法进行消费
* consumeBroadcastEnable //是否可以广播消费
* retryQueueNums //重试队列的大小
* brokerId //正常情况从哪消费
* whichBrokerWhenConsumeSlowly//出问题了从哪消费
* 终端 在线的消费客户端查看,包括版本订阅信息和消费模式
* 消费详情 对应消费组的消费明细查看这个消费组订阅的所有Topic的消费情况每个queue对应的消费client查看包括Retry消息
* 配置 查看变更消费组的配置
* 删除 在指定的broker上删除消费组
## 发布管理页面
* 通过Topic和Group查询在线的消息生产者客户端
* 信息包含客户端主机 版本
## 消息查询页面
* 根据Topic和时间区间查询
*由于数据量大 做多只会展示2000条多的会被忽略
* 根据Topic和Key进行查询
* 最多只会展示64条
* 根据消息主题和消息Id进行消息的查询
* 消息详情可以展示这条消息的详细信息,查看消息对应到具体消费组的消费情况(如果异常,可以查看具体的异常信息)。可以向指定的消费组重发消息。

65
doc/1_0_0/UserGuide_EN.md Normal file
View File

@@ -0,0 +1,65 @@
# RocketMQ User Guide
## OPS Page
* You can change console's namesvrAddr here
* You can change the value of useVIPChannel here (if you rocketMQ version < 3.5.8,the value of useVIPChannel should be false)
## DashBoard Page
* broker's message count (broker total message count/5 min trend)
* topic's message counttopic total message count/5 min trend
## Cluster Page
* Cluster Detail
* relation between cluster and broker
* broker's master / salve node
* broker'a detail info(runtime info)
* broker's config
## Topic Page
* show all the topics,you can filter topic by search bar
* filter (Normal/retry/dead) topic
* Add/Update Topic
* clusterName (create on which cluster)
* brokerName (create on which broker)
* topicName
* writeQueueNums
* readQueueNums
* perm //2 for write 4 for read 6 for write and read
* STATUS look over message send status(send to which broker/which queue/how many messages)
* ROUTER look update topic's routerthis topic send to which brokerthe broker's queue info
* CONSUMER MANAGEthis topic consume by which group,how about the consume state
* TOPIC CONFIGcheck or change the topic's config
* SEND MESSAGEsend a test message
* Reset CONSUMER OFFSET (the consumer online or not online is different,you need check the reset result)
* DELETE will delete the topic on all broker and namesvr
## Consumer Page
* show all the consumers,you can filter consumer by search bar
* refresh page/refresh page per 5 seconds
* order by SubscriptionGroup/Quantity/TPS/Delay
* Add/Update Consumer
* clusterName (create on which cluster)
* brokerName (create on which broker)
* groupName (consumer group name)
* consumeEnable (this group can't consume message if this is false)
* consumeBroadcastEnable (can't use broadcast is this is false)
* retryQueueNums
* brokerId (consume form where when broker is normal)
* whichBrokerWhenConsumeSlowly(consume form where when broker has problem)
* CLIENT (look over online consumer's client,include subscribe info and consume mode)
* CONSUME DETAIL (look over this consumer's consume detail,broker offset and the consumer offset,queue consumed by which client)
* CONFIG check or change the consumer's config
* DELETE (delete the consumer group on selected group)
## Producer Page
* Query online producer client by topic and group
* show client's server / version
## Message Page
* Query By Topic And Time
*Only Return 2000 Messagesthe message more than 2000 will be hide
* Query By Topic And Key
* Only Return 64 Messages
* Query By Topic And MessageId
* look over this message's detail info.you can see the message's consume state(each group has one line),show the exception message if has exception.
you can send this message to the group you selected

276
pom.xml Normal file
View File

@@ -0,0 +1,276 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<groupId>org.apache</groupId>
<artifactId>rocketmq-console-ng</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>rocketmq-console-ng</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<guava.version>16.0.1</guava.version>
<commons-digester.version>2.1</commons-digester.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-io.version>2.4</commons-io.version>
<commons-cli.version>1.2</commons-cli.version>
<rocketmq.version>4.0.0-incubating</rocketmq.version>
<surefire.version>2.19.1</surefire.version>
<aspectj.version>1.6.11</aspectj.version>
<main.basedir>${basedir}/../..</main.basedir>
<docker.image.prefix>styletang</docker.image.prefix>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-tools</artifactId>
<version>${rocketmq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-namesrv</artifactId>
<version>${rocketmq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-broker</artifactId>
<version>${rocketmq.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- Spring AOP + AspectJ -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joor</artifactId>
<version>0.9.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.11</version>
<configuration>
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<imageTags>
<imageTag>${project.version}</imageTag>
<imageTag>latest</imageTag>
</imageTags>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<excludes>src/main/resources</excludes>
<configLocation>style/rmq_checkstyle.xml</configLocation>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<excludes>
<exclude>src/test/**</exclude>
</excludes>
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
<argLine>@{failsafeArgLine}</argLine>
<includes>
<include>src/test/**</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!--<plugin>-->
<!--<artifactId>maven-javadoc-plugin</artifactId>-->
<!--<version>2.10.4</version>-->
<!--<configuration>-->
<!--<charset>UTF-8</charset>-->
<!--</configuration>-->
<!--<executions>-->
<!--<execution>-->
<!--<id>attach-javadocs</id>-->
<!--<goals>-->
<!--<goal>jar</goal>-->
<!--</goals>-->
<!--</execution>-->
<!--</executions>-->
<!--</plugin>-->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco.exec</destFile>
</configuration>
</execution>
<execution>
<id>default-prepare-agent-integration</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco-it.exec</destFile>
<propertyName>failsafeArgLine</propertyName>
</configuration>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>default-report-integration</id>
<goals>
<goal>report-integration</goal>
</goals>
</execution>
<execution>
<id>default-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<!-- implementation is needed only for Maven 2 -->
<rule implementation="org.jacoco.maven.RuleConfiguration">
<element>BUNDLE</element>
<limits>
<!-- implementation is needed only for Maven 2 -->
<limit implementation="org.jacoco.report.check.Limit">
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>0.20</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>4.3.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.4</version>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,6 @@
FROM java:8
VOLUME /tmp
ADD rocketmq-console-ng-1.0.0.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar /app.jar" ]

View File

@@ -0,0 +1,31 @@
/*
* 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 org.apache.rocketmq.console;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAutoConfiguration
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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 org.apache.rocketmq.console.aspect.admin;
import java.lang.reflect.Method;
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
import org.apache.rocketmq.console.service.client.MQAdminInstance;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Aspect
@Service
public class MQAdminAspect {
private Logger logger = LoggerFactory.getLogger(MQAdminAspect.class);
public MQAdminAspect() {
}
@Pointcut("execution(* org.apache.rocketmq.console.service.client.MQAdminExtImpl..*(..))")
public void mQAdminMethodPointCut() {
}
@Pointcut("@annotation(org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod)")
public void multiMQAdminMethodPointCut() {
}
@Around(value = "mQAdminMethodPointCut() || multiMQAdminMethodPointCut()")
public Object aroundMQAdminMethod(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object obj = null;
try {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
MultiMQAdminCmdMethod multiMQAdminCmdMethod = method.getAnnotation(MultiMQAdminCmdMethod.class);
if (multiMQAdminCmdMethod != null && multiMQAdminCmdMethod.timeoutMillis() > 0) {
MQAdminInstance.initMQAdminInstance(multiMQAdminCmdMethod.timeoutMillis());
}
else {
MQAdminInstance.initMQAdminInstance(0);
}
obj = joinPoint.proceed();
}
finally {
MQAdminInstance.destroyMQAdminInstance();
logger.debug("op=look method={} cost={}", joinPoint.getSignature().getName(), System.currentTimeMillis() - start);
}
return obj;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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 org.apache.rocketmq.console.aspect.admin.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MultiMQAdminCmdMethod {
long timeoutMillis() default 0;
}

View File

@@ -0,0 +1,30 @@
/*
* 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 org.apache.rocketmq.console.aspect.admin.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OriginalControllerReturnValue {
}

View File

@@ -0,0 +1,87 @@
/*
* 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 org.apache.rocketmq.console.config;
import java.io.File;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.common.MixAll;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY;
@Configuration
@ConfigurationProperties(prefix = "rocketmq.config")
public class RMQConfigure {
private Logger logger = LoggerFactory.getLogger(RMQConfigure.class);
//use rocketmq.namesrv.addr first,if it is empty,than use system proerty or system env
private volatile String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY, System.getenv(MixAll.NAMESRV_ADDR_ENV));
private volatile String isVIPChannel = System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true");
private String dataPath;
private boolean enableDashBoardCollect;
public String getNamesrvAddr() {
return namesrvAddr;
}
public void setNamesrvAddr(String namesrvAddr) {
if (StringUtils.isNotBlank(namesrvAddr)) {
this.namesrvAddr = namesrvAddr;
System.setProperty(MixAll.NAMESRV_ADDR_PROPERTY, namesrvAddr);
logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr);
}
}
public String getRocketMqConsoleDataPath() {
return dataPath;
}
public String getConsoleCollectData() {
return dataPath + File.separator + "dashboard";
}
public void setDataPath(String dataPath) {
this.dataPath = dataPath;
}
public String getIsVIPChannel() {
return isVIPChannel;
}
public void setIsVIPChannel(String isVIPChannel) {
if (StringUtils.isNotBlank(isVIPChannel)) {
this.isVIPChannel = isVIPChannel;
System.setProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, isVIPChannel);
logger.info("setIsVIPChannel isVIPChannel={}", isVIPChannel);
}
}
public boolean isEnableDashBoardCollect() {
return enableDashBoardCollect;
}
public void setEnableDashBoardCollect(String enableDashBoardCollect) {
this.enableDashBoardCollect = Boolean.valueOf(enableDashBoardCollect);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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 org.apache.rocketmq.console.controller;
import org.apache.rocketmq.console.service.ClusterService;
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 javax.annotation.Resource;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/cluster")
public class ClusterController {
@Resource
private ClusterService clusterService;
@RequestMapping(value = "/list.query", method = RequestMethod.GET)
@ResponseBody
public Object list() {
return clusterService.list();
}
@RequestMapping(value = "/brokerConfig.query", method = RequestMethod.GET)
@ResponseBody
public Object brokerConfig(@RequestParam String brokerAddr) {
return clusterService.getBrokerConfig(brokerAddr);
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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 org.apache.rocketmq.console.controller;
import com.google.common.base.Preconditions;
import javax.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.console.model.ConnectionInfo;
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
import org.apache.rocketmq.console.service.ConsumerService;
import org.apache.rocketmq.console.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
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;
@Controller
@RequestMapping("/consumer")
public class ConsumerController {
private Logger logger = LoggerFactory.getLogger(ConsumerController.class);
@Resource
private ConsumerService consumerService;
@RequestMapping(value = "/groupList.query")
@ResponseBody
public Object list() {
return consumerService.queryGroupList();
}
@RequestMapping(value = "/group.query")
@ResponseBody
public Object groupQuery(@RequestParam String consumerGroup) {
return consumerService.queryGroup(consumerGroup);
}
@RequestMapping(value = "/resetOffset.do", method = {RequestMethod.POST})
@ResponseBody
public Object resetOffset(@RequestBody ResetOffsetRequest resetOffsetRequest) {
logger.info("op=look resetOffsetRequest={}", JsonUtil.obj2String(resetOffsetRequest));
return consumerService.resetOffset(resetOffsetRequest);
}
@RequestMapping(value = "/examineSubscriptionGroupConfig.query")
@ResponseBody
public Object examineSubscriptionGroupConfig(@RequestParam String consumerGroup) {
return consumerService.examineSubscriptionGroupConfig(consumerGroup);
}
@RequestMapping(value = "/deleteSubGroup.do", method = {RequestMethod.POST})
@ResponseBody
public Object deleteSubGroup(@RequestBody DeleteSubGroupRequest deleteSubGroupRequest) {
return consumerService.deleteSubGroup(deleteSubGroupRequest);
}
@RequestMapping(value = "/createOrUpdate.do", method = {RequestMethod.POST})
@ResponseBody
public Object consumerCreateOrUpdateRequest(@RequestBody ConsumerConfigInfo consumerConfigInfo) {
Preconditions.checkArgument(CollectionUtils.isNotEmpty(consumerConfigInfo.getBrokerNameList()) || CollectionUtils.isNotEmpty(consumerConfigInfo.getClusterNameList()),
"clusterName or brokerName can not be all blank");
return consumerService.createAndUpdateSubscriptionGroupConfig(consumerConfigInfo);
}
@RequestMapping(value = "/fetchBrokerNameList.query", method = {RequestMethod.GET})
@ResponseBody
public Object fetchBrokerNameList(@RequestParam String consumerGroup) {
return consumerService.fetchBrokerNameSetBySubscriptionGroup(consumerGroup);
}
@RequestMapping(value = "/queryTopicByConsumer.query")
@ResponseBody
public Object queryConsumerByTopic(@RequestParam String consumerGroup) {
return consumerService.queryConsumeStatsListByGroupName(consumerGroup);
}
@RequestMapping(value = "/consumerConnection.query")
@ResponseBody
public Object consumerConnection(@RequestParam(required = false) String consumerGroup) {
ConsumerConnection consumerConnection = consumerService.getConsumerConnection(consumerGroup);
consumerConnection.setConnectionSet(ConnectionInfo.buildConnectionInfoHashSet(consumerConnection.getConnectionSet()));
return consumerConnection;
}
@RequestMapping(value = "/consumerRunningInfo.query")
@ResponseBody
public Object getConsumerRunningInfo(@RequestParam String consumerGroup, @RequestParam String clientId,
@RequestParam boolean jstack) {
return consumerService.getConsumerRunningInfo(consumerGroup, clientId, jstack);
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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 org.apache.rocketmq.console.controller;
import javax.annotation.Resource;
import com.google.common.base.Strings;
import org.apache.rocketmq.console.service.DashboardService;
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;
@Controller
@RequestMapping("/dashboard")
public class DashboardController {
@Resource
DashboardService dashboardService;
@RequestMapping(value = "/broker.query", method = RequestMethod.GET)
@ResponseBody
public Object broker(@RequestParam String date) {
return dashboardService.queryBrokerData(date);
}
@RequestMapping(value = "/topic.query", method = RequestMethod.GET)
@ResponseBody
public Object topic(@RequestParam String date, String topicName) {
if (Strings.isNullOrEmpty(topicName)) {
return dashboardService.queryTopicData(date);
}
return dashboardService.queryTopicData(date,topicName);
}
@RequestMapping(value = "/topicCurrent", method = RequestMethod.GET)
@ResponseBody
public Object topicCurrent() {
return dashboardService.queryTopicCurrentData();
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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 org.apache.rocketmq.console.controller;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.apache.rocketmq.console.model.MessageView;
import org.apache.rocketmq.console.service.MessageService;
import org.apache.rocketmq.console.util.JsonUtil;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/message")
public class MessageController {
private Logger logger = LoggerFactory.getLogger(MessageController.class);
@Resource
private MessageService messageService;
@RequestMapping(value = "/viewMessage.query", method = RequestMethod.GET)
@ResponseBody
public Object viewMessage(@RequestParam(required = false) String topic, @RequestParam String msgId) {
Map<String, Object> messageViewMap = Maps.newHashMap();
Pair<MessageView, List<MessageTrack>> messageViewListPair = messageService.viewMessage(topic, msgId);
messageViewMap.put("messageView", messageViewListPair.getObject1());
messageViewMap.put("messageTrackList", messageViewListPair.getObject2());
return messageViewMap;
}
@RequestMapping(value = "/queryMessageByTopicAndKey.query", method = RequestMethod.GET)
@ResponseBody
public Object queryMessageByTopicAndKey(@RequestParam String topic, @RequestParam String key) {
return messageService.queryMessageByTopicAndKey(topic, key);
}
@RequestMapping(value = "/queryMessageByTopic.query", method = RequestMethod.GET)
@ResponseBody
public Object queryMessageByTopic(@RequestParam String topic, @RequestParam long begin,
@RequestParam long end) {
return messageService.queryMessageByTopic(topic, begin, end);
}
@RequestMapping(value = "/consumeMessageDirectly.do", method = RequestMethod.POST)
@ResponseBody
public Object consumeMessageDirectly(@RequestParam String topic, @RequestParam String consumerGroup,
@RequestParam String msgId,
@RequestParam(required = false) String clientId) {
logger.info("msgId={} consumerGroup={} clientId={}", msgId, consumerGroup, clientId);
ConsumeMessageDirectlyResult consumeMessageDirectlyResult = messageService.consumeMessageDirectly(topic, msgId, consumerGroup, clientId);
logger.info("consumeMessageDirectlyResult={}", JsonUtil.obj2String(consumeMessageDirectlyResult));
return consumeMessageDirectlyResult;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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 org.apache.rocketmq.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
import org.apache.rocketmq.console.service.MonitorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@Controller
@RequestMapping("/monitor")
public class MonitorController {
private Logger logger = LoggerFactory.getLogger(MonitorController.class);
@Resource
private MonitorService monitorService;
@RequestMapping(value = "/createOrUpdateConsumerMonitor.do", method = {RequestMethod.POST})
@ResponseBody
public Object createOrUpdateConsumerMonitor(@RequestParam String consumeGroupName, @RequestParam int minCount,
@RequestParam int maxDiffTotal) {
return monitorService.createOrUpdateConsumerMonitor(consumeGroupName, new ConsumerMonitorConfig(minCount, maxDiffTotal));
}
@RequestMapping(value = "/consumerMonitorConfig.query", method = {RequestMethod.GET})
@ResponseBody
public Object consumerMonitorConfig() {
return monitorService.queryConsumerMonitorConfig();
}
@RequestMapping(value = "/consumerMonitorConfigByGroupName.query", method = {RequestMethod.GET})
@ResponseBody
public Object consumerMonitorConfigByGroupName(@RequestParam String consumeGroupName) {
return monitorService.queryConsumerMonitorConfigByGroupName(consumeGroupName);
}
@RequestMapping(value = "/deleteConsumerMonitor.do", method = {RequestMethod.POST})
@ResponseBody
public Object deleteConsumerMonitor(@RequestParam String consumeGroupName) {
return monitorService.deleteConsumerMonitor(consumeGroupName);
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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 org.apache.rocketmq.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.console.aspect.admin.annotation.OriginalControllerReturnValue;
import org.apache.rocketmq.console.service.OpsService;
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;
@Controller
@RequestMapping("/rocketmq")
public class NamesvrController {
@Resource
private OpsService opsService;
@RequestMapping(value = "/nsaddr", method = RequestMethod.GET)
@ResponseBody
@OriginalControllerReturnValue
public Object nsaddr() {
return opsService.getNameSvrList();
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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 org.apache.rocketmq.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.console.service.OpsService;
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;
@Controller
@RequestMapping("/ops")
public class OpsController {
@Resource
private OpsService opsService;
@RequestMapping(value = "/homePage.query", method = RequestMethod.GET)
@ResponseBody
public Object homePage() {
return opsService.homePageInfo();
}
@RequestMapping(value = "/updateNameSvrAddr.do", method = RequestMethod.POST)
@ResponseBody
public Object updateNameSvrAddr(@RequestParam String nameSvrAddrList) {
opsService.updateNameSvrAddrList(nameSvrAddrList);
return true;
}
@RequestMapping(value = "/updateIsVIPChannel.do", method = RequestMethod.POST)
@ResponseBody
public Object updateIsVIPChannel(@RequestParam String useVIPChannel) {
opsService.updateIsVIPChannel(useVIPChannel);
return true;
}
@RequestMapping(value = "/rocketMqStatus.query", method = RequestMethod.GET)
@ResponseBody
public Object clusterStatus() {
return opsService.rocketMqStatusCheck();
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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 org.apache.rocketmq.console.controller;
import javax.annotation.Resource;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.console.model.ConnectionInfo;
import org.apache.rocketmq.console.service.ProducerService;
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;
@Controller
@RequestMapping("/producer")
public class ProducerController {
@Resource
private ProducerService producerService;
@RequestMapping(value = "/producerConnection.query", method = {RequestMethod.GET})
@ResponseBody
public Object producerConnection(@RequestParam String producerGroup, @RequestParam String topic) {
ProducerConnection producerConnection = producerService.getProducerConnection(producerGroup, topic);
producerConnection.setConnectionSet(ConnectionInfo.buildConnectionInfoHashSet(producerConnection.getConnectionSet()));
return producerConnection;
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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 org.apache.rocketmq.console.controller;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.util.List;
import javax.annotation.Resource;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@Controller
@RequestMapping("/test")
public class TestController {
private Logger logger = LoggerFactory.getLogger(TestController.class);
private String testTopic = "TestTopic";
@Resource
private RMQConfigure rMQConfigure;
@RequestMapping(value = "/runTask.do", method = RequestMethod.GET)
@ResponseBody
public Object list() throws MQClientException, RemotingException, InterruptedException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(testTopic + "Group");
consumer.setNamesrvAddr(rMQConfigure.getNamesrvAddr());
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe(testTopic, "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
logger.info("receiveMessage msgSize={}", msgs.size());
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
final DefaultMQProducer producer = new DefaultMQProducer(testTopic + "Group");
producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
producer.setNamesrvAddr(rMQConfigure.getNamesrvAddr());
producer.start();
new Thread(new Runnable() {
@Override public void run() {
int i = 0;
while (true) {
try {
Message msg = new Message(testTopic,
"TagA" + i,
"KEYS" + i,
("Hello RocketMQ " + i).getBytes()
);
Thread.sleep(1000L);
SendResult sendResult = producer.send(msg);
logger.info("sendMessage={}", JsonUtil.obj2String(sendResult));
}
catch (Exception e) {
e.printStackTrace();
try {
Thread.sleep(1000);
}
catch (Exception ignore) {
}
}
}
}
}).start();
return true;
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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 org.apache.rocketmq.console.controller;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
import org.apache.rocketmq.console.service.ConsumerService;
import org.apache.rocketmq.console.service.TopicService;
import org.apache.rocketmq.console.util.JsonUtil;
import com.google.common.base.Preconditions;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/topic")
public class TopicController {
private Logger logger = LoggerFactory.getLogger(TopicController.class);
@Resource
private TopicService topicService;
@Resource
private ConsumerService consumerService;
@RequestMapping(value = "/list.query", method = RequestMethod.GET)
@ResponseBody
public Object list() throws MQClientException, RemotingException, InterruptedException {
return topicService.fetchAllTopicList();
}
@RequestMapping(value = "/stats.query", method = RequestMethod.GET)
@ResponseBody
public Object stats(@RequestParam String topic) {
return topicService.stats(topic);
}
@RequestMapping(value = "/route.query", method = RequestMethod.GET)
@ResponseBody
public Object route(@RequestParam String topic) {
return topicService.route(topic);
}
@RequestMapping(value = "/createOrUpdate.do", method = { RequestMethod.POST})
@ResponseBody
public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) {
Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()),
"clusterName or brokerName can not be all blank");
logger.info("op=look topicCreateOrUpdateRequest={}", JsonUtil.obj2String(topicCreateOrUpdateRequest));
topicService.createOrUpdate(topicCreateOrUpdateRequest);
return true;
}
@RequestMapping(value = "/queryConsumerByTopic.query")
@ResponseBody
public Object queryConsumerByTopic(@RequestParam String topic) {
return consumerService.queryConsumeStatsListByTopicName(topic);
}
@RequestMapping(value = "/queryTopicConsumerInfo.query")
@ResponseBody
public Object queryTopicConsumerInfo(@RequestParam String topic) {
return topicService.queryTopicConsumerInfo(topic);
}
@RequestMapping(value = "/examineTopicConfig.query")
@ResponseBody
public Object examineTopicConfig(@RequestParam String topic,
@RequestParam(required = false) String brokerName) throws RemotingException, MQClientException, InterruptedException {
return topicService.examineTopicConfig(topic);
}
@RequestMapping(value = "/sendTopicMessage.do", method = {RequestMethod.POST})
@ResponseBody
public Object sendTopicMessage(
@RequestBody SendTopicMessageRequest sendTopicMessageRequest) throws RemotingException, MQClientException, InterruptedException {
return topicService.sendTopicMessageRequest(sendTopicMessageRequest);
}
@RequestMapping(value = "/deleteTopic.do", method = {RequestMethod.POST})
@ResponseBody
public Object delete(@RequestParam(required = false) String clusterName, @RequestParam String topic) {
return topicService.deleteTopic(topic, clusterName);
}
@RequestMapping(value = "/deleteTopicByBroker.do", method = {RequestMethod.POST})
@ResponseBody
public Object deleteTopicByBroker(@RequestParam String brokerName, @RequestParam String topic) {
return topicService.deleteTopicInBroker(brokerName, topic);
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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 org.apache.rocketmq.console.exception;
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 9213584003139969215L;
private int code;
public ServiceException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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 org.apache.rocketmq.console.model;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.HashSet;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.protocol.body.Connection;
public class ConnectionInfo extends Connection {
private String versionDesc;
public static ConnectionInfo buildConnectionInfo(Connection connection) {
ConnectionInfo connectionInfo = new ConnectionInfo();
connectionInfo.setClientId(connection.getClientId());
connectionInfo.setClientAddr(connection.getClientAddr());
connectionInfo.setLanguage(connection.getLanguage());
connectionInfo.setVersion(connection.getVersion());
connectionInfo.setVersionDesc(MQVersion.getVersionDesc(connection.getVersion()));
return connectionInfo;
}
public static HashSet<Connection> buildConnectionInfoHashSet(Collection<Connection> connectionList) {
HashSet<Connection> connectionHashSet = Sets.newHashSet();
for (Connection connection : connectionList) {
connectionHashSet.add(buildConnectionInfo(connection));
}
return connectionHashSet;
}
public String getVersionDesc() {
return versionDesc;
}
public void setVersionDesc(String versionDesc) {
this.versionDesc = versionDesc;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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 org.apache.rocketmq.console.model;
import org.apache.rocketmq.common.admin.RollbackStats;
import com.google.common.collect.Lists;
import java.util.List;
public class ConsumerGroupRollBackStat {
private boolean status;
private String errMsg;
private List<RollbackStats> rollbackStatsList = Lists.newArrayList();
public ConsumerGroupRollBackStat(boolean status) {
this.status = status;
}
public ConsumerGroupRollBackStat(boolean status, String errMsg) {
this.status = status;
this.errMsg = errMsg;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public List<RollbackStats> getRollbackStatsList() {
return rollbackStatsList;
}
public void setRollbackStatsList(List<RollbackStats> rollbackStatsList) {
this.rollbackStatsList = rollbackStatsList;
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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 org.apache.rocketmq.console.model;
public class ConsumerMonitorConfig {
private int minCount;
private int maxDiffTotal;
public ConsumerMonitorConfig() {
}
public ConsumerMonitorConfig(int minCount, int maxDiffTotal) {
this.minCount = minCount;
this.maxDiffTotal = maxDiffTotal;
}
public int getMinCount() {
return minCount;
}
public void setMinCount(int minCount) {
this.minCount = minCount;
}
public int getMaxDiffTotal() {
return maxDiffTotal;
}
public void setMaxDiffTotal(int maxDiffTotal) {
this.maxDiffTotal = maxDiffTotal;
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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 org.apache.rocketmq.console.model;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
public class GroupConsumeInfo implements Comparable<GroupConsumeInfo> {
private String group;
private String version;
private int count;
private ConsumeType consumeType;
private MessageModel messageModel;
private int consumeTps;
private long diffTotal = -1;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public ConsumeType getConsumeType() {
return consumeType;
}
public void setConsumeType(ConsumeType consumeType) {
this.consumeType = consumeType;
}
public MessageModel getMessageModel() {
return messageModel;
}
public void setMessageModel(MessageModel messageModel) {
this.messageModel = messageModel;
}
public long getDiffTotal() {
return diffTotal;
}
public void setDiffTotal(long diffTotal) {
this.diffTotal = diffTotal;
}
@Override
public int compareTo(GroupConsumeInfo o) {
if (this.count != o.count) {
return o.count - this.count;
}
return (int) (o.diffTotal - diffTotal);
}
public int getConsumeTps() {
return consumeTps;
}
public void setConsumeTps(int consumeTps) {
this.consumeTps = consumeTps;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}

View File

@@ -0,0 +1,196 @@
/*
* 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 org.apache.rocketmq.console.model;
import org.apache.rocketmq.common.message.MessageExt;
import com.google.common.base.Charsets;
import org.springframework.beans.BeanUtils;
import java.net.SocketAddress;
import java.util.Map;
public class MessageView {
/** from MessageExt **/
private int queueId;
private int storeSize;
private long queueOffset;
private int sysFlag;
private long bornTimestamp;
private SocketAddress bornHost;
private long storeTimestamp;
private SocketAddress storeHost;
private String msgId;
private long commitLogOffset;
private int bodyCRC;
private int reconsumeTimes;
private long preparedTransactionOffset;
/**from MessageExt**/
/** from Message **/
private String topic;
private int flag;
private Map<String, String> properties;
private String messageBody; // body
/** from Message **/
public static MessageView fromMessageExt(MessageExt messageExt) {
MessageView messageView = new MessageView();
BeanUtils.copyProperties(messageExt, messageView);
if (messageExt.getBody() != null) {
messageView.setMessageBody(new String(messageExt.getBody(), Charsets.UTF_8));
}
return messageView;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public int getQueueId() {
return queueId;
}
public void setQueueId(int queueId) {
this.queueId = queueId;
}
public int getStoreSize() {
return storeSize;
}
public void setStoreSize(int storeSize) {
this.storeSize = storeSize;
}
public long getQueueOffset() {
return queueOffset;
}
public void setQueueOffset(long queueOffset) {
this.queueOffset = queueOffset;
}
public int getSysFlag() {
return sysFlag;
}
public void setSysFlag(int sysFlag) {
this.sysFlag = sysFlag;
}
public long getBornTimestamp() {
return bornTimestamp;
}
public void setBornTimestamp(long bornTimestamp) {
this.bornTimestamp = bornTimestamp;
}
public SocketAddress getBornHost() {
return bornHost;
}
public void setBornHost(SocketAddress bornHost) {
this.bornHost = bornHost;
}
public long getStoreTimestamp() {
return storeTimestamp;
}
public void setStoreTimestamp(long storeTimestamp) {
this.storeTimestamp = storeTimestamp;
}
public SocketAddress getStoreHost() {
return storeHost;
}
public void setStoreHost(SocketAddress storeHost) {
this.storeHost = storeHost;
}
public String getMsgId() {
return msgId;
}
public void setMsgId(String msgId) {
this.msgId = msgId;
}
public long getCommitLogOffset() {
return commitLogOffset;
}
public void setCommitLogOffset(long commitLogOffset) {
this.commitLogOffset = commitLogOffset;
}
public int getBodyCRC() {
return bodyCRC;
}
public void setBodyCRC(int bodyCRC) {
this.bodyCRC = bodyCRC;
}
public int getReconsumeTimes() {
return reconsumeTimes;
}
public void setReconsumeTimes(int reconsumeTimes) {
this.reconsumeTimes = reconsumeTimes;
}
public long getPreparedTransactionOffset() {
return preparedTransactionOffset;
}
public void setPreparedTransactionOffset(long preparedTransactionOffset) {
this.preparedTransactionOffset = preparedTransactionOffset;
}
public String getMessageBody() {
return messageBody;
}
public void setMessageBody(String messageBody) {
this.messageBody = messageBody;
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 org.apache.rocketmq.console.model;
import org.apache.rocketmq.common.admin.OffsetWrapper;
import org.apache.rocketmq.common.message.MessageQueue;
import org.springframework.beans.BeanUtils;
public class QueueStatInfo {
private String brokerName;
private int queueId;
private long brokerOffset;
private long consumerOffset;
private String clientInfo;
private long lastTimestamp;
public static QueueStatInfo fromOffsetTableEntry(MessageQueue key, OffsetWrapper value) {
QueueStatInfo queueStatInfo = new QueueStatInfo();
BeanUtils.copyProperties(key, queueStatInfo);
BeanUtils.copyProperties(value, queueStatInfo);
return queueStatInfo;
}
public String getClientInfo() {
return clientInfo;
}
public void setClientInfo(String clientInfo) {
this.clientInfo = clientInfo;
}
public String getBrokerName() {
return brokerName;
}
public void setBrokerName(String brokerName) {
this.brokerName = brokerName;
}
public int getQueueId() {
return queueId;
}
public void setQueueId(int queueId) {
this.queueId = queueId;
}
public long getBrokerOffset() {
return brokerOffset;
}
public void setBrokerOffset(long brokerOffset) {
this.brokerOffset = brokerOffset;
}
public long getConsumerOffset() {
return consumerOffset;
}
public void setConsumerOffset(long consumerOffset) {
this.consumerOffset = consumerOffset;
}
public long getLastTimestamp() {
return lastTimestamp;
}
public void setLastTimestamp(long lastTimestamp) {
this.lastTimestamp = lastTimestamp;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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 org.apache.rocketmq.console.model;
import com.google.common.collect.Lists;
import java.util.List;
public class TopicConsumerInfo {
private String topic;
private long diffTotal;
private long lastTimestamp;
private List<QueueStatInfo> queueStatInfoList = Lists.newArrayList();
public TopicConsumerInfo(String topic) {
this.topic = topic;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public long getDiffTotal() {
return diffTotal;
}
public void setDiffTotal(long diffTotal) {
this.diffTotal = diffTotal;
}
public List<QueueStatInfo> getQueueStatInfoList() {
return queueStatInfoList;
}
public long getLastTimestamp() {
return lastTimestamp;
}
public void appendQueueStatInfo(QueueStatInfo queueStatInfo) {
queueStatInfoList.add(queueStatInfo);
diffTotal = diffTotal + (queueStatInfo.getBrokerOffset() - queueStatInfo.getConsumerOffset());
lastTimestamp = Math.max(lastTimestamp, queueStatInfo.getLastTimestamp());
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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 org.apache.rocketmq.console.model.request;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import java.util.List;
public class ConsumerConfigInfo {
private List<String> clusterNameList;
private List<String> brokerNameList;
private SubscriptionGroupConfig subscriptionGroupConfig;
public ConsumerConfigInfo() {
}
public ConsumerConfigInfo(List<String> brokerNameList, SubscriptionGroupConfig subscriptionGroupConfig) {
this.brokerNameList = brokerNameList;
this.subscriptionGroupConfig = subscriptionGroupConfig;
}
public List<String> getClusterNameList() {
return clusterNameList;
}
public void setClusterNameList(List<String> clusterNameList) {
this.clusterNameList = clusterNameList;
}
public List<String> getBrokerNameList() {
return brokerNameList;
}
public void setBrokerNameList(List<String> brokerNameList) {
this.brokerNameList = brokerNameList;
}
public SubscriptionGroupConfig getSubscriptionGroupConfig() {
return subscriptionGroupConfig;
}
public void setSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
this.subscriptionGroupConfig = subscriptionGroupConfig;
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 org.apache.rocketmq.console.model.request;
import java.util.List;
public class DeleteSubGroupRequest {
private String groupName;
private List<String> brokerNameList;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public List<String> getBrokerNameList() {
return brokerNameList;
}
public void setBrokerNameList(List<String> brokerNameList) {
this.brokerNameList = brokerNameList;
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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 org.apache.rocketmq.console.model.request;
import java.util.List;
public class ResetOffsetRequest {
private List<String> consumerGroupList;
private String topic;
private long resetTime;
private boolean force;
public List<String> getConsumerGroupList() {
return consumerGroupList;
}
public void setConsumerGroupList(List<String> consumerGroupList) {
this.consumerGroupList = consumerGroupList;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public long getResetTime() {
return resetTime;
}
public void setResetTime(long resetTime) {
this.resetTime = resetTime;
}
public boolean isForce() {
return force;
}
public void setForce(boolean force) {
this.force = force;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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 org.apache.rocketmq.console.model.request;
public class SendTopicMessageRequest {
private String topic;
private String key;
private String tag;
private String messageBody;
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getMessageBody() {
return messageBody;
}
public void setMessageBody(String messageBody) {
this.messageBody = messageBody;
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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 org.apache.rocketmq.console.model.request;
import com.google.common.base.Objects;
import java.util.List;
public class TopicConfigInfo {
private List<String> clusterNameList;
private List<String> brokerNameList;
/** topicConfig */
private String topicName;
private int writeQueueNums;
private int readQueueNums;
private int perm;
private boolean order;
public List<String> getClusterNameList() {
return clusterNameList;
}
public void setClusterNameList(List<String> clusterNameList) {
this.clusterNameList = clusterNameList;
}
/** topicConfig */
public List<String> getBrokerNameList() {
return brokerNameList;
}
public void setBrokerNameList(List<String> brokerNameList) {
this.brokerNameList = brokerNameList;
}
public String getTopicName() {
return topicName;
}
public void setTopicName(String topicName) {
this.topicName = topicName;
}
public int getWriteQueueNums() {
return writeQueueNums;
}
public void setWriteQueueNums(int writeQueueNums) {
this.writeQueueNums = writeQueueNums;
}
public int getReadQueueNums() {
return readQueueNums;
}
public void setReadQueueNums(int readQueueNums) {
this.readQueueNums = readQueueNums;
}
public int getPerm() {
return perm;
}
public void setPerm(int perm) {
this.perm = perm;
}
public boolean isOrder() {
return order;
}
public void setOrder(boolean order) {
this.order = order;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
TopicConfigInfo that = (TopicConfigInfo) o;
return writeQueueNums == that.writeQueueNums &&
readQueueNums == that.readQueueNums &&
perm == that.perm &&
order == that.order &&
Objects.equal(topicName, that.topicName);
}
@Override
public int hashCode() {
return Objects.hashCode(topicName, writeQueueNums, readQueueNums, perm, order);
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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 org.apache.rocketmq.console.service;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import com.google.common.base.Throwables;
import com.google.common.collect.Sets;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
public abstract class AbstractCommonService {
@Resource
protected MQAdminExt mqAdminExt;
protected final Set<String> changeToBrokerNameSet(HashMap<String, Set<String>> clusterAddrTable,
List<String> clusterNameList, List<String> brokerNameList) {
Set<String> finalBrokerNameList = Sets.newHashSet();
if (CollectionUtils.isNotEmpty(clusterNameList)) {
try {
for (String clusterName : clusterNameList) {
finalBrokerNameList.addAll(clusterAddrTable.get(clusterName));
}
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
if (CollectionUtils.isNotEmpty(brokerNameList)) {
finalBrokerNameList.addAll(brokerNameList);
}
return finalBrokerNameList;
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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 org.apache.rocketmq.console.service;
import java.util.Map;
import java.util.Properties;
public interface ClusterService {
Map<String, Object> list();
Properties getBrokerConfig(String brokerAddr);
}

View File

@@ -0,0 +1,58 @@
/*
* 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 org.apache.rocketmq.console.service;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.console.model.ConsumerGroupRollBackStat;
import org.apache.rocketmq.console.model.GroupConsumeInfo;
import org.apache.rocketmq.console.model.TopicConsumerInfo;
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface ConsumerService {
List<GroupConsumeInfo> queryGroupList();
GroupConsumeInfo queryGroup(String consumerGroup);
List<TopicConsumerInfo> queryConsumeStatsListByGroupName(String groupName);
List<TopicConsumerInfo> queryConsumeStatsList(String topic, String groupName);
Map<String, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic);
Map<String /*consumerGroup*/, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest);
List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group);
boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest);
boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo);
Set<String> fetchBrokerNameSetBySubscriptionGroup(String group);
ConsumerConnection getConsumerConnection(String consumerGroup);
ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack);
}

View File

@@ -0,0 +1,36 @@
/*
* 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 org.apache.rocketmq.console.service;
import com.google.common.cache.LoadingCache;
import java.io.File;
import java.util.List;
import java.util.Map;
public interface DashboardCollectService {
// todo just move the task to org.apache.rocketmq.console.task.DashboardCollectTask
// the code can be reconstruct
LoadingCache<String, List<String>> getBrokerMap();
LoadingCache<String, List<String>> getTopicMap();
Map<String, List<String>> jsonDataFile2map(File file);
Map<String, List<String>> getBrokerCache(String date);
Map<String, List<String>> getTopicCache(String date);
}

View File

@@ -0,0 +1,42 @@
/*
* 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 org.apache.rocketmq.console.service;
import java.util.List;
import java.util.Map;
public interface DashboardService {
/**
* @param date format yyyy-MM-dd
*/
Map<String, List<String>> queryBrokerData(String date);
/**
* @param date format yyyy-MM-dd
*/
Map<String, List<String>> queryTopicData(String date);
/**
* @param date format yyyy-MM-dd
* @param topicName
*/
List<String> queryTopicData(String date, String topicName);
List<String> queryTopicCurrentData();
}

View File

@@ -0,0 +1,51 @@
/*
* 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 org.apache.rocketmq.console.service;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.apache.rocketmq.console.model.MessageView;
import java.util.List;
public interface MessageService {
/**
* @param subject
* @param msgId
*/
Pair<MessageView, List<MessageTrack>> viewMessage(String subject, final String msgId);
List<MessageView> queryMessageByTopicAndKey(final String topic, final String key);
/**
* @param topic
* @param begin
* @param end
* org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
*/
List<MessageView> queryMessageByTopic(final String topic, final long begin,
final long end);
List<MessageTrack> messageTrackDetail(MessageExt msg);
ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup,
String clientId);
}

View File

@@ -0,0 +1,30 @@
/*
* 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 org.apache.rocketmq.console.service;
import java.util.Map;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
public interface MonitorService {
boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config);
Map<String, ConsumerMonitorConfig> queryConsumerMonitorConfig();
ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName);
boolean deleteConsumerMonitor(String consumeGroupName);
}

View File

@@ -0,0 +1,32 @@
/*
* 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 org.apache.rocketmq.console.service;
import java.util.Map;
import org.apache.rocketmq.console.service.checker.CheckerType;
public interface OpsService {
Map<String, Object> homePageInfo();
void updateNameSvrAddrList(String nameSvrAddrList);
String getNameSvrList();
Map<CheckerType,Object> rocketMqStatusCheck();
boolean updateIsVIPChannel(String useVIPChannel);
}

View File

@@ -0,0 +1,24 @@
/*
* 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 org.apache.rocketmq.console.service;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
public interface ProducerService {
ProducerConnection getProducerConnection(String producerGroup, String topic);
}

View File

@@ -0,0 +1,54 @@
/*
* 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 org.apache.rocketmq.console.service;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.admin.TopicStatsTable;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.TopicList;
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
import java.util.List;
public interface TopicService {
TopicList fetchAllTopicList();
TopicStatsTable stats(String topic);
TopicRouteData route(String topic);
GroupList queryTopicConsumerInfo(String topic);
void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest);
TopicConfig examineTopicConfig(String topic, String brokerName);
List<TopicConfigInfo> examineTopicConfig(String topic);
boolean deleteTopic(String topic, String clusterName);
boolean deleteTopic(String topic);
boolean deleteTopicInBroker(String brokerName, String topic);
SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest);
}

View File

@@ -0,0 +1,23 @@
/*
* 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 org.apache.rocketmq.console.service.checker;
public enum CheckerType {
CLUSTER_HEALTH_CHECK,
TOPIC_ONLY_ONE_BROKER_CHECK
}

View File

@@ -0,0 +1,24 @@
/*
* 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 org.apache.rocketmq.console.service.checker;
public interface RocketMqChecker {
public Object doCheck();
public CheckerType checkerType();
}

View File

@@ -0,0 +1,34 @@
/*
* 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 org.apache.rocketmq.console.service.checker.impl;
import org.apache.rocketmq.console.service.checker.CheckerType;
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
import org.springframework.stereotype.Service;
@Service
public class ClusterHealthCheckerImpl implements RocketMqChecker {
@Override
public Object doCheck() {
return null;
}
@Override
public CheckerType checkerType() {
return CheckerType.CLUSTER_HEALTH_CHECK;
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 org.apache.rocketmq.console.service.checker.impl;
import org.apache.rocketmq.console.service.checker.CheckerType;
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
import org.springframework.stereotype.Service;
@Service
public class TopicOnlyOneBrokerCheckerImpl implements RocketMqChecker {
@Override
public Object doCheck() {
return null;
}
@Override
public CheckerType checkerType() {
return CheckerType.TOPIC_ONLY_ONE_BROKER_CHECK;
}
}

View File

@@ -0,0 +1,501 @@
/*
* 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 org.apache.rocketmq.console.service.client;
import com.google.common.base.Throwables;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.rocketmq.client.QueryResult;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.impl.MQAdminImpl;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.admin.ConsumeStats;
import org.apache.rocketmq.common.admin.RollbackStats;
import org.apache.rocketmq.common.admin.TopicStatsTable;
import org.apache.rocketmq.common.message.MessageClientIDSetter;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.common.protocol.body.ConsumeStatsList;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
import org.apache.rocketmq.common.protocol.body.TopicList;
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.console.util.JsonUtil;
import org.apache.rocketmq.remoting.RemotingClient;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.joor.Reflect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode;
@Service
public class MQAdminExtImpl implements MQAdminExt {
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
public MQAdminExtImpl() {
}
@Override
public void updateBrokerConfig(String brokerAddr, Properties properties)
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
UnsupportedEncodingException, InterruptedException, MQBrokerException {
MQAdminInstance.threadLocalMQAdminExt().updateBrokerConfig(brokerAddr, properties);
}
@Override
public void createAndUpdateTopicConfig(String addr, TopicConfig config)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
}
@Override
public void createAndUpdateSubscriptionGroupConfig(String addr, SubscriptionGroupConfig config)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateSubscriptionGroupConfig(addr, config);
}
@Override
public SubscriptionGroupConfig examineSubscriptionGroupConfig(String addr, String group) {
RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_SUBSCRIPTIONGROUP_CONFIG, null);
RemotingCommand response = null;
try {
response = remotingClient.invokeSync(addr, request, 3000);
}
catch (Exception err) {
throw Throwables.propagate(err);
}
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
SubscriptionGroupWrapper subscriptionGroupWrapper = decode(response.getBody(), SubscriptionGroupWrapper.class);
return subscriptionGroupWrapper.getSubscriptionGroupTable().get(group);
}
default:
throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark()));
}
}
@Override
public TopicConfig examineTopicConfig(String addr, String topic) {
RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_TOPIC_CONFIG, null);
RemotingCommand response = null;
try {
response = remotingClient.invokeSync(addr, request, 3000);
}
catch (Exception err) {
throw Throwables.propagate(err);
}
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
TopicConfigSerializeWrapper topicConfigSerializeWrapper = decode(response.getBody(), TopicConfigSerializeWrapper.class);
return topicConfigSerializeWrapper.getTopicConfigTable().get(topic);
}
default:
throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark()));
}
}
@Override
public TopicStatsTable examineTopicStats(String topic)
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().examineTopicStats(topic);
}
@Override
public TopicList fetchAllTopicList() throws RemotingException, MQClientException, InterruptedException {
TopicList topicList = MQAdminInstance.threadLocalMQAdminExt().fetchAllTopicList();
logger.debug("op=look={}", JsonUtil.obj2String(topicList.getTopicList()));
return topicList;
}
@Override
public KVTable fetchBrokerRuntimeStats(String brokerAddr)
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().fetchBrokerRuntimeStats(brokerAddr);
}
@Override
public ConsumeStats examineConsumeStats(String consumerGroup)
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().examineConsumeStats(consumerGroup);
}
@Override
public ConsumeStats examineConsumeStats(String consumerGroup, String topic)
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().examineConsumeStats(consumerGroup, topic);
}
@Override
public ClusterInfo examineBrokerClusterInfo()
throws InterruptedException, MQBrokerException, RemotingTimeoutException, RemotingSendRequestException,
RemotingConnectException {
return MQAdminInstance.threadLocalMQAdminExt().examineBrokerClusterInfo();
}
@Override
public TopicRouteData examineTopicRouteInfo(String topic)
throws RemotingException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().examineTopicRouteInfo(topic);
}
@Override
public ConsumerConnection examineConsumerConnectionInfo(String consumerGroup)
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
InterruptedException, MQBrokerException, RemotingException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().examineConsumerConnectionInfo(consumerGroup);
}
@Override
public ProducerConnection examineProducerConnectionInfo(String producerGroup, String topic)
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().examineProducerConnectionInfo(producerGroup, topic);
}
@Override
public List<String> getNameServerAddressList() {
return MQAdminInstance.threadLocalMQAdminExt().getNameServerAddressList();
}
@Override
public int wipeWritePermOfBroker(String namesrvAddr, String brokerName)
throws RemotingCommandException, RemotingConnectException, RemotingSendRequestException,
RemotingTimeoutException, InterruptedException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().wipeWritePermOfBroker(namesrvAddr, brokerName);
}
@Override
public void putKVConfig(String namespace, String key, String value) {
MQAdminInstance.threadLocalMQAdminExt().putKVConfig(namespace, key, value);
}
@Override
public String getKVConfig(String namespace, String key)
throws RemotingException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().getKVConfig(namespace, key);
}
@Override
public KVTable getKVListByNamespace(String namespace)
throws RemotingException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().getKVListByNamespace(namespace);
}
@Override
public void deleteTopicInBroker(Set<String> addrs, String topic)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
logger.info("addrs={} topic={}", JsonUtil.obj2String(addrs), topic);
MQAdminInstance.threadLocalMQAdminExt().deleteTopicInBroker(addrs, topic);
}
@Override
public void deleteTopicInNameServer(Set<String> addrs, String topic)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().deleteTopicInNameServer(addrs, topic);
}
@Override
public void deleteSubscriptionGroup(String addr, String groupName)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().deleteSubscriptionGroup(addr, groupName);
}
@Override
public void createAndUpdateKvConfig(String namespace, String key, String value)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateKvConfig(namespace, key, value);
}
@Override
public void deleteKvConfig(String namespace, String key)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().deleteKvConfig(namespace, key);
}
@Override
public List<RollbackStats> resetOffsetByTimestampOld(String consumerGroup, String topic, long timestamp,
boolean force) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().resetOffsetByTimestampOld(consumerGroup, topic, timestamp, force);
}
@Override
public Map<MessageQueue, Long> resetOffsetByTimestamp(String topic, String group, long timestamp,
boolean isForce) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().resetOffsetByTimestamp(topic, group, timestamp, isForce);
}
@Override
public void resetOffsetNew(String consumerGroup, String topic, long timestamp)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().resetOffsetNew(consumerGroup, topic, timestamp);
}
@Override
public Map<String, Map<MessageQueue, Long>> getConsumeStatus(String topic, String group,
String clientAddr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().getConsumeStatus(topic, group, clientAddr);
}
@Override
public void createOrUpdateOrderConf(String key, String value, boolean isCluster)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createOrUpdateOrderConf(key, value, isCluster);
}
@Override
public GroupList queryTopicConsumeByWho(String topic)
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
InterruptedException, MQBrokerException, RemotingException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().queryTopicConsumeByWho(topic);
}
@Override
public boolean cleanExpiredConsumerQueue(String cluster)
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException,
InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().cleanExpiredConsumerQueue(cluster);
}
@Override
public boolean cleanExpiredConsumerQueueByAddr(String addr)
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException,
InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().cleanExpiredConsumerQueueByAddr(addr);
}
@Override
public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack)
throws RemotingException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().getConsumerRunningInfo(consumerGroup, clientId, jstack);
}
@Override
public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId,
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().consumeMessageDirectly(consumerGroup, clientId, msgId);
}
@Override
public List<MessageTrack> messageTrackDetail(MessageExt msg)
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().messageTrackDetail(msg);
}
@Override
public void cloneGroupOffset(String srcGroup, String destGroup, String topic, boolean isOffline)
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
MQAdminInstance.threadLocalMQAdminExt().cloneGroupOffset(srcGroup, destGroup, topic, isOffline);
}
@Override
public void createTopic(String key, String newTopic, int queueNum) throws MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createTopic(key, newTopic, queueNum);
}
@Override
public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag)
throws MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createTopic(key, newTopic, queueNum, topicSysFlag);
}
@Override
public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().searchOffset(mq, timestamp);
}
@Override
public long maxOffset(MessageQueue mq) throws MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().maxOffset(mq);
}
@Override
public long minOffset(MessageQueue mq) throws MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().minOffset(mq);
}
@Override
public long earliestMsgStoreTime(MessageQueue mq) throws MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().earliestMsgStoreTime(mq);
}
@Override
public MessageExt viewMessage(String msgId)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().viewMessage(msgId);
}
@Override
public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end)
throws MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().queryMessage(topic, key, maxNum, begin, end);
}
@Override
@Deprecated
public void start() throws MQClientException {
throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
}
@Override
@Deprecated
public void shutdown() {
throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
}
// below is 3.2.6->3.5.8 updated
@Override
public List<QueueTimeSpan> queryConsumeTimeSpan(String topic,
String group) throws InterruptedException, MQBrokerException, RemotingException, MQClientException {
return MQAdminInstance.threadLocalMQAdminExt().queryConsumeTimeSpan(topic, group);
}
//MessageClientIDSetter.getNearlyTimeFromID has bug,so we subtract half a day
//next version we will remove it
//https://issues.apache.org/jira/browse/ROCKETMQ-111
//https://github.com/apache/incubator-rocketmq/pull/69
@Override
public MessageExt viewMessage(String topic,
String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
logger.info("MessageClientIDSetter.getNearlyTimeFromID(msgId)={} msgId={}", MessageClientIDSetter.getNearlyTimeFromID(msgId), msgId);
try {
return viewMessage(msgId);
}
catch (Exception e) {
}
MQAdminImpl mqAdminImpl = MQAdminInstance.threadLocalMqClientInstance().getMQAdminImpl();
QueryResult qr = Reflect.on(mqAdminImpl).call("queryMessage", topic, msgId, 32,
MessageClientIDSetter.getNearlyTimeFromID(msgId).getTime() - 1000 * 60 * 60 * 13L, Long.MAX_VALUE, true).get();
if (qr != null && qr.getMessageList() != null && qr.getMessageList().size() > 0) {
return qr.getMessageList().get(0);
}
else {
return null;
}
}
@Override
public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId, String topic,
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().consumeMessageDirectly(consumerGroup, clientId, topic, msgId);
}
@Override
public Properties getBrokerConfig(
String brokerAddr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, UnsupportedEncodingException, InterruptedException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().getBrokerConfig(brokerAddr);
}
@Override
public TopicList fetchTopicsByCLuster(
String clusterName) throws RemotingException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().fetchTopicsByCLuster(clusterName);
}
@Override
public boolean cleanUnusedTopic(
String cluster) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().cleanUnusedTopic(cluster);
}
@Override
public boolean cleanUnusedTopicByAddr(
String addr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().cleanUnusedTopicByAddr(addr);
}
@Override
public BrokerStatsData viewBrokerStatsData(String brokerAddr, String statsName,
String statsKey) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().viewBrokerStatsData(brokerAddr, statsName, statsKey);
}
@Override
public Set<String> getClusterList(
String topic) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().getClusterList(topic);
}
@Override
public ConsumeStatsList fetchConsumeStatsInBroker(String brokerAddr, boolean isOrder,
long timeoutMillis) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
return MQAdminInstance.threadLocalMQAdminExt().fetchConsumeStatsInBroker(brokerAddr, isOrder, timeoutMillis);
}
@Override
public Set<String> getTopicClusterList(
String topic) throws InterruptedException, MQBrokerException, MQClientException, RemotingException {
return MQAdminInstance.threadLocalMQAdminExt().getTopicClusterList(topic);
}
@Override
public SubscriptionGroupWrapper getAllSubscriptionGroup(String brokerAddr,
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().getAllSubscriptionGroup(brokerAddr, timeoutMillis);
}
@Override
public TopicConfigSerializeWrapper getAllTopicGroup(String brokerAddr,
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
return MQAdminInstance.threadLocalMQAdminExt().getAllTopicGroup(brokerAddr, timeoutMillis);
}
@Override
public void updateConsumeOffset(String brokerAddr, String consumeGroup, MessageQueue mq,
long offset) throws RemotingException, InterruptedException, MQBrokerException {
MQAdminInstance.threadLocalMQAdminExt().updateConsumeOffset(brokerAddr, consumeGroup, mq, offset);
}
// 4.0.0 added
@Override public void updateNameServerConfig(Properties properties,
List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
}
@Override public Map<String, Properties> getNameServerConfig(
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
return null;
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 org.apache.rocketmq.console.service.client;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.impl.MQClientAPIImpl;
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
import org.apache.rocketmq.remoting.RemotingClient;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.joor.Reflect;
public class MQAdminInstance {
private static final ThreadLocal<DefaultMQAdminExt> MQ_ADMIN_EXT_THREAD_LOCAL = new ThreadLocal<DefaultMQAdminExt>();
private static final ThreadLocal<Integer> INIT_COUNTER = new ThreadLocal<Integer>();
public static MQAdminExt threadLocalMQAdminExt() {
DefaultMQAdminExt defaultMQAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
if (defaultMQAdminExt == null) {
throw new IllegalStateException("defaultMQAdminExt should be init before you get this");
}
return defaultMQAdminExt;
}
public static RemotingClient threadLocalRemotingClient() {
MQClientInstance mqClientInstance = threadLocalMqClientInstance();
MQClientAPIImpl mQClientAPIImpl = Reflect.on(mqClientInstance).get("mQClientAPIImpl");
return Reflect.on(mQClientAPIImpl).get("remotingClient");
}
public static MQClientInstance threadLocalMqClientInstance() {
DefaultMQAdminExtImpl defaultMQAdminExtImpl = Reflect.on(MQAdminInstance.threadLocalMQAdminExt()).get("defaultMQAdminExtImpl");
return Reflect.on(defaultMQAdminExtImpl).get("mqClientInstance");
}
public static void initMQAdminInstance(long timeoutMillis) throws MQClientException {
Integer nowCount = INIT_COUNTER.get();
if (nowCount == null) {
DefaultMQAdminExt defaultMQAdminExt;
if (timeoutMillis > 0) {
defaultMQAdminExt = new DefaultMQAdminExt(timeoutMillis);
}
else {
defaultMQAdminExt = new DefaultMQAdminExt();
}
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
defaultMQAdminExt.start();
MQ_ADMIN_EXT_THREAD_LOCAL.set(defaultMQAdminExt);
INIT_COUNTER.set(1);
}
else {
INIT_COUNTER.set(nowCount + 1);
}
}
public static void destroyMQAdminInstance() {
Integer nowCount = INIT_COUNTER.get() - 1;
if (nowCount > 0) {
INIT_COUNTER.set(nowCount);
return;
}
MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
if (mqAdminExt != null) {
mqAdminExt.shutdown();
MQ_ADMIN_EXT_THREAD_LOCAL.remove();
INIT_COUNTER.remove();
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.route.BrokerData;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.apache.rocketmq.console.service.ClusterService;
import org.apache.rocketmq.console.util.JsonUtil;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Properties;
@Service
public class ClusterServiceImpl implements ClusterService {
private Logger logger = LoggerFactory.getLogger(ClusterServiceImpl.class);
@Resource
private MQAdminExt mqAdminExt;
@Override
public Map<String, Object> list() {
try {
Map<String, Object> resultMap = Maps.newHashMap();
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
logger.info("op=look_clusterInfo {}", JsonUtil.obj2String(clusterInfo));
Map<String/*brokerName*/, Map<Long/* brokerId */, Object/* brokerDetail */>> brokerServer = Maps.newHashMap();
for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) {
Map<Long, Object> brokerMasterSlaveMap = Maps.newHashMap();
for (Map.Entry<Long/* brokerId */, String/* broker address */> brokerAddr : brokerData.getBrokerAddrs().entrySet()) {
KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats(brokerAddr.getValue());
// KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats("127.0.0.1:10911");
brokerMasterSlaveMap.put(brokerAddr.getKey(), kvTable.getTable());
}
brokerServer.put(brokerData.getBrokerName(), brokerMasterSlaveMap);
}
resultMap.put("clusterInfo", clusterInfo);
resultMap.put("brokerServer", brokerServer);
return resultMap;
}
catch (Exception err) {
throw Throwables.propagate(err);
}
}
@Override
public Properties getBrokerConfig(String brokerAddr) {
try {
return mqAdminExt.getBrokerConfig(brokerAddr);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
}

View File

@@ -0,0 +1,341 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.admin.ConsumeStats;
import org.apache.rocketmq.common.admin.RollbackStats;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.common.protocol.body.Connection;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.route.BrokerData;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
import org.apache.rocketmq.console.model.ConsumerGroupRollBackStat;
import org.apache.rocketmq.console.model.GroupConsumeInfo;
import org.apache.rocketmq.console.model.QueueStatInfo;
import org.apache.rocketmq.console.model.TopicConsumerInfo;
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
import org.apache.rocketmq.console.service.AbstractCommonService;
import org.apache.rocketmq.console.service.ConsumerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import static com.google.common.base.Throwables.propagate;
@Service
public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService {
private Logger logger = LoggerFactory.getLogger(ConsumerServiceImpl.class);
@Override
@MultiMQAdminCmdMethod
public List<GroupConsumeInfo> queryGroupList() {
Set<String> consumerGroupSet = Sets.newHashSet();
try {
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) {
SubscriptionGroupWrapper subscriptionGroupWrapper = mqAdminExt.getAllSubscriptionGroup(brokerData.selectBrokerAddr(), 3000L);
consumerGroupSet.addAll(subscriptionGroupWrapper.getSubscriptionGroupTable().keySet());
}
}
catch (Exception err) {
throw Throwables.propagate(err);
}
List<GroupConsumeInfo> groupConsumeInfoList = Lists.newArrayList();
for (String consumerGroup : consumerGroupSet) {
groupConsumeInfoList.add(queryGroup(consumerGroup));
}
Collections.sort(groupConsumeInfoList);
return groupConsumeInfoList;
}
@Override
@MultiMQAdminCmdMethod
public GroupConsumeInfo queryGroup(String consumerGroup) {
GroupConsumeInfo groupConsumeInfo = new GroupConsumeInfo();
try {
ConsumeStats consumeStats = null;
try {
consumeStats = mqAdminExt.examineConsumeStats(consumerGroup);
}
catch (Exception e) {
logger.warn("examineConsumeStats exception, " + consumerGroup, e);
}
ConsumerConnection consumerConnection = null;
try {
consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
}
catch (Exception e) {
logger.warn("examineConsumerConnectionInfo exception, " + consumerGroup, e);
}
groupConsumeInfo.setGroup(consumerGroup);
if (consumeStats != null) {
groupConsumeInfo.setConsumeTps((int)consumeStats.getConsumeTps());
groupConsumeInfo.setDiffTotal(consumeStats.computeTotalDiff());
}
if (consumerConnection != null) {
groupConsumeInfo.setCount(consumerConnection.getConnectionSet().size());
groupConsumeInfo.setMessageModel(consumerConnection.getMessageModel());
groupConsumeInfo.setConsumeType(consumerConnection.getConsumeType());
groupConsumeInfo.setVersion(MQVersion.getVersionDesc(consumerConnection.computeMinVersion()));
}
}
catch (Exception e) {
logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, "
+ consumerGroup, e);
}
return groupConsumeInfo;
}
@Override
public List<TopicConsumerInfo> queryConsumeStatsListByGroupName(String groupName) {
return queryConsumeStatsList(null, groupName);
}
@Override
@MultiMQAdminCmdMethod
public List<TopicConsumerInfo> queryConsumeStatsList(final String topic, String groupName) {
ConsumeStats consumeStats = null;
try {
consumeStats = mqAdminExt.examineConsumeStats(groupName, topic);
}
catch (Exception e) {
throw propagate(e);
}
List<MessageQueue> mqList = Lists.newArrayList(Iterables.filter(consumeStats.getOffsetTable().keySet(), new Predicate<MessageQueue>() {
@Override
public boolean apply(MessageQueue o) {
return StringUtils.isBlank(topic) || o.getTopic().equals(topic);
}
}));
Collections.sort(mqList);
List<TopicConsumerInfo> topicConsumerInfoList = Lists.newArrayList();
TopicConsumerInfo nowTopicConsumerInfo = null;
Map<MessageQueue, String> messageQueueClientMap = getClientConnection(groupName);
for (MessageQueue mq : mqList) {
if (nowTopicConsumerInfo == null || (!StringUtils.equals(mq.getTopic(), nowTopicConsumerInfo.getTopic()))) {
nowTopicConsumerInfo = new TopicConsumerInfo(mq.getTopic());
topicConsumerInfoList.add(nowTopicConsumerInfo);
}
QueueStatInfo queueStatInfo = QueueStatInfo.fromOffsetTableEntry(mq, consumeStats.getOffsetTable().get(mq));
queueStatInfo.setClientInfo(messageQueueClientMap.get(mq));
nowTopicConsumerInfo.appendQueueStatInfo(queueStatInfo);
}
return topicConsumerInfoList;
}
private Map<MessageQueue, String> getClientConnection(String groupName) {
Map<MessageQueue, String> results = Maps.newHashMap();
try {
ConsumerConnection consumerConnection = mqAdminExt.examineConsumerConnectionInfo(groupName);
for (Connection connection : consumerConnection.getConnectionSet()) {
String clinetId = connection.getClientId();
ConsumerRunningInfo consumerRunningInfo = mqAdminExt.getConsumerRunningInfo(groupName, clinetId, false);
for (MessageQueue messageQueue : consumerRunningInfo.getMqTable().keySet()) {
// results.put(messageQueue, clinetId + " " + connection.getClientAddr());
results.put(messageQueue, clinetId);
}
}
}
catch (Exception err) {
logger.error("op=getClientConnection_error", err);
}
return results;
}
@Override
@MultiMQAdminCmdMethod
public Map<String /*groupName*/, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic) {
Map<String, TopicConsumerInfo> group2ConsumerInfoMap = Maps.newHashMap();
try {
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
for (String group : groupList.getGroupList()) {
List<TopicConsumerInfo> topicConsumerInfoList = null;
try {
topicConsumerInfoList = queryConsumeStatsList(topic, group);
}
catch (Exception ignore) {
}
group2ConsumerInfoMap.put(group, CollectionUtils.isEmpty(topicConsumerInfoList) ? new TopicConsumerInfo(topic) : topicConsumerInfoList.get(0));
}
return group2ConsumerInfoMap;
}
catch (Exception e) {
throw propagate(e);
}
}
@Override
@MultiMQAdminCmdMethod
public Map<String, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest) {
Map<String, ConsumerGroupRollBackStat> groupRollbackStats = Maps.newHashMap();
for (String consumerGroup : resetOffsetRequest.getConsumerGroupList()) {
try {
Map<MessageQueue, Long> rollbackStatsMap =
mqAdminExt.resetOffsetByTimestamp(resetOffsetRequest.getTopic(), consumerGroup, resetOffsetRequest.getResetTime(), resetOffsetRequest.isForce());
ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true);
List<RollbackStats> rollbackStatsList = consumerGroupRollBackStat.getRollbackStatsList();
for (Map.Entry<MessageQueue, Long> rollbackStatsEntty : rollbackStatsMap.entrySet()) {
RollbackStats rollbackStats = new RollbackStats();
rollbackStats.setRollbackOffset(rollbackStatsEntty.getValue());
rollbackStats.setQueueId(rollbackStatsEntty.getKey().getQueueId());
rollbackStats.setBrokerName(rollbackStatsEntty.getKey().getBrokerName());
rollbackStatsList.add(rollbackStats);
}
groupRollbackStats.put(consumerGroup, consumerGroupRollBackStat);
}
catch (MQClientException e) {
if (ResponseCode.CONSUMER_NOT_ONLINE == e.getResponseCode()) {
try {
ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true);
List<RollbackStats> rollbackStatsList = mqAdminExt.resetOffsetByTimestampOld(consumerGroup, resetOffsetRequest.getTopic(), resetOffsetRequest.getResetTime(), true);
consumerGroupRollBackStat.setRollbackStatsList(rollbackStatsList);
groupRollbackStats.put(consumerGroup, consumerGroupRollBackStat);
continue;
}
catch (Exception err) {
logger.error("op=resetOffset_which_not_online_error", err);
}
}
else {
logger.error("op=resetOffset_error", e);
}
groupRollbackStats.put(consumerGroup, new ConsumerGroupRollBackStat(false, e.getMessage()));
}
catch (Exception e) {
logger.error("op=resetOffset_error", e);
groupRollbackStats.put(consumerGroup, new ConsumerGroupRollBackStat(false, e.getMessage()));
}
}
return groupRollbackStats;
}
@Override
@MultiMQAdminCmdMethod
public List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group) {
List<ConsumerConfigInfo> consumerConfigInfoList = Lists.newArrayList();
try {
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
for (String brokerName : clusterInfo.getBrokerAddrTable().keySet()) { //foreach brokerName
String brokerAddress = clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr();
SubscriptionGroupConfig subscriptionGroupConfig = mqAdminExt.examineSubscriptionGroupConfig(brokerAddress, group);
if (subscriptionGroupConfig == null) {
continue;
}
consumerConfigInfoList.add(new ConsumerConfigInfo(Lists.newArrayList(brokerName), subscriptionGroupConfig));
}
}
catch (Exception e) {
throw propagate(e);
}
return consumerConfigInfoList;
}
@Override
@MultiMQAdminCmdMethod
public boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest) {
try {
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
for (String brokerName : deleteSubGroupRequest.getBrokerNameList()) {
logger.info("addr={} groupName={}", clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
mqAdminExt.deleteSubscriptionGroup(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
}
}
catch (Exception e) {
throw propagate(e);
}
return true;
}
@Override
public boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo) {
try {
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
consumerConfigInfo.getClusterNameList(), consumerConfigInfo.getBrokerNameList())) {
mqAdminExt.createAndUpdateSubscriptionGroupConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), consumerConfigInfo.getSubscriptionGroupConfig());
}
}
catch (Exception err) {
throw Throwables.propagate(err);
}
return true;
}
@Override
@MultiMQAdminCmdMethod
public Set<String> fetchBrokerNameSetBySubscriptionGroup(String group) {
Set<String> brokerNameSet = Sets.newHashSet();
try {
List<ConsumerConfigInfo> consumerConfigInfoList = examineSubscriptionGroupConfig(group);
for (ConsumerConfigInfo consumerConfigInfo : consumerConfigInfoList) {
brokerNameSet.addAll(consumerConfigInfo.getBrokerNameList());
}
}
catch (Exception e) {
throw Throwables.propagate(e);
}
return brokerNameSet;
}
@Override
public ConsumerConnection getConsumerConnection(String consumerGroup) {
try {
return mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@Override
public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack) {
try {
return mqAdminExt.getConsumerRunningInfo(consumerGroup, clientId, jstack);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import com.google.common.base.Ticker;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.exception.ServiceException;
import org.apache.rocketmq.console.service.DashboardCollectService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class DashboardCollectServiceImpl implements DashboardCollectService {
@Resource
private RMQConfigure rmqConfigure;
private final static Logger log = LoggerFactory.getLogger(DashboardCollectServiceImpl.class);
private LoadingCache<String, List<String>> brokerMap = CacheBuilder.newBuilder()
.maximumSize(1000)
.concurrencyLevel(10)
.recordStats()
.ticker(Ticker.systemTicker())
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
log.debug(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
.build(
new CacheLoader<String, List<String>>() {
@Override
public List<String> load(String key) {
List<String> list = Lists.newArrayList();
return list;
}
}
);
private LoadingCache<String, List<String>> topicMap = CacheBuilder.newBuilder()
.maximumSize(1000)
.concurrencyLevel(10)
.recordStats()
.ticker(Ticker.systemTicker())
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
log.debug(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
.build(
new CacheLoader<String, List<String>>() {
@Override
public List<String> load(String key) {
List<String> list = Lists.newArrayList();
return list;
}
}
);
@Override
public LoadingCache<String, List<String>> getBrokerMap() {
return brokerMap;
}
@Override
public LoadingCache<String, List<String>> getTopicMap() {
return topicMap;
}
@Override
public Map<String, List<String>> jsonDataFile2map(File file) {
List<String> strings;
try {
strings = Files.readLines(file, Charsets.UTF_8);
}
catch (IOException e) {
throw Throwables.propagate(e);
}
StringBuffer sb = new StringBuffer();
for (String string : strings) {
sb.append(string);
}
JSONObject json = (JSONObject) JSONObject.parse(sb.toString());
Set<Map.Entry<String, Object>> entries = json.entrySet();
Map<String, List<String>> map = Maps.newHashMap();
for (Map.Entry<String, Object> entry : entries) {
JSONArray tpsArray = (JSONArray) entry.getValue();
if (tpsArray == null) {
continue;
}
Object[] tpsStrArray = tpsArray.toArray();
List<String> tpsList = Lists.newArrayList();
for (Object tpsObj : tpsStrArray) {
tpsList.add("" + tpsObj);
}
map.put(entry.getKey(), tpsList);
}
return map;
}
@Override
public Map<String, List<String>> getBrokerCache(String date) {
String dataLocationPath = rmqConfigure.getConsoleCollectData();
File file = new File(dataLocationPath + date + ".json");
if (!file.exists()) {
throw Throwables.propagate(new ServiceException(1, "This date have't data!"));
}
return jsonDataFile2map(file);
}
@Override
public Map<String, List<String>> getTopicCache(String date) {
String dataLocationPath = rmqConfigure.getConsoleCollectData();
File file = new File(dataLocationPath + date + "_topic" + ".json");
if (!file.exists()) {
throw Throwables.propagate(new ServiceException(1, "This date have't data!"));
}
return jsonDataFile2map(file);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.google.common.collect.Lists;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.rocketmq.console.service.DashboardCollectService;
import org.apache.rocketmq.console.service.DashboardService;
import org.springframework.stereotype.Service;
@Service
public class DashboardServiceImpl implements DashboardService {
@Resource
private DashboardCollectService dashboardCollectService;
/**
* @param date format yyyy-MM-dd
*/
@Override
public Map<String, List<String>> queryBrokerData(String date) {
return dashboardCollectService.getBrokerCache(date);
}
@Override
public Map<String, List<String>> queryTopicData(String date) {
return dashboardCollectService.getTopicCache(date);
}
/**
* @param date format yyyy-MM-dd
* @param topicName
*/
@Override
public List<String> queryTopicData(String date, String topicName) {
if (null != dashboardCollectService.getTopicCache(date)) {
return dashboardCollectService.getTopicCache(date).get(topicName);
}
return null;
}
@Override
public List<String> queryTopicCurrentData() {
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Map<String, List<String>> topicCache = dashboardCollectService.getTopicCache(format.format(date));
List<String> result = Lists.newArrayList();
for (Map.Entry<String, List<String>> entry : topicCache.entrySet()) {
List<String> value = entry.getValue();
result.add(entry.getKey() + "," + value.get(value.size() - 1).split(",")[4]);
}
return result;
}
}

View File

@@ -0,0 +1,199 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.body.Connection;
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.console.model.MessageView;
import org.apache.rocketmq.console.service.MessageService;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class MessageServiceImpl implements MessageService {
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
/**
* @see org.apache.rocketmq.store.config.MessageStoreConfig maxMsgsNumBatch = 64;
* @see org.apache.rocketmq.store.index.IndexService maxNum = Math.min(maxNum, this.defaultMessageStore.getMessageStoreConfig().getMaxMsgsNumBatch());
*/
private final static int QUERY_MESSAGE_MAX_NUM = 64;
@Resource
private MQAdminExt mqAdminExt;
public Pair<MessageView, List<MessageTrack>> viewMessage(String subject, final String msgId) {
try {
MessageExt messageExt = mqAdminExt.viewMessage(subject, msgId);
List<MessageTrack> messageTrackList = messageTrackDetail(messageExt);
return new Pair<>(MessageView.fromMessageExt(messageExt), messageTrackList);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@Override
public List<MessageView> queryMessageByTopicAndKey(String topic, String key) {
try {
return Lists.transform(mqAdminExt.queryMessage(topic, key, QUERY_MESSAGE_MAX_NUM, 0, System.currentTimeMillis()).getMessageList(), new Function<MessageExt, MessageView>() {
@Override
public MessageView apply(MessageExt messageExt) {
return MessageView.fromMessageExt(messageExt);
}
});
}
catch (Exception err) {
throw Throwables.propagate(err);
}
}
@Override
public List<MessageView> queryMessageByTopic(String topic, final long begin, final long end) {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, null);
List<MessageView> messageViewList = Lists.newArrayList();
try {
String subExpression = "*";
consumer.start();
Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues(topic);
for (MessageQueue mq : mqs) {
long minOffset = consumer.searchOffset(mq, begin);
long maxOffset = consumer.searchOffset(mq, end);
READQ:
for (long offset = minOffset; offset <= maxOffset; ) {
try {
if (messageViewList.size() > 2000) {
break;
}
PullResult pullResult = consumer.pull(mq, subExpression, offset, 32);
offset = pullResult.getNextBeginOffset();
switch (pullResult.getPullStatus()) {
case FOUND:
List<MessageView> messageViewListByQuery = Lists.transform(pullResult.getMsgFoundList(), new Function<MessageExt, MessageView>() {
@Override
public MessageView apply(MessageExt messageExt) {
messageExt.setBody(null);
return MessageView.fromMessageExt(messageExt);
}
});
List<MessageView> filteredList = Lists.newArrayList(Iterables.filter(messageViewListByQuery, new Predicate<MessageView>() {
@Override
public boolean apply(MessageView messageView) {
if (messageView.getStoreTimestamp() < begin || messageView.getStoreTimestamp() > end) {
logger.info("begin={} end={} time not in range {} {}", begin, end, messageView.getStoreTimestamp(), new Date(messageView.getStoreTimestamp()).toString());
}
return messageView.getStoreTimestamp() >= begin && messageView.getStoreTimestamp() <= end;
}
}));
messageViewList.addAll(filteredList);
break;
case NO_MATCHED_MSG:
case NO_NEW_MSG:
case OFFSET_ILLEGAL:
break READQ;
}
}
catch (Exception e) {
break;
}
}
}
Collections.sort(messageViewList, new Comparator<MessageView>() {
@Override
public int compare(MessageView o1, MessageView o2) {
if (o1.getStoreTimestamp() - o2.getStoreTimestamp() == 0) {
return 0;
}
return (o1.getStoreTimestamp() > o2.getStoreTimestamp()) ? -1 : 1;
}
});
return messageViewList;
}
catch (Exception e) {
throw Throwables.propagate(e);
}
finally {
consumer.shutdown();
}
}
@Override
public List<MessageTrack> messageTrackDetail(MessageExt msg) {
try {
return mqAdminExt.messageTrackDetail(msg);
}
catch (Exception e) {
logger.error("op=messageTrackDetailError", e);
return Collections.emptyList();
}
}
@Override
public ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup,
String clientId) {
if (StringUtils.isNotBlank(clientId)) {
try {
return mqAdminExt.consumeMessageDirectly(consumerGroup, clientId, topic, msgId);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
try {
ConsumerConnection consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
for (Connection connection : consumerConnection.getConnectionSet()) {
if (StringUtils.isBlank(connection.getClientId())) {
continue;
}
logger.info("clientId={}", connection.getClientId());
return mqAdminExt.consumeMessageDirectly(consumerGroup, connection.getClientId(), topic, msgId);
}
}
catch (Exception e) {
throw Throwables.propagate(e);
}
throw new IllegalStateException("NO CONSUMER");
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.base.Throwables;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
import org.apache.rocketmq.console.service.MonitorService;
import org.apache.rocketmq.console.util.JsonUtil;
import org.springframework.stereotype.Service;
@Service
public class MonitorServiceImpl implements MonitorService {
@Resource
private RMQConfigure rmqConfigure;
private Map<String, ConsumerMonitorConfig> configMap = new ConcurrentHashMap<>();
@Override
public boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config) {
configMap.put(name, config);// todo if write map success but write file fail
writeToFile(getConsumerMonitorConfigDataPath(), configMap);
return true;
}
@Override
public Map<String, ConsumerMonitorConfig> queryConsumerMonitorConfig() {
return configMap;
}
@Override
public ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName) {
return configMap.get(consumeGroupName);
}
@Override
public boolean deleteConsumerMonitor(String consumeGroupName) {
configMap.remove(consumeGroupName);
writeToFile(getConsumerMonitorConfigDataPath(), configMap);
return true;
}
//rocketmq.console.data.path/monitor/consumerMonitorConfig.json
private String getConsumerMonitorConfigDataPath() {
return rmqConfigure.getRocketMqConsoleDataPath() + File.separatorChar + "monitor" + File.separatorChar + "consumerMonitorConfig.json";
}
private String getConsumerMonitorConfigDataPathBackUp() {
return getConsumerMonitorConfigDataPath() + ".bak";
}
private void writeToFile(String path, Object data) {
writeDataJsonToFile(path, JsonUtil.obj2String(data));
}
private void writeDataJsonToFile(String path, String dataStr) {
try {
MixAll.string2File(dataStr, path);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@PostConstruct
private void loadData() {
String content = MixAll.file2String(getConsumerMonitorConfigDataPath());
if (content == null) {
content = MixAll.file2String(getConsumerMonitorConfigDataPathBackUp());
}
if (content == null) {
return;
}
configMap = JsonUtil.string2Obj(content, new TypeReference<ConcurrentHashMap<String, ConsumerMonitorConfig>>() {
});
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.service.AbstractCommonService;
import org.apache.rocketmq.console.service.OpsService;
import org.apache.rocketmq.console.service.checker.CheckerType;
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
import org.springframework.stereotype.Service;
@Service
public class OpsServiceImpl extends AbstractCommonService implements OpsService {
@Resource
private RMQConfigure rMQConfigure;
@Resource
private List<RocketMqChecker> rocketMqCheckerList;
@Override
public Map<String, Object> homePageInfo() {
Map<String, Object> homePageInfoMap = Maps.newHashMap();
homePageInfoMap.put("namesvrAddrList", Splitter.on(";").splitToList(rMQConfigure.getNamesrvAddr()));
homePageInfoMap.put("useVIPChannel", Boolean.valueOf(rMQConfigure.getIsVIPChannel()));
return homePageInfoMap;
}
@Override
public void updateNameSvrAddrList(String nameSvrAddrList) {
rMQConfigure.setNamesrvAddr(nameSvrAddrList);
}
@Override
public String getNameSvrList() {
return rMQConfigure.getNamesrvAddr();
}
@Override
public Map<CheckerType, Object> rocketMqStatusCheck() {
Map<CheckerType, Object> checkResultMap = Maps.newHashMap();
for (RocketMqChecker rocketMqChecker : rocketMqCheckerList) {
checkResultMap.put(rocketMqChecker.checkerType(), rocketMqChecker.doCheck());
}
return checkResultMap;
}
@Override public boolean updateIsVIPChannel(String useVIPChannel) {
rMQConfigure.setIsVIPChannel(useVIPChannel);
return true;
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.google.common.base.Throwables;
import javax.annotation.Resource;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.console.service.ProducerService;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.springframework.stereotype.Service;
@Service
public class ProducerServiceImpl implements ProducerService {
@Resource
private MQAdminExt mqAdminExt;
@Override
public ProducerConnection getProducerConnection(String producerGroup, String topic) {
try {
return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
}

View File

@@ -0,0 +1,214 @@
/*
* 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 org.apache.rocketmq.console.service.impl;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.admin.TopicStatsTable;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.TopicList;
import org.apache.rocketmq.common.protocol.route.BrokerData;
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
import org.apache.rocketmq.console.service.AbstractCommonService;
import org.apache.rocketmq.console.service.TopicService;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TopicServiceImpl extends AbstractCommonService implements TopicService {
@Autowired
private RMQConfigure rMQConfigure;
@Override
public TopicList fetchAllTopicList() {
try {
return mqAdminExt.fetchAllTopicList();
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@Override
public TopicStatsTable stats(String topic) {
try {
return mqAdminExt.examineTopicStats(topic);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@Override
public TopicRouteData route(String topic) {
try {
return mqAdminExt.examineTopicRouteInfo(topic);
}
catch (Exception ex) {
throw Throwables.propagate(ex);
}
}
@Override
public GroupList queryTopicConsumerInfo(String topic) {
try {
return mqAdminExt.queryTopicConsumeByWho(topic);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
@Override
public void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest) {
TopicConfig topicConfig = new TopicConfig();
BeanUtils.copyProperties(topicCreateOrUpdateRequest, topicConfig);
try {
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
topicCreateOrUpdateRequest.getClusterNameList(), topicCreateOrUpdateRequest.getBrokerNameList())) {
mqAdminExt.createAndUpdateTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topicConfig);
}
}
catch (Exception err) {
throw Throwables.propagate(err);
}
}
@Override
public TopicConfig examineTopicConfig(String topic, String brokerName) {
ClusterInfo clusterInfo = null;
try {
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
}
catch (Exception e) {
throw Throwables.propagate(e);
}
return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic);
}
@Override
public List<TopicConfigInfo> examineTopicConfig(String topic) {
List<TopicConfigInfo> topicConfigInfoList = Lists.newArrayList();
TopicRouteData topicRouteData = route(topic);
for (BrokerData brokerData : topicRouteData.getBrokerDatas()) {
TopicConfigInfo topicConfigInfo = new TopicConfigInfo();
TopicConfig topicConfig = examineTopicConfig(topic, brokerData.getBrokerName());
BeanUtils.copyProperties(topicConfig, topicConfigInfo);
topicConfigInfo.setBrokerNameList(Lists.newArrayList(brokerData.getBrokerName()));
topicConfigInfoList.add(topicConfigInfo);
}
return topicConfigInfoList;
}
@Override
public boolean deleteTopic(String topic, String clusterName) {
try {
if (StringUtils.isBlank(clusterName)) {
return deleteTopic(topic);
}
Set<String> masterSet = CommandUtil.fetchMasterAddrByClusterName(mqAdminExt, clusterName);
mqAdminExt.deleteTopicInBroker(masterSet, topic);
Set<String> nameServerSet = null;
if (StringUtils.isNotBlank(rMQConfigure.getNamesrvAddr())) {
String[] ns = rMQConfigure.getNamesrvAddr().split(";");
nameServerSet = new HashSet<String>(Arrays.asList(ns));
}
mqAdminExt.deleteTopicInNameServer(nameServerSet, topic);
}
catch (Exception err) {
throw Throwables.propagate(err);
}
return true;
}
@Override
public boolean deleteTopic(String topic) {
ClusterInfo clusterInfo = null;
try {
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
}
catch (Exception err) {
throw Throwables.propagate(err);
}
for (String clusterName : clusterInfo.getClusterAddrTable().keySet()) {
deleteTopic(topic, clusterName);
}
return true;
}
@Override
public boolean deleteTopicInBroker(String brokerName, String topic) {
try {
ClusterInfo clusterInfo = null;
try {
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
}
catch (Exception e) {
throw Throwables.propagate(e);
}
mqAdminExt.deleteTopicInBroker(Sets.newHashSet(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr()), topic);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
return true;
}
@Override
public SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest) {
DefaultMQProducer producer = new DefaultMQProducer(MixAll.SELF_TEST_PRODUCER_GROUP);
producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
producer.setNamesrvAddr(rMQConfigure.getNamesrvAddr());
try {
producer.start();
Message msg = new Message(sendTopicMessageRequest.getTopic(),
sendTopicMessageRequest.getTag(),
sendTopicMessageRequest.getKey(),
sendTopicMessageRequest.getMessageBody().getBytes()
);
return producer.send(msg);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
finally {
producer.shutdown();
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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 org.apache.rocketmq.console.support;
import javax.servlet.http.HttpServletRequest;
import org.apache.rocketmq.console.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice(basePackages = "org.apache.rocketmq.console")
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = Exception.class)
@ResponseBody
public JsonResult<Object> jsonErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
JsonResult<Object> value = null;
if (ex != null) {
logger.error("op=global_exception_handler_print_error", ex);
if (ex instanceof ServiceException) {
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
}
else {
value = new JsonResult<Object>(-1, ex.getMessage());
}
}
return value;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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 org.apache.rocketmq.console.support;
import java.lang.annotation.Annotation;
import org.apache.rocketmq.console.aspect.admin.annotation.OriginalControllerReturnValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice(basePackages = "org.apache.rocketmq.console")
public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class);
@Override
public Object beforeBodyWrite(
Object obj, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
Annotation originalControllerReturnValue = methodParameter.getMethodAnnotation(OriginalControllerReturnValue.class);
if (originalControllerReturnValue != null) {
return obj;
}
JsonResult value;
if (obj instanceof JsonResult) {
value = (JsonResult)obj;
}
else {
value = new JsonResult(obj);
}
return value;
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
}

View File

@@ -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 org.apache.rocketmq.console.support;
public class JsonResult<T> {
private int status = 0;
private T data;
private String errMsg;
public JsonResult(T data) {
this.data = data;
}
public JsonResult(int status, String errMsg) {
this.status = status;
this.errMsg = errMsg;
}
public JsonResult(int status, T data, String errMsg) {
this.status = status;
this.data = data;
this.errMsg = errMsg;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
}

View File

@@ -0,0 +1,320 @@
/*
* 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 org.apache.rocketmq.console.task;
import com.google.common.base.Stopwatch;
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.TopicList;
import org.apache.rocketmq.common.protocol.route.BrokerData;
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
import org.apache.rocketmq.store.stats.BrokerStatsManager;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand;
import com.google.common.base.Throwables;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.annotation.Resource;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.service.DashboardCollectService;
import org.apache.rocketmq.console.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DashboardCollectTask {
private Date currentDate = new Date();
@Resource
private MQAdminExt mqAdminExt;
@Resource
private RMQConfigure rmqConfigure;
@Resource
private DashboardCollectService dashboardCollectService;
private final static Logger log = LoggerFactory.getLogger(DashboardCollectTask.class);
@Scheduled(cron = "30 0/1 * * * ?")
@MultiMQAdminCmdMethod(timeoutMillis = 5000)
public void collectTopic() {
if (!rmqConfigure.isEnableDashBoardCollect()) {
return;
}
Date date = new Date();
Stopwatch stopwatch = Stopwatch.createStarted();
try {
TopicList topicList = mqAdminExt.fetchAllTopicList();
Set<String> topicSet = topicList.getTopicList();
for (String topic : topicSet) {
if (topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) || topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX)) {
continue;
}
TopicRouteData topicRouteData = mqAdminExt.examineTopicRouteInfo(topic);
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
double inTPS = 0;
long inMsgCntToday = 0;
double outTPS = 0;
long outMsgCntToday = 0;
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
if (masterAddr != null) {
try {
stopwatch.start();
log.info("start time: {}", stopwatch.toString());
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.TOPIC_PUT_NUMS, topic);
stopwatch.stop();
log.info("stop time : {}", stopwatch.toString());
stopwatch.reset();
inTPS += bsd.getStatsMinute().getTps();
inMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
}
catch (Exception e) {
// throw Throwables.propagate(e);
}
}
}
if (groupList != null && !groupList.getGroupList().isEmpty()) {
for (String group : groupList.getGroupList()) {
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
if (masterAddr != null) {
try {
String statsKey = String.format("%s@%s", topic, group);
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.GROUP_GET_NUMS, statsKey);
outTPS += bsd.getStatsMinute().getTps();
outMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
}
catch (Exception e) {
// throw Throwables.propagate(e);
}
}
}
}
}
List<String> list;
try {
list = dashboardCollectService.getTopicMap().get(topic);
}
catch (ExecutionException e) {
throw Throwables.propagate(e);
}
if (null == list) {
list = Lists.newArrayList();
}
list.add(date.getTime() + "," + new BigDecimal(inTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + inMsgCntToday + "," + new BigDecimal(outTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + outMsgCntToday);
dashboardCollectService.getTopicMap().put(topic, list);
}
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
}
catch (Exception err) {
throw Throwables.propagate(err);
}
}
@Scheduled(cron = "0 0/1 * * * ?")
public void collectBroker() {
if (!rmqConfigure.isEnableDashBoardCollect()) {
return;
}
try {
Date date = new Date();
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
Set<Map.Entry<String, BrokerData>> clusterEntries = clusterInfo.getBrokerAddrTable().entrySet();
Map<String, String> addresses = Maps.newHashMap();
for (Map.Entry<String, BrokerData> clusterEntry : clusterEntries) {
HashMap<Long, String> addrs = clusterEntry.getValue().getBrokerAddrs();
Set<Map.Entry<Long, String>> addrsEntries = addrs.entrySet();
for (Map.Entry<Long, String> addrEntry : addrsEntries) {
addresses.put(addrEntry.getValue(), clusterEntry.getKey() + ":" + addrEntry.getKey());
}
}
Set<Map.Entry<String, String>> entries = addresses.entrySet();
for (Map.Entry<String, String> entry : entries) {
List<String> list = dashboardCollectService.getBrokerMap().get(entry.getValue());
if (null == list) {
list = Lists.newArrayList();
}
KVTable kvTable = fetchBrokerRuntimeStats(entry.getKey(), 3);
if (kvTable == null) {
continue;
}
String[] tpsArray = kvTable.getTable().get("getTotalTps").split(" ");
BigDecimal totalTps = new BigDecimal(0);
for (String tps : tpsArray) {
totalTps = totalTps.add(new BigDecimal(tps));
}
BigDecimal averageTps = totalTps.divide(new BigDecimal(tpsArray.length), 5, BigDecimal.ROUND_HALF_UP);
list.add(date.getTime() + "," + averageTps.toString());
dashboardCollectService.getBrokerMap().put(entry.getValue(), list);
}
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
private KVTable fetchBrokerRuntimeStats(String brokerAddr, Integer retryTime) {
if (retryTime == 0) {
return null;
}
try {
return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr);
}
catch (Exception e) {
try {
Thread.sleep(1000);
}
catch (InterruptedException e1) {
throw Throwables.propagate(e1);
}
fetchBrokerRuntimeStats(brokerAddr, retryTime - 1);
throw Throwables.propagate(e);
}
}
@Scheduled(cron = "0/5 * * * * ?")
public void saveData() {
if (!rmqConfigure.isEnableDashBoardCollect()) {
return;
}
//one day refresh cache one time
String dataLocationPath = rmqConfigure.getConsoleCollectData();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String nowDateStr = format.format(new Date());
String currentDateStr = format.format(currentDate);
if (!currentDateStr.equals(nowDateStr)) {
dashboardCollectService.getBrokerMap().invalidateAll();
dashboardCollectService.getTopicMap().invalidateAll();
currentDate = new Date();
}
File brokerFile = new File(dataLocationPath + nowDateStr + ".json");
File topicFile = new File(dataLocationPath + nowDateStr + "_topic" + ".json");
try {
Map<String, List<String>> brokerFileMap;
Map<String, List<String>> topicFileMap;
if (brokerFile.exists()) {
brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile);
}
else {
brokerFileMap = Maps.newHashMap();
Files.createParentDirs(brokerFile);
}
if (topicFile.exists()) {
topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile);
}
else {
topicFileMap = Maps.newHashMap();
Files.createParentDirs(topicFile);
}
brokerFile.createNewFile();
topicFile.createNewFile();
writeFile(dashboardCollectService.getBrokerMap(), brokerFileMap, brokerFile);
writeFile(dashboardCollectService.getTopicMap(), topicFileMap, topicFile);
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
}
catch (IOException e) {
throw Throwables.propagate(e);
}
}
private void writeFile(LoadingCache<String, List<String>> map, Map<String, List<String>> fileMap,
File file) throws IOException {
Map<String, List<String>> newMap = map.asMap();
Map<String, List<String>> resultMap = Maps.newHashMap();
if (fileMap.size() == 0) {
resultMap = newMap;
}
else {
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
List<String> oldList = entry.getValue();
List<String> newList = newMap.get(entry.getKey());
resultMap.put(entry.getKey(), appendData(newList, oldList));
if (newList == null || newList.size() == 0) {
map.put(entry.getKey(), appendData(newList, oldList));
}
}
for (Map.Entry<String, List<String>> entry : newMap.entrySet()) {
List<String> oldList = fileMap.get(entry.getKey());
if (oldList == null || oldList.size() == 0) {
resultMap.put(entry.getKey(), entry.getValue());
}
}
}
Files.write(JsonUtil.obj2String(resultMap).getBytes(), file);
}
private List<String> appendData(List<String> newTpsList, List<String> oldTpsList) {
List<String> result = Lists.newArrayList();
if (newTpsList == null || newTpsList.size() == 0) {
return oldTpsList;
}
if (oldTpsList == null || oldTpsList.size() == 0) {
return newTpsList;
}
String oldLastTps = oldTpsList.get(oldTpsList.size() - 1);
Long oldLastTimestamp = Long.parseLong(oldLastTps.split(",")[0]);
String newFirstTps = newTpsList.get(0);
Long newFirstTimestamp = Long.parseLong(newFirstTps.split(",")[0]);
if (oldLastTimestamp.longValue() < newFirstTimestamp.longValue()) {
result.addAll(oldTpsList);
result.addAll(newTpsList);
return result;
}
return newTpsList;
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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 org.apache.rocketmq.console.task;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
import org.apache.rocketmq.console.model.GroupConsumeInfo;
import org.apache.rocketmq.console.service.ConsumerService;
import org.apache.rocketmq.console.service.MonitorService;
import org.apache.rocketmq.console.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class MonitorTask {
private Logger logger = LoggerFactory.getLogger(MonitorTask.class);
@Resource
private MonitorService monitorService;
@Resource
private ConsumerService consumerService;
// @Scheduled(cron = "* * * * * ?")
public void scanProblemConsumeGroup() {
for (Map.Entry<String, ConsumerMonitorConfig> configEntry : monitorService.queryConsumerMonitorConfig().entrySet()) {
GroupConsumeInfo consumeInfo = consumerService.queryGroup(configEntry.getKey());
if (consumeInfo.getCount() < configEntry.getValue().getMinCount() || consumeInfo.getDiffTotal() > configEntry.getValue().getMaxDiffTotal()) {
logger.info("op=look consumeInfo {}", JsonUtil.obj2String(consumeInfo)); // notify the alert system
}
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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 org.apache.rocketmq.console.util;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("unchecked")
public class JsonUtil {
private static Logger logger = LoggerFactory.getLogger(JsonUtil.class);
private static ObjectMapper objectMapper = new ObjectMapper();
private JsonUtil() {
}
static {
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.setFilters(new SimpleFilterProvider().setFailOnUnknownId(false));
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
public static void writeValue(Writer writer, Object obj) {
try {
objectMapper.writeValue(writer, obj);
}
catch (IOException e) {
Throwables.propagateIfPossible(e);
}
}
public static <T> String obj2String(T src) {
if (src == null) {
return null;
}
try {
return src instanceof String ? (String)src : objectMapper.writeValueAsString(src);
}
catch (Exception e) {
logger.error("Parse Object to String error src=" + src, e);
return null;
}
}
public static <T> byte[] obj2Byte(T src) {
if (src == null) {
return null;
}
try {
return src instanceof byte[] ? (byte[])src : objectMapper.writeValueAsBytes(src);
}
catch (Exception e) {
logger.error("Parse Object to byte[] error", e);
return null;
}
}
public static <T> T string2Obj(String str, Class<T> clazz) {
if (Strings.isNullOrEmpty(str) || clazz == null) {
return null;
}
str = escapesSpecialChar(str);
try {
return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz);
}
catch (Exception e) {
logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e);
return null;
}
}
public static <T> T byte2Obj(byte[] bytes, Class<T> clazz) {
if (bytes == null || clazz == null) {
return null;
}
try {
return clazz.equals(byte[].class) ? (T)bytes : objectMapper.readValue(bytes, clazz);
}
catch (Exception e) {
logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e);
return null;
}
}
public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
if (Strings.isNullOrEmpty(str) || typeReference == null) {
return null;
}
str = escapesSpecialChar(str);
try {
return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
}
catch (Exception e) {
logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str,
typeReference.getType(), e);
return null;
}
}
public static <T> T byte2Obj(byte[] bytes, TypeReference<T> typeReference) {
if (bytes == null || typeReference == null) {
return null;
}
try {
return (T)(typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes,
typeReference));
}
catch (Exception e) {
logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes,
typeReference.getType(), e);
return null;
}
}
public static <T> T map2Obj(Map<String, String> map, Class<T> clazz) {
String str = obj2String(map);
return string2Obj(str, clazz);
}
private static String escapesSpecialChar(String str) {
return str.replace("\n", "\\n").replace("\r", "\\r");
}
}

View File

@@ -0,0 +1,16 @@
server.contextPath=
server.port=8080
#spring.application.index=true
spring.application.name=rocketmq-console
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
logging.config=classpath:logback.xml
#if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
rocketmq.config.namesrvAddr=
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
rocketmq.config.isVIPChannel=
#rocketmq-console's data path:dashboard/monitor
rocketmq.config.dataPath=/tmp/rocketmq-console/data
#set it false if you don't want use dashboard.default true
rocketmq.config.enableDashBoardCollect=true

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
</encoder>
</appender>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/logs/consolelogs/rocketmq-console.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.home}/logs/consolelogs/rocketmq-console-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>104857600</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<MaxHistory>10</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>

View File

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<!--
~ 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.
-->
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>RocketMq-console-ng</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name='description' content=''>
<meta name='keywords' content=''>
<!--iOS -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- preLoading -->
<link rel="stylesheet" href="style/preLoading/normalize.css">
<link rel="stylesheet" href="style/preLoading/main.css">
<script src="vendor/preLoading/modernizr-2.6.2.min.js"></script>
<!-- preLoading -->
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="vendor/bootstrap-material-design/css/bootstrap-material-design.css">
<link rel="stylesheet" href="vendor/bootstrap-material-design/css/ripples.css">
<link rel="stylesheet" href="vendor/angular/notification/angular-ui-notification.css">
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog.min.css">
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog-theme-default.css">
<link rel="stylesheet" href="vendor/dropdown/jquery.dropdown.css">
<link rel="stylesheet" href="vendor/datatimepicker/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/fonts/fontawesome-webfont.svg">
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen.css"/>
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen-spinner.css"/>
<link rel="stylesheet" type="text/css" href="vendor/angular-material/angular-material.min.css"/>
<link rel="stylesheet" type="text/css" href="vendor/md-tab/docs.css"/>
<link rel="stylesheet" href="style/animate.css">
<link rel="stylesheet" href="style/theme.css">
<link rel="stylesheet" href="style/app.css">
</head>
<body ng-controller="AppCtrl">
<!--[if lte IE 9]>
<script type="text/javascript">
location.href = 'view/pages/un_support_browser.html';
</script>
<![endif]-->
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
</div>
<div ng-include="'view/layout/_header.html'"></div>
<section ng-view></section>
<div ng-include="'view/layout/_footer.html'"></div>
<script type="text/javascript" src="vendor/jquery/jquery1.11.3.min.js"></script>
<script type="text/javascript" src="vendor/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="vendor/bootstrap-material-design/js/material.min.js"></script>
<script type="text/javascript" src="vendor/bootstrap-material-design/js/ripples.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-translate.min.js"></script>
<script type="text/javascript"
src="vendor/angular/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js"></script>
<script type="text/javascript" src="vendor/angular/i18n/angular-locale_zh-cn.js"></script>
<script type="text/javascript" src="src/i18n/en.js"></script>
<script type="text/javascript" src="src/i18n/zh.js"></script>
<script type="text/javascript" src="vendor/angular/angular-cookies.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-animate.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-route.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-ui-router.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-sanitize.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-aria.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-messages.min.js"></script>
<script type="text/javascript" src="vendor/angular/angular-sanitize.min.js"></script>
<script type="text/javascript" src="vendor/angular/notification/angular-ui-notification.js"></script>
<script type="text/javascript" src="vendor/pagination/tm.pagination.js"></script>
<script type="text/javascript" src="vendor/ng-dialog/ngDialog.min.js"></script>
<script type="text/javascript" src="vendor/json-bigint/json-bigint.js"></script>
<script type="text/javascript" src="vendor/dropdown/jquery.dropdown.js"></script>
<script type="text/javascript" src="vendor/datatimepicker/moment.min.js"></script>
<script type="text/javascript" src="vendor/datatimepicker/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="vendor/datatimepicker/angular-eonasdan-datetimepicker.min.js"></script>
<script type="text/javascript" src="vendor/chosen/angular-chosen.js"></script>
<script type="text/javascript" src="vendor/chosen/chosen.jquery.min.js"></script>
<script type="text/javascript" src='vendor/md-tab/svg-assets-cache.js'></script>
<script type="text/javascript" src='vendor/angular-material/angular-material.min.js'></script>
<script type="text/javascript" src="vendor/echarts/echarts.min.js"></script>
<script type="text/javascript" src="src/app.js"></script>
<script type="text/javascript" src="src/controller.js?v=201702250025"></script>
<script type="text/javascript" src="src/tools/tools.js"></script>
<script type="text/javascript" src="src/cluster.js?timestamp=4"></script>
<script type="text/javascript" src="src/topic.js"></script>
<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
<script type="text/javascript" src="src/producer.js"></script>
<script type="text/javascript" src="src/message.js"></script>
<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
<script type="text/javascript" src="vendor/preLoading/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,208 @@
/*
* 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.
*/
'use strict';
var app = angular.module('app', [
'ngAnimate',
'ngCookies',
'ngRoute',
'ngDialog',
'ngMaterial',
'ngSanitize',
'material.svgAssetsCache',
'ui-notification',
'tm.pagination',
'ae-datetimepicker',
'localytics.directives',
'pascalprecht.translate'
]).run(
['$rootScope','$location','$cookies',
function ($rootScope,$location,$cookies) {
// var filter = function(url){
// var outFilterArrs = []
// outFilterArrs.push("/login");
// outFilterArrs.push("/reg");
// outFilterArrs.push("/logout");
// outFilterArrs.push("/404");
// var flag = false;
// $.each(outFilterArrs,function(i,value){
// if(url.indexOf(value) > -1){
// flag = true;
// return false;
// }
// });
// return flag;
// }
// if(angular.isDefined($cookies.get("isLogin")) && $cookies.get("isLogin") == 'true'){
// chatApi.login();
// }
$rootScope.$on('$routeChangeSuccess', function() {
var pathArray = $location.url().split("/");
var index = pathArray.indexOf("");
if(index >= 0){
pathArray.remove(index);
}
$rootScope.path = pathArray[0];
//初始化material UI控件
$.material.init();
});
$rootScope.$on('$routeChangeStart',function (evt, next,current) {
window.clearInterval($rootScope._thread);
})
}
]
).animation('.view', function () {
return {
animate: function (element, className, from, to, done) {
//styles
}
}
});
app.provider('getDictName', function () {
var dictList = [];
this.init = function () {
var url = "src/data/dict.json";//无法使用common服务类地址只能写死
var params = {};
$.get(url, params, function (ret) {
dictList = ret;
})
}
this.$get = function () {
return function (dictType, value) {
for (var i = 0; i < dictList.length; i++) {
var dict = dictList[i];
if (dict.TYPE == dictType && dict.DICT_VALUE == value) {
return dict.DICT_NAME;
}
}
}
}
})
app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNameProvider','$sceProvider','$translateProvider','$mdThemingProvider',
function ($routeProvider, $httpProvider ,$cookiesProvider,getDictNameProvider,$sceProvider,$translateProvider,$mdThemingProvider) {
//关闭html校验存在安全隐患但目前没问题使用ng-bind-html需要注意防止跨站攻击
$sceProvider.enabled(false);
//前端字典项目初始化
getDictNameProvider.init();
//init angular
$mdThemingProvider.theme('default')
.primaryPalette('pink')
.accentPalette('light-blue');
//设置ajax默认配置
$.ajaxSetup({
type: "POST",
contentType: 'application/json',
cache:false,
timeout : 5000, //超时时间设置,单位毫秒
converters:{
"text json": JSONbig.parse
}
});
$httpProvider.defaults.cache = false;
$routeProvider.when('/', {
templateUrl: 'view/pages/index.html',
controller:'dashboardCtrl'
}).when('/cluster', {
templateUrl: 'view/pages/cluster.html',
controller:'clusterController'
}).when('/topic', {
templateUrl: 'view/pages/topic.html',
controller:'topicController'
}).when('/consumer', {
templateUrl: 'view/pages/consumer.html',
controller:'consumerController'
}).when('/producer', {
templateUrl: 'view/pages/producer.html',
controller:'producerController'
}).when('/message', {
templateUrl: 'view/pages/message.html',
controller:'messageController'
}).when('/ops', {
templateUrl: 'view/pages/ops.html',
controller:'opsController'
}).when('/404', {
templateUrl: '404'
}).otherwise('404');
$translateProvider.translations('en',en);
$translateProvider.translations('zh',zh);
$translateProvider.preferredLanguage('en');
$translateProvider.useCookieStorage();
// $translateProvider.useSanitizeValueStrategy('sanitize');
}]);
app.filter('range', function() {
return function(input, range) {
var total = parseInt(range.totalPage) + 1;
var count = 5;
for (var i = range.start; i<total; i++) {
if(count > 0){
input.push(i);
count -- ;
}else {
break;
}
}
return input;
};
});
app.filter('dict',['getDictName',function(getDictName){
return function(value,type){
return getDictName(type,value);
}
}])
/**
* 数组扩展方法,移除数组中某一元素或某一段元素
* @param from 需要移除元素的索引开始值(只传一个参数表示单独移除该元素)
* @param to 需要移除元素的索引结束值
* @returns {*}
*/
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
/**
* 根据元素值查询数组中元素的索引
* @param val
* @returns {number}
*/
Array.prototype.indexOf = function(val) {
for (var i = 0; i < this.length; i++) {
if (this[i] == val) return i;
}
return -1;
};

View File

@@ -0,0 +1,72 @@
/*
* 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.
*/
app.controller('clusterController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) {
$scope.clusterMap = {};//cluster:brokerNameList
$scope.brokerMap = {};//brokerName:{id:addr}
$scope.brokerDetail = {};//{brokerName,id:detail}
$scope.clusterNames = [];
$scope.selectedCluster = "";
var callback = function (resp) {
if (resp.status == 0) {
$scope.clusterMap = resp.data.clusterInfo.clusterAddrTable;
$scope.brokerMap = resp.data.clusterInfo.brokerAddrTable;
$scope.brokerDetail = resp.data.brokerServer;
$.each($scope.clusterMap,function(clusterName,clusterBrokersNames){
$scope.clusterNames.push(clusterName);
});
if ($scope.clusterNames.length > 0) {
$scope.selectedCluster = $scope.clusterNames[0];
}
$scope.brokers = tools.generateBrokerMap($scope.brokerDetail,$scope.clusterMap,$scope.brokerMap);
$scope.switchCluster();
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
}
remoteApi.queryClusterList(callback);
$scope.switchCluster = function(){
$scope.instances = $scope.brokers[$scope.selectedCluster];
}
$scope.showDetail = function (brokerName,index) {
$scope.detail = $scope.brokerDetail[brokerName][index];
$scope.brokerName = brokerName;
$scope.index = index;
$(".brokerModal").modal();
}
$scope.showConfig = function (brokerAddr,brokerName,index) {
$scope.brokerAddr = brokerAddr;
$scope.brokerName = brokerName;
$scope.index = index;
$http({
method: "GET",
url: "cluster/brokerConfig.query",
params:{brokerAddr:brokerAddr}
}).success(function (resp) {
if (resp.status == 0) {
$scope.brokerConfig = resp.data;
$(".configModal").modal();
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
})
}
}])

View File

@@ -0,0 +1,350 @@
/*
* 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.
*/
var module = app;
module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.paginationConf = {
currentPage: 1,
totalItems: 0,
itemsPerPage: 10,
pagesLength: 15,
perPageOptions: [10],
rememberPerPage: 'perPageItems',
onChange: function () {
$scope.showConsumerGroupList(this.currentPage,this.totalItems);
}
};
$scope.sortKey = null;
$scope.sortOrder=1;
$scope.intervalProcessSwitch = false;
$scope.intervalProcess = null;
$scope.allConsumerGrouopList = [];
$scope.consumerGroupShowList = [];
$scope.sortByKey = function (key) {
$scope.paginationConf.currentPage=1;
$scope.sortOrder = -$scope.sortOrder;
$scope.sortKey = key;
$scope.doSort();
};
$scope.doSort = function (){// todo how to change this fe's code ? (it's dirty)
if($scope.sortKey == 'diffTotal'){
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.diffTotal > b.diffTotal) ? $scope.sortOrder : ((b.diffTotal > a.diffTotal) ? -$scope.sortOrder : 0);} );
}
if($scope.sortKey == 'group'){
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.group > b.group) ? $scope.sortOrder : ((b.group > a.group) ? -$scope.sortOrder : 0);} );
}
if($scope.sortKey == 'count'){
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.count > b.count) ? $scope.sortOrder : ((b.count > a.count) ? -$scope.sortOrder : 0);} );
}
if($scope.sortKey == 'consumeTps'){
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.consumeTps > b.consumeTps) ? $scope.sortOrder : ((b.consumeTps > a.consumeTps) ? -$scope.sortOrder : 0);} );
}
$scope.filterList($scope.paginationConf.currentPage)
};
$scope.refreshConsumerData = function () {
$http({
method: "GET",
url: "consumer/groupList.query"
}).success(function (resp) {
if(resp.status ==0){
$scope.allConsumerGrouopList = resp.data;
console.log($scope.allConsumerGrouopList);
console.log(JSON.stringify(resp));
$scope.showConsumerGroupList($scope.paginationConf.currentPage,$scope.allConsumerGrouopList.length);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.monitor = function(consumerGroupName){
$http({
method: "GET",
url: "monitor/consumerMonitorConfigByGroupName.query",
params:{consumeGroupName:consumerGroupName}
}).success(function (resp) {
// if(resp.status ==0){
ngDialog.open({
template: 'consumerMonitorDialog',
controller: 'consumerMonitorDialogController',
data:{consumerGroupName:consumerGroupName,data:resp.data}
});
// }else {
// Notification.error({message: resp.errMsg, delay: 2000});
// }
});
};
$scope.$watch('intervalProcessSwitch', function () {
if ($scope.intervalProcess != null) {
clearInterval($scope.intervalProcess);
$scope.intervalProcess = null;
}
if ($scope.intervalProcessSwitch) {
$scope.intervalProcess = setInterval($scope.refreshConsumerData, 10000);
}
});
$scope.refreshConsumerData();
$scope.filterStr="";
$scope.$watch('filterStr', function() {
$scope.paginationConf.currentPage=1;
$scope.filterList(1)
});
$scope.filterList = function (currentPage) {
var lowExceptStr = $scope.filterStr.toLowerCase();
var canShowList = [];
$scope.allConsumerGrouopList.forEach(function(element) {
console.log(element)
if (element.group.toLowerCase().indexOf(lowExceptStr) != -1){
canShowList.push(element);
}
});
$scope.paginationConf.totalItems =canShowList.length;
var perPage = $scope.paginationConf.itemsPerPage;
var from = (currentPage - 1) * perPage;
var to = (from + perPage)>canShowList.length?canShowList.length:from + perPage;
$scope.consumerGroupShowList = canShowList.slice(from, to);
};
$scope.showConsumerGroupList = function (currentPage,totalItem) {
var perPage = $scope.paginationConf.itemsPerPage;
var from = (currentPage - 1) * perPage;
var to = (from + perPage)>totalItem?totalItem:from + perPage;
$scope.consumerGroupShowList = $scope.allConsumerGrouopList.slice(from, to);
$scope.paginationConf.totalItems = totalItem ;
console.log($scope.consumerGroupShowList)
console.log($scope.paginationConf.totalItems)
$scope.doSort()
};
$scope.openAddDialog = function () {
$scope.openCreateOrUpdateDialog(null);
};
$scope.openCreateOrUpdateDialog = function(request){
var bIsUpdate = true;
if(request == null){
request = [{
brokerNameList: [],
subscriptionGroupConfig: {
groupName: "",
consumeEnable: true,
consumeFromMinEnable: true,
consumeBroadcastEnable: true,
retryQueueNums: 1,
retryMaxTimes: 16,
brokerId: 0,
whichBrokerWhenConsumeSlowly: 1
}
}];
bIsUpdate = false;
}
console.log(request);
$http({
method: "GET",
url: "cluster/list.query"
}).success(function (resp) {
if(resp.status ==0){
console.log(resp);
ngDialog.open({
template: 'consumerModifyDialog',
controller: 'consumerModifyDialogController',
data:{
consumerRequestList:request,
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
allBrokerNameList:Object.keys(resp.data.brokerServer),
bIsUpdate:bIsUpdate
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.detail = function(consumerGroupName){
$http({
method: "GET",
url: "consumer/queryTopicByConsumer.query",
params:{consumerGroup:consumerGroupName}
}).success(function (resp) {
if(resp.status ==0){
console.log(resp);
ngDialog.open({
template: 'consumerTopicViewDialog',
controller: 'consumerTopicViewDialogController',
data:{consumerGroupName:consumerGroupName,data:resp.data}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.client = function(consumerGroupName){
$http({
method: "GET",
url: "consumer/consumerConnection.query",
params:{consumerGroup:consumerGroupName}
}).success(function (resp) {
if(resp.status ==0){
console.log(resp);
ngDialog.open({
template: 'clientInfoDialog',
// controller: 'addTopicDialogController',
data:{data:resp.data,consumerGroupName:consumerGroupName}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.updateConfigDialog = function(consumerGroupName){
$http({
method: "GET",
url: "consumer/examineSubscriptionGroupConfig.query",
params:{consumerGroup:consumerGroupName}
}).success(function (resp) {
if(resp.status ==0){
console.log(resp);
$scope.openCreateOrUpdateDialog(resp.data);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.delete = function(consumerGroupName){
$http({
method: "GET",
url: "consumer/fetchBrokerNameList.query",
params:{
consumerGroup:consumerGroupName
}
}).success(function (resp) {
if(resp.status ==0){
console.log(resp);
ngDialog.open({
template: 'deleteConsumerDialog',
controller: 'deleteConsumerDialogController',
data:{
// allClusterList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
allBrokerNameList:resp.data,
consumerGroupName:consumerGroupName
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
}])
module.controller('consumerMonitorDialogController', function ($scope, ngDialog, $http,Notification) {
$scope.createOrUpdateConsumerMonitor = function () {
$http({
method: "POST",
url: "monitor/createOrUpdateConsumerMonitor.do",
params:{consumeGroupName:$scope.ngDialogData.consumerGroupName,
minCount:$scope.ngDialogData.data.minCount,
maxDiffTotal:$scope.ngDialogData.data.maxDiffTotal}
}).success(function (resp) {
if(resp.status ==0){
Notification.info({message: "delete success!", delay: 2000});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
}
);
module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.selectedClusterList = [];
$scope.selectedBrokerNameList = [];
$scope.delete = function () {
console.log($scope.selectedClusterList);
console.log($scope.selectedBrokerNameList);
console.log($scope.ngDialogData.consumerGroupName);
$http({
method: "POST",
url: "consumer/deleteSubGroup.do",
data:{groupName:$scope.ngDialogData.consumerGroupName,
brokerNameList:$scope.selectedBrokerNameList}
}).success(function (resp) {
if(resp.status ==0){
Notification.info({message: "delete success!", delay: 2000});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
}]
);
module.controller('consumerModifyDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.postConsumerRequest = function (consumerRequest) {
var request = JSON.parse(JSON.stringify(consumerRequest));
console.log(request);
$http({
method: "POST",
url: "consumer/createOrUpdate.do",
data:request
}).success(function (resp) {
if(resp.status ==0){
Notification.info({message: "update success!", delay: 2000});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
}]
);
module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
$scope.consumerRunningInfo = function (consumerGroup, clientId, jstack) {
$http({
method: "GET",
url: "consumer/consumerRunningInfo.query",
params: {
consumerGroup: consumerGroup,
clientId: clientId,
jstack: jstack
}
}).success(function (resp) {
if (resp.status == 0) {
ngDialog.open({
template: 'consumerClientDialog',
data:{consumerClientInfo:resp.data,
clientId:clientId}
});
} else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
}]
);

View File

@@ -0,0 +1,557 @@
/*
* 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.
*/
app.controller('AppCtrl', ['$scope','$rootScope','$cookies','$location','$translate', function ($scope,$rootScope,$cookies,$location,$translate) {
$scope.changeTranslate = function(langKey){
$translate.use(langKey);
}
}]);
app.controller('dashboardCtrl', ['$scope','$rootScope','$translate','$filter','Notification','remoteApi','tools', function ($scope,$rootScope,$translate,$filter,Notification,remoteApi,tools) {
$scope.barChart = echarts.init(document.getElementById('main'));
$scope.lineChart = echarts.init(document.getElementById('line'));
$scope.topicBarChart = echarts.init(document.getElementById('topicBar'));
$scope.topicLineChart = echarts.init(document.getElementById('topicLine'));
$scope.timepickerOptions ={format: 'YYYY-MM-DD', showClear: true};
$scope.topicNames = [];
$translate('BROKER').then(function (broker) {
$scope.BROKER_TITLE = broker;
initBrokerBarChart();
initBrokerLineChart();
}, function (translationId) {
$scope.BROKER_TITLE = translationId;
});
$translate('TOPIC').then(function (topic) {
$scope.TOPIC_TITLE = topic;
initTopicBarChart();
initTopicLineChart();
}, function (translationId) {
$scope.TOPIC_TITLE = translationId;
});
var initBrokerBarChart = function(){
$scope.barChart.setOption({
title: {
text:$scope.BROKER_TITLE + ' TOP 10'
},
tooltip: {},
legend: {
data:['TotalMsg']
},
axisPointer : {
type : 'shadow'
},
xAxis: {
data: [],
axisLabel: {
inside: false,
textStyle: {
color: '#000000'
},
rotate: 0,
interval:0
},
axisTick: {
show: true
},
axisLine: {
show: true
},
z: 10
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
axisLabel: {
formatter: function(value){
return value.toFixed(2);
}
},
splitLine: {
show: true
}
},
series: [{
name: 'TotalMsg',
type: 'bar',
data: []
}]
})
}
var initBrokerLineChart = function(){
$scope.lineChart.setOption({
title: {
text: $scope.BROKER_TITLE + ' 5min trend'
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false
}
},
yAxis: {
type: 'value',
boundaryGap: [0, '80%'],
axisLabel: {
formatter: function(value){
return value.toFixed(2);
}
},
splitLine: {
show: true
}
},
dataZoom: [{
type: 'inside',
start: 90,
end: 100
}, {
start: 0,
end: 10,
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2
}
}],
legend: {
data: [],
top:30
},
xAxis: {
type: 'time',
boundaryGap: false,
data: []
},
series: []
})
}
var initTopicBarChart = function(){
$scope.topicBarChart.setOption({
title: {
text:$scope.TOPIC_TITLE + ' TOP 10'
},
tooltip: {},
legend: {
data:['TotalMsg']
},
axisPointer : {
type : 'shadow'
},
xAxis: {
data: [],
axisLabel: {
inside: false,
textStyle: {
color: '#000000'
},
rotate: 0,
interval:0
},
axisTick: {
show: true
},
axisLine: {
show: true
},
z: 10
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
axisLabel: {
formatter: function(value){
return value.toFixed(2);
}
},
splitLine: {
show: true
}
},
series: [{
name: 'TotalMsg',
type: 'bar',
data: []
}]
})
}
var initTopicLineChart = function(){
var _option = {
baseOption:{
title: {
text: $scope.TOPIC_TITLE + ' 5min trend'
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
grid:{
top:100
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false
}
},
yAxis: {
type: 'value',
boundaryGap: [0, '80%'],
axisLabel: {
formatter: function(value){
return value.toFixed(2);
}
},
splitLine: {
show: true
}
},
dataZoom: [{
type: 'inside',
start: 90,
end: 100
}, {
start: 0,
end: 10,
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2
}
}],
legend:{
data:[],
top:30
},
xAxis: {
type: 'time',
boundaryGap: false,
data: []
},
series: []
}
}
$scope.topicLineChart.setOption(_option)
}
var getBrokerBarChartOp = function(xAxisData,data){
// 指定图表的配置项和数据
var option = {
xAxis: {
data: xAxisData,
axisLabel: {
inside: false,
textStyle: {
color: '#000000'
},
rotate: 0,
interval:0
},
axisTick: {
show: true
},
axisLine: {
show: true
},
z: 10
},
series: [{
name: 'TotalMsg',
type: 'bar',
data: data
}]
};
$scope.barChart.setOption(option);
}
var callback = function (resp) {
$scope.barChart.hideLoading();
if (resp.status == 0) {
var clusterMap = resp.data.clusterInfo.clusterAddrTable;
var brokerMap = resp.data.clusterInfo.brokerAddrTable;
var brokerDetail = resp.data.brokerServer;
var clusterMap = tools.generateBrokerMap(brokerDetail,clusterMap,brokerMap);
$scope.brokerArray = [];
$.each(clusterMap,function(clusterName,brokers){
$.each(brokers,function(i,broker){
$scope.brokerArray.push(broker)
})
})
//sort the brokerArray
$scope.brokerArray.sort(function(firstBroker,lastBroker){
var firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow);
var lastTotalMsg = parseFloat(lastBroker.msgGetTotalTodayNow);
return lastTotalMsg-firstTotalMsg;
});
var xAxisData = [],
data = [];
$.each($scope.brokerArray,function(i,broker){
if(i > 9){
return false;
}
xAxisData.push(broker.brokerName + ":" + broker.index);
data.push(broker.msgGetTotalTodayNow);
})
getBrokerBarChartOp(xAxisData,data);
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
}
$scope.barChart.showLoading();
remoteApi.queryClusterList(callback);
$scope.topicBarChart.showLoading();
remoteApi.queryTopicCurrentData(function(resp){
$scope.topicBarChart.hideLoading();
if (resp.status == 0) {
var topicList = resp.data;
topicList.sort(function(first,last){
var firstTotalMsg = parseFloat(first.split(",")[1]);
var lastTotalMsg = parseFloat(last.split(",")[1]);
return lastTotalMsg-firstTotalMsg;
})
var xAxisData = [];
var data = [];
$.each(topicList,function (i,currentData) {
var currentArray = currentData.split(",");
$scope.topicNames.push(currentArray[0]);
if(!angular.isDefined($scope.selectedTopic)){
$scope.selectedTopic = currentArray[0];
}
})
$.each(topicList,function (i, currentData) {
if(i > 9){
return false;
}
var currentArray = currentData.split(",");
xAxisData.push(currentArray[0]);
data.push(currentArray[1]);
})
// 指定图表的配置项和数据
var option = {
xAxis: {
data: xAxisData,
axisLabel: {
inside: false,
textStyle: {
color: '#000000'
},
rotate: 60,
interval:0
},
axisTick: {
show: true
},
axisLine: {
show: true
},
z: 10
},
series: [{
name: 'TotalMsg',
type: 'bar',
data: data
}]
};
$scope.topicBarChart.setOption(option);
queryLineData();
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
})
var getBrokerLineChart = function(legend,data){
var series = [];
var xAxisData = [];
var flag = true;
var i = 0;
$.each(data,function(key,value){
// if(i > 9 ){
// return false;
// }
var _tps = [];
$.each(value,function(i,tpsValue){
var tpsArray = tpsValue.split(",");
if(flag){
xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss"));
}
_tps.push(tpsArray[1]);
})
flag = false;
var _series = {
name:key,
type:'line',
smooth:true,
symbol: 'none',
sampling: 'average',
data: _tps
}
series.push(_series);
i++
})
var option = {
legend: {
data: legend
},
color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", '#EE7942'],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData
},
series: series
};
return option;
}
var getTopicLineChart = function(legend,data){
var series = [];
var xAxisData = [];
var flag = true;
var i = 0;
$.each(data,function(key,value){
var _tps = [];
$.each(value,function(i,tpsValue){
var tpsArray = tpsValue.split(",");
if(flag){
xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss"));
}
_tps.push(tpsArray[3]);
})
flag = false;
var _series = {
name:key,
type:'line',
smooth:true,
symbol: 'none',
sampling: 'average',
data: _tps
}
series.push(_series);
i++
})
var option = {
baseOption:{
legend: {
data: legend
},
// color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", '#EE7942'],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData
},
series: series
},
media:[
{
query:{},
option:{
}
}
]
};
return option;
}
var queryLineData = function () {
var _date;
if($scope.date != null){
_date = $filter('date')($scope.date.valueOf(), "yyyy-MM-dd");
}else{
_date = $filter('date')(new Date(), "yyyy-MM-dd");
}
// $scope.lineChart.showLoading();
remoteApi.queryBrokerHisData(_date,function(resp){
// $scope.lineChart.hideLoading();
if (resp.status == 0) {
var _data = {}
var _xAxisData = [];
$.each(resp.data,function(address,values){
_data[address] = values;
_xAxisData.push(address);
})
$scope.lineChart.setOption(getBrokerLineChart(_xAxisData,_data));
}else{
Notification.error({message: "" + resp.errMsg, delay: 2000});
}
})
$scope.topicLineChart.showLoading();
remoteApi.queryTopicHisData(_date,$scope.selectedTopic,function (resp) {
$scope.topicLineChart.hideLoading();
if (resp.status == 0) {
var _data = {};
_data[$scope.selectedTopic] = resp.data;
var _xAxisData = $scope.selectedTopic;
$scope.topicLineChart.setOption(getTopicLineChart(_xAxisData,_data));
}else{
Notification.error({message: "" + resp.errMsg, delay: 2000});
}
})
}
//router after will clear this thread
$rootScope._thread = setInterval( queryLineData, tools.dashboardRefreshTime);
}]);

View File

@@ -0,0 +1,4 @@
[
{"TYPE":"DEMO_TYPE","DICT_VALUE":"0","DICT_NAME":"test1"},
{"TYPE":"DEMO_TYPE","DICT_VALUE":"1","DICT_NAME":"test2"}
]

View File

@@ -0,0 +1,80 @@
var en = {
"TITLE": "RocketMq-Console-Ng",
"CLOSE": "Close",
"NO": "NO.",
"ADDRESS": "Address",
"VERSION": "Version",
"PRO_MSG_TPS": "Produce Massage TPS",
"CUS_MSG_TPS": "Consumer Massage TPS",
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
"TODAY_PRO_COUNT": "Today Produce Count",
"TODAY_CUS_COUNT": "Today Ponsume Count",
"INSTANCE": "Instance",
"SPLIT": "Broker",
"CLUSTER": "Cluster",
"CLUSTER_DETAIL": "Cluster Detail",
"TOPIC": "Topic",
"SUBSCRIPTION_GROUP":"SubscriptionGroup",
"PRODUCER_GROUP":"ProducerGroup",
"CONSUMER":"Consumer",
"PRODUCER":"Producer",
"MESSAGE":"Message",
"COMMIT": "Commit",
"OPERATION": "Operation",
"ADD": "Add",
"UPDATE": "Update",
"STATUS": "Status",
"ROUTER": "Router",
"MANAGE": "Manage",
"CONFIG": "Config",
"SEND_MSG": "Send Massage",
"RESET_CUS_OFFSET": "Reset Consumer Offset",
"DELETE": "Delete",
"CHANGE_LANG": "ChangeLanguage",
"BROKER": "Broker",
"NORMAL": "NORMAL",
"RETRY": "RETRY",
"DLQ": "DLQ",
"QUANTITY":"Quantity",
"TYPE":"Type",
"MODE":"Mode",
"DELAY":"Delay",
"DASHBOARD":"Dashboard",
"CONSUME_DETAIL":"CONSUME DETAIL",
"CLIENT":"CLIENT",
"LAST_CONSUME_TIME":"LastConsumeTime",
"TIME":"Time",
"RESET":"RESET",
"DATE":"Date",
"NO_DATA":"NO DATA",
"SEARCH":"Search",
"BEGIN":"Begin",
"END":"End",
"TOPIC_CHANGE":"Topic Change",
"SEND":"Send",
"SUBSCRIPTION_CHANGE":"Subscription Change",
"QUEUE":"Queue",
"MIN_OFFSET":"minOffset",
"MAX_OFFSET":"maxOffset",
"LAST_UPDATE_TIME_STAMP":"lastUpdateTimeStamp",
"QUEUE_DATAS":"queueDatas",
"READ_QUEUE_NUMS":"readQueueNums",
"WRITE_QUEUE_NUMS":"writeQueueNums",
"PERM":"perm",
"TAG":"Tag",
"KEY":"Key",
"MESSAGE_BODY":"Message Body",
"TOPIC_NAME":"topicName",
"ORDER":"order",
"CONSUMER_CLIENT":"consumerClient",
"BROKER_OFFSET":"brokerOffset",
"CONSUMER_OFFSET":"consumerOffset",
"DIFF_TOTAL":"diffTotal",
"LAST_TIME_STAMP":"lastTimeStamp",
"RESET_OFFSET":"resetOffset",
"CLUSTER_NAME":"clusterName",
"OPS":"OPS",
"AUTO_REFRESH":"AUTO_REFRESH",
"REFRESH":"REFRESH"
}

View File

@@ -0,0 +1,80 @@
var zh = {
"TITLE": "RocketMq控制台",
"CLOSE": "关闭",
"NO": "编号",
"ADDRESS": "地址",
"VERSION": "版本",
"PRO_MSG_TPS": "生产消息TPS",
"CUS_MSG_TPS": "消费消息TPS",
"YESTERDAY_PRO_COUNT": "昨日生产总数",
"YESTERDAY_CUS_COUNT": "昨日消费总数",
"TODAY_PRO_COUNT": "今天生产总数",
"TODAY_CUS_COUNT": "今天消费总数",
"INSTANCE": "实例",
"SPLIT": "分片",
"CLUSTER": "集群",
"CLUSTER_DETAIL": "集群详情",
"COMMIT": "提交",
"TOPIC": "主题",
"SUBSCRIPTION_GROUP":"订阅组",
"PRODUCER_GROUP":"生产组",
"CONSUMER":"消费者",
"PRODUCER":"生产者",
"MESSAGE":"消息",
"OPERATION": "操作",
"ADD": "新增",
"UPDATE": "更新",
"STATUS": "状态",
"ROUTER": "路由",
"MANAGE": "管理",
"CONFIG": "配置",
"SEND_MSG": "发送消息",
"RESET_CUS_OFFSET": "重置消费位点",
"DELETE": "删除",
"CHANGE_LANG": "更换语言",
"BROKER": "Broker",
"NORMAL": "普通",
"RETRY": "重试",
"DLQ": "死信",
"QUANTITY":"数量",
"TYPE":"类型",
"MODE":"模式",
"DELAY":"延迟",
"DASHBOARD":"驾驶舱",
"CONSUME_DETAIL":"消费详情",
"CLIENT":"终端",
"LAST_CONSUME_TIME":"最后消费时间",
"TIME":"时间点",
"RESET":"重置",
"DATE":"日期",
"NO_DATA":"暂无数据",
"SEARCH":"搜索",
"BEGIN":"开始",
"END":"结束",
"TOPIC_CHANGE":"修改主题",
"SEND":"发送",
"SUBSCRIPTION_CHANGE":"修改订阅",
"QUEUE":"队列",
"MIN_OFFSET":"最小位点",
"MAX_OFFSET":"最大位点",
"LAST_UPDATE_TIME_STAMP":"上次更新时间",
"QUEUE_DATAS":"队列信息",
"READ_QUEUE_NUMS":"读队列数量",
"WRITE_QUEUE_NUMS":"写队列数量",
"PERM":"perm",
"TAG":"标签",
"KEY":"值",
"MESSAGE_BODY":"消息主体",
"TOPIC_NAME":"主题名",
"ORDER":"顺序",
"CONSUMER_CLIENT":"消费者终端",
"BROKER_OFFSET":"代理者位点",
"CONSUMER_OFFSET":"消费者位点",
"DIFF_TOTAL":"差值",
"LAST_TIME_STAMP":"上次时间",
"RESET_OFFSET":"重置位点",
"CLUSTER_NAME":"集群名",
"OPS":"运维",
"AUTO_REFRESH":"自动刷新",
"REFRESH":"刷新"
}

View File

@@ -0,0 +1,200 @@
/*
* 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.
*/
var module = app;
module.controller('messageController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.allTopicList = [];
$scope.selectedTopic =[];
$scope.key ="";
$scope.messageId ="";
$scope.queryMessageByTopicResult=[];
$scope.queryMessageByTopicAndKeyResult=[];
$scope.queryMessageByMessageIdResult={};
$http({
method: "GET",
url: "topic/list.query"
}).success(function (resp) {
if(resp.status ==0){
$scope.allTopicList = resp.data.topicList.sort();
console.log($scope.allTopicList);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
$scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD HH:mm');
$scope.timepickerEnd = moment().add(1,'hour').format('YYYY-MM-DD HH:mm');
$scope.timepickerOptions ={format: 'YYYY-MM-DD HH:mm', showClear: true};
$scope.paginationConf = {
currentPage: 1,
totalItems: 0,
itemsPerPage: 20,
pagesLength: 15,
perPageOptions: [10],
rememberPerPage: 'perPageItems',
onChange: function () {
$scope.changeShowMessageList(this.currentPage,this.totalItems);
}
};
$scope.queryMessageByTopic = function () {
console.log($scope.selectedTopic);
console.log($scope.timepickerBegin)
console.log($scope.timepickerEnd)
$http({
method: "GET",
url: "message/queryMessageByTopic.query",
params: {
topic: $scope.selectedTopic,
begin: $scope.timepickerBegin.valueOf(),
end: $scope.timepickerEnd.valueOf()
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
$scope.queryMessageByTopicResult = resp.data;
$scope.changeShowMessageList(1,$scope.queryMessageByTopicResult.length);
// todo
// console.log($scope.queryMessageByTopicResult);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.queryMessageByTopicAndKey = function () {
console.log($scope.selectedTopic);
console.log($scope.key);
$http({
method: "GET",
url: "message/queryMessageByTopicAndKey.query",
params: {
topic: $scope.selectedTopic,
key:$scope.key
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
$scope.queryMessageByTopicAndKeyResult = resp.data;
console.log($scope.queryMessageByTopicAndKeyResult);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.queryMessageByBrokerAndOffset = function (storeHost,commitLogOffset) {
$http({
method: "GET",
url: "message/viewMessageByBrokerAndOffset.query",
params: {
brokerHost: storeHost.address,
port:storeHost.port,
offset: commitLogOffset
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
ngDialog.open({
template: 'messageDetailViewDialog',
controller: 'messageDetailViewDialogController',
data: resp.data
});
} else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.queryMessageByMessageId = function (messageId,topic) {
$http({
method: "GET",
url: "message/viewMessage.query",
params: {
msgId: messageId,
topic:topic
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
ngDialog.open({
template: 'messageDetailViewDialog',
controller: 'messageDetailViewDialogController',
data:resp.data
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.changeShowMessageList = function (currentPage,totalItem) {
var perPage = $scope.paginationConf.itemsPerPage;
var from = (currentPage - 1) * perPage;
var to = (from + perPage)>totalItem?totalItem:from + perPage;
$scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, to);
$scope.paginationConf.totalItems = totalItem ;
};
}]);
module.controller('messageDetailViewDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
$scope.resendMessage = function (msgId,topic,consumerGroup) {
$http({
method: "POST",
url: "message/consumeMessageDirectly.do",
params: {
msgId: msgId,
consumerGroup:consumerGroup,
topic:topic
}
}).success(function (resp) {
if (resp.status == 0) {
ngDialog.open({
template: 'operationResultDialog',
data:{
result:resp.data
}
});
}
else {
ngDialog.open({
template: 'operationResultDialog',
data:{
result:resp.errMsg
}
});
}
});
};
$scope.showExceptionDesc = function (errmsg) {
if(errmsg == null){
errmsg = "Don't have Exception"
}
ngDialog.open({
template: 'operationResultDialog',
data:{
result:errmsg
}
});
};
}]
);

View File

@@ -0,0 +1,59 @@
/*
* 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.
*/
app.controller('opsController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) {
$scope.namesvrAddrList = [];
$scope.useVIPChannel = true;
$http({
method: "GET",
url: "ops/homePage.query"
}).success(function (resp) {
if (resp.status == 0) {
$scope.namesvrAddrList = resp.data.namesvrAddrList;
$scope.useVIPChannel = resp.data.useVIPChannel
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
});
$scope.updateNameSvrAddr = function () {
$http({
method: "POST",
url: "ops/updateNameSvrAddr.do",
params:{nameSvrAddrList:$scope.namesvrAddrList.join(";")}
}).success(function (resp) {
if (resp.status == 0) {
Notification.info({message: "SUCCESS", delay: 2000});
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.updateIsVIPChannel = function () {
$http({
method: "POST",
url: "ops/updateIsVIPChannel.do",
params:{useVIPChannel:$scope.useVIPChannel}
}).success(function (resp) {
if (resp.status == 0) {
Notification.info({message: "SUCCESS", delay: 2000});
}else{
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
}]);

View File

@@ -0,0 +1,49 @@
/*
* 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.
*/
var module = app;
module.controller('producerController', ['$scope', '$http','Notification',function ($scope, $http,Notification) {
$scope.selectedTopic=[];
$scope.producerGroup="";
$http({
method: "GET",
url: "topic/list.query"
}).success(function (resp) {
if(resp.status ==0){
$scope.allTopicList = resp.data.topicList.sort();
console.log($scope.allTopicList);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
$scope.queryClientByTopicAndGroup = function () {
$http({
method: "GET",
url: "producer/producerConnection.query",
params:{
topic:$scope.selectedTopic,
producerGroup:$scope.producerGroup
}
}).success(function (resp) {
if(resp.status ==0){
$scope.connectionList = resp.data.connectionSet;
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
} ]);

View File

@@ -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.
*/
app.service('remoteApi', ['$http','tools', function ($http,tools) {
var queryTopic = function(callback){
$http({
method: "GET",
url: "topic/list.query"
}).success(callback);
}
var queryClusterList = function(callback){
$http({
method: "GET",
url: "cluster/list.query"
}).success(callback);
}
var queryBrokerHisData = function(date,callback){
var url = 'dashboard/broker.query';
var data = {date:date};
var setting = {
type: "GET",
data:data,
timeout:15000,//data is too large,so master set time out is long enough
success:callback
}
$.ajax(url,setting)
}
var queryTopicHisData = function(date,topicName,callback){
var url = 'dashboard/topic.query';
var data = {date:date,topicName:topicName};
var setting = {
type: "GET",
data:data,
timeout:15000,//data is too large,so master set time out is long enough
success:callback
}
$.ajax(url,setting)
}
var queryTopicCurrentData = function(callback){
var url = 'dashboard/topicCurrent';
var setting = {
type: "GET",
timeout:15000,//data is too large,so master set time out is long enough
success:callback
}
$.ajax(url,setting)
}
return {
queryTopic:queryTopic,
queryClusterList:queryClusterList,
queryBrokerHisData:queryBrokerHisData,
queryTopicHisData:queryTopicHisData,
queryTopicCurrentData:queryTopicCurrentData
}
}])

View File

@@ -0,0 +1,87 @@
/*
* 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.
*/
app.service('tools', ['$http', function ($http) {
var ctx = "";
var dashboardRefreshTime = 5000; // todo improve. when data size is large,request is too slow
var generateBrokerMap = function(brokerDetail,clusterMap,brokerMap){
var map = {};
$.each(brokerDetail,function(k,v){
$.each(clusterMap,function (ck, cv) {
if(angular.isUndefined(map[ck])){
map[ck] = [];
}
$.each(cv,function(cvi,cvv){
if(cvv == k){
var index = 0;
$.each(v,function(vi,vv){
vv.split = k;
vv.index = index;
vv.address = brokerMap[cvv].brokerAddrs[index];
vv.brokerName = brokerMap[cvv].brokerName;
map[ck].push(vv);
index++;
})
}
})
})
})
return map;
}
var fastSort = function (arrayToSort, propertyToSortWith, sortDirection) {
// temporary holder of position and sort-value
var map = arrayToSort.map(function (e, i) {
if (typeof e[propertyToSortWith] === 'string') {
return { index: i, value: e[propertyToSortWith].toLowerCase() };
}
else {
return { index: i, value: e[propertyToSortWith] };
}
})
// sorting the map containing the reduced values
map.sort(function (a, b) {
if (sortDirection === "ascending") {
return +(a.value > b.value) || +(a.value === b.value) - 1;
}
else {
return +(a.value < b.value) || +(a.value === b.value) - 1;
}
});
// container for the resulting order
var result = map.map(function (e) {
return arrayToSort[e.index];
});
return result;
};
return {
generateBrokerMap:generateBrokerMap,
fastSort:fastSort,
ctx:ctx,
dashboardRefreshTime:dashboardRefreshTime
}
}])

View File

@@ -0,0 +1,406 @@
/**
* Created by tcrow on 2017/1/12 0012.
*/
var module = app;
module.directive('ngConfirmClick', [
function(){
return {
link: function (scope, element, attr) {
var msg = attr.ngConfirmClick || "Are you sure?";
var clickAction = attr.confirmedClick;
element.bind('click',function (event) {
if ( window.confirm(msg) ) {
scope.$eval(clickAction)
}
});
}
};
}]);
module.controller('topicController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.paginationConf = {
currentPage: 1,
totalItems: 0,
itemsPerPage: 10,
pagesLength: 15,
perPageOptions: [10],
rememberPerPage: 'perPageItems',
onChange: function () {
$scope.showTopicList(this.currentPage,this.totalItems);
}
};
$scope.filterNormal = true
$scope.filterRetry = false
$scope.filterDLQ = false
$scope.allTopicList = [];
$scope.topicShowList = [];
$http({
method: "GET",
url: "topic/list.query"
}).success(function (resp) {
if(resp.status ==0){
$scope.allTopicList = resp.data.topicList.sort();
console.log($scope.allTopicList);
console.log(JSON.stringify(resp));
$scope.showTopicList(1,$scope.allTopicList.length);
}else {
Notification.error({message: resp.errMsg, delay: 5000});
}
});
$scope.filterStr="";
$scope.$watch('filterStr', function() {
$scope.filterList(1);
});
$scope.$watch('filterNormal', function() {
$scope.filterList(1);
});
$scope.$watch('filterRetry', function() {
$scope.filterList(1);
});
$scope.$watch('filterDLQ', function() {
$scope.filterList(1);
});
$scope.filterList = function (currentPage) {
var lowExceptStr = $scope.filterStr.toLowerCase();
var canShowList = [];
$scope.allTopicList.forEach(function(element) {
if($scope.filterByType(element)){
if (element.toLowerCase().indexOf(lowExceptStr) != -1){
canShowList.push(element);
}
}
});
$scope.paginationConf.totalItems =canShowList.length;
var perPage = $scope.paginationConf.itemsPerPage;
var from = (currentPage - 1) * perPage;
var to = (from + perPage)>canShowList.length?canShowList.length:from + perPage;
$scope.topicShowList = canShowList.slice(from, to);
};
$scope.filterByType = function(str){
if($scope.filterRetry){
if(str.startsWith("%R")){
return true
}
}
if($scope.filterDLQ){
if(str.startsWith("%D")){
return true
}
}
if($scope.filterNormal){
if(str.startsWith("%") == false){
return true
}
}
return false;
};
$scope.showTopicList = function (currentPage,totalItem) {
if($scope.filterStr != ""){
$scope.filterList(currentPage);
return;
}
var perPage = $scope.paginationConf.itemsPerPage;
var from = (currentPage - 1) * perPage;
var to = (from + perPage)>totalItem?totalItem:from + perPage;
console.log($scope.allTopicList);
console.log(from)
console.log(to)
$scope.topicShowList = $scope.allTopicList.slice(from, to);
$scope.paginationConf.totalItems = totalItem ;
console.log($scope.topicShowList)
console.log($scope.paginationConf.totalItems)
$scope.filterList(currentPage);
};
$scope.deleteTopic= function (topic) {
$http({
method: "POST",
url: "topic/deleteTopic.do",
params:{
topic:topic
}
}).success(function (resp) {
if(resp.status ==0){
Notification.info({message: "delete success!", delay: 2000});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.statsView = function (topic) {
$http({
method: "GET",
url: "topic/stats.query",
params: {topic: topic}
}).success(function (resp) {
if (resp.status == 0) {
console.log(JSON.stringify(resp));
ngDialog.open({
template: 'statsViewDialog',
trapFocus:false,
data:{
topic:topic,
statsData:resp.data
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
})
};
$scope.routerView = function (topic) {
$http({
method: "GET",
url: "topic/route.query",
params: {topic: topic}
}).success(function (resp) {
if (resp.status == 0) {
console.log(JSON.stringify(resp));
ngDialog.open({
template: 'routerViewDialog',
controller: 'routerViewDialogController',
trapFocus:false,
data:{
topic:topic,
routeData:resp.data
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
})
};
$scope.consumerView = function (topic) {
$http({
method: "GET",
url: "topic/queryConsumerByTopic.query",
params: {topic: topic}
}).success(function (resp) {
if (resp.status == 0) {
console.log(JSON.stringify(resp));
ngDialog.open({
template: 'consumerViewDialog',
data:{
topic:topic,
consumerData:resp.data,
consumerGroupCount:Object.keys(resp.data).length
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
})
};
$scope.openDeleteTopicDialog = function (topic) {
ngDialog.open({
template: 'deleteTopicDialog',
controller: 'deleteTopicDialogController',
data:{
topic:topic,
consumerData:"asd"
}
});
};
$scope.openConsumerResetOffsetDialog = function (topic) {
$http({
method: "GET",
url: "topic/queryTopicConsumerInfo.query",
params:{
topic:topic
}
}).success(function (resp) {
if(resp.status ==0){
if(resp.data.groupList == null){
Notification.error({message: "don't have consume group!", delay: 2000});
return
}
ngDialog.open({
template: 'consumerResetOffsetDialog',
controller: 'consumerResetOffsetDialogController',
data:{
topic: topic,
selectedConsumerGroup:[],
allConsumerGroupList:resp.data.groupList
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.openSendTopicMessageDialog = function (topic) {
ngDialog.open({
template: 'sendTopicMessageDialog',
controller: 'sendTopicMessageDialogController',
data: {
topic: topic
}
});
};
$scope.openUpdateDialog = function (topic) {
$http({
method: "GET",
url: "topic/examineTopicConfig.query",
params:{
topic:topic
}
}).success(function (resp) {
if(resp.status ==0){
$scope.openCreateOrUpdateDialog(resp.data);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.openCreateOrUpdateDialog = function (request) {
var bIsUpdate = true;
if(request == null){
request = [{
writeQueueNums:16,
readQueueNums:16,
perm:6,
order:false,
topicName:"",
brokerNameList:[]
}];
bIsUpdate = false;
}
$http({
method: "GET",
url: "cluster/list.query"
}).success(function (resp) {
if(resp.status ==0){
console.log(resp);
ngDialog.open({
template: 'topicModifyDialog',
controller: 'topicModifyDialogController',
data:{
topicRequestList:request,
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
allBrokerNameList:Object.keys(resp.data.brokerServer),
bIsUpdate:bIsUpdate
}
});
}
});
}
$scope.openAddDialog = function () {
$scope.openCreateOrUpdateDialog(null);
}
}]);
module.controller('topicModifyDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.postTopicRequest = function (topicRequestItem) {
console.log(topicRequestItem);
var request = JSON.parse(JSON.stringify(topicRequestItem));
console.log(request);
$http({
method: "POST",
url: "topic/createOrUpdate.do",
data:request
}).success(function (resp) {
if(resp.status ==0){
Notification.info({message: "success!", delay: 2000});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
}
}]
);
module.controller('consumerResetOffsetDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
$scope.timepicker = {};
$scope.timepicker.date = moment().format('YYYY-MM-DD HH:mm');
$scope.timepicker.options = {format: 'YYYY-MM-DD HH:mm', showClear: true};
$scope.resetOffset = function () {
console.log($scope.timepicker.date);
console.log($scope.timepicker.date.valueOf());
console.log($scope.ngDialogData.selectedConsumerGroup);
$http({
method: "POST",
url: "consumer/resetOffset.do",
data: {
resetTime: $scope.timepicker.date.valueOf(),
consumerGroupList: $scope.ngDialogData.selectedConsumerGroup,
topic:$scope.ngDialogData.topic,
force:true
}
}).success(function (resp) {
if(resp.status ==0){
ngDialog.open({
template: 'resetOffsetResultDialog',
data:{
result:resp.data
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
})
}
}]
);
module.controller('sendTopicMessageDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.sendTopicMessage = {
topic: $scope.ngDialogData.topic,
key: "key",
tag:"tag",
messageBody:"messageBody"
};
$scope.send = function () {
$http({
method: "POST",
url: "topic/sendTopicMessage.do",
data: $scope.sendTopicMessage
}).success(function (resp) {
if(resp.status ==0){
ngDialog.open({
template: 'sendResultDialog',
data:{
result:resp.data
}
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
})
}
}]
);
module.controller('routerViewDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.deleteTopicByBroker = function (broker) {
$http({
method: "POST",
url: "topic/deleteTopicByBroker.do",
params: {brokerName:broker.brokerName,topic:$scope.ngDialogData.topic}
}).success(function (resp) {
if(resp.status ==0){
Notification.info({message: "delete success", delay: 2000});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
})
};
}]
);

View File

@@ -0,0 +1,32 @@
.view {
}
.view .ng-enter {
overflow-y: auto;
}
.view .ng-leave {
opacity: 0;
-webkit-transition: opacity .2s linear;
transition: opacity 0.2s linear;
}
.ng-hide-add {
transform: rotateZ(0);
transform-origin: right;
transition: all 0.2s ease-in-out;
}
.ng-hide-add.ng-hide-add-active {
transform: rotateZ(-135deg);
}
.ng-hide-remove {
transform: rotateY(90deg);
transform-origin: left;
transition: all 0.2s ease;
}
.ng-hide-remove.ng-hide-remove-active {
transform: rotateY(0);
}

View File

@@ -0,0 +1,274 @@
/* Write your styles */
.scrollTo {
position: fixed;
right: 50px;
bottom: 70px;
z-index: 999;
}
/*.icon_u_c{background:url(/app/img/icon_user_center.png) no-repeat left top;}*/
.icon {
float: left;
width: 24px;
height: 24px;
display: inline-block;
margin: 10px 18px 0 5px;
}
.icon_alipay {
background:url("/style/img/alipay.png") no-repeat left top;
height:100px;
width: 100px;
margin-top: -40px;
}
.icon_alipay_active {
background:url("/style/img/alipay_active.png") no-repeat left top;
height:100px;
width: 100px;
margin-top: -40px;
}
.icon_weipay {
background:url("/style/img/weipay.png") no-repeat left top;
height:100px;
width: 100px;
margin-top: -40px;
}
.icon_weipay_active {
background:url("/style/img/weipay_active.png") no-repeat left top;
height:100px;
width: 100px;
margin-top: -40px;
}
.round {
background:#5bc0de;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius:5px;
color: #FFFFFF;
}
/* ANIMATIONS
============================================================================= */
/* leaving animations ----------------------------------------- */
/* rotate and fall */
@keyframes rotateFall {
0% { transform: rotateZ(0deg); }
20% { transform: rotateZ(10deg); animation-timing-function: ease-out; }
40% { transform: rotateZ(17deg); }
60% { transform: rotateZ(16deg); }
100% { transform: translateY(100%) rotateZ(17deg); }
}
/* slide in from the bottom */
@keyframes slideOutLeft {
to { transform: translateX(-100%); }
}
/*!* rotate out newspaper *!*/
@keyframes rotateOutNewspaper {
to { transform: translateZ(-3000px) rotateZ(360deg); opacity: 0; }
}
/* entering animations --------------------------------------- */
/* scale up */
@keyframes scaleUp {
from { opacity: 0.3; -webkit-transform: scale(0.8); }
}
/* slide in from the right */
@keyframes slideInRight {
from { transform:translateX(100%); }
to { transform: translateX(0); }
}
/* slide in from the bottom */
@keyframes slideInUp {
from { transform:translateY(100%); }
to { transform: translateY(0); }
}
.ng-enter { animation: scaleUp 0.5s both ease-in; z-index: 8888; }
.ng-leave { animation: slideOutLeft 0.2s both ease-in; z-index: 9999; }
.managerList {
list-style:none;
position: relative;
width: 100%;
margin-left: 20px;
}
.managerList > li {
width: 95%;
margin-top:10px;
}
.managerList ul {
list-style-type:none;
float:left;
padding: 0;
}
.managerList li {
list-style-type:none;
float:left;
padding: 0;
}
.managerList .head {
width: 90%;
}
.managerList .footer {
width: 90%;
margin-top: 10px;
margin-left: 50px;
}
.managerList .checkbox {
width: 50px;
}
.managerList .liCheck {
width: 50px;
}
.managerList .liContent {
width: 90%;
}
.managerList .preview {
width: 50%;
margin-left: 50px;
margin-top: 10px;
}
.managerList .logo {
margin-top: 9px;
width: 50px;
}
.managerList .content{
width: 50%;
}
.managerList .content ul{
width: 90%;
}
.managerList .butt{
/*width: 20%;*/
}
.badgeCustomer {
background-color:#f44336;
margin-top:23px;
}
.chatPanel {
width: 90%;
float: left;
border: 1px solid #d4d4d4;
border-radius: 2px;
padding: 20px;
position: relative;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
float: right;
}
.chatPanel:after{
position: absolute;
top: 27px;
right: -14px;
display: inline-block;
border-top: 14px solid transparent;
border-left: 14px solid #fff;
border-right: 0 solid #fff;
border-bottom: 14px solid transparent;
content: " ";
}
.chatPanel:before{
position: absolute;
top: 26px;
right: -15px;
display: inline-block;
border-top: 15px solid transparent;
border-left: 15px solid #ccc;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
content: " ";
}
.chatPanel-left {
float: left;
}
.chatPanel-left:before {
border-left-width: 0;
border-right-width: 15px;
left: -15px;
right: auto;
}
.chatPanel-left:after {
border-left-width: 0;
border-right-width: 14px;
left: -14px;
right: auto;
}
.xxs-avatar {
width: 10px;
height: 10px;
}
.xs-avatar {
width: 50px;
height: 50px;
}
.md-avatar {
width: 70px;
height: 70px;
}
.lg-avatar {
width: 100px;
height: 100px;
}
.text-truncate {
word-wrap: normal;
/* for IE */
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.text-yellow {
color:#FFCC66;
}
.label-xl{
font-size:150%;
}
.pointer {
cursor: pointer;
}
.limit_height{
height: 600px;overflow-y:auto;overflow-x:hidden;
}
.table.text-middle>tbody>tr>td,.table.text-middle>tbody>tr>th{
vertical-align: middle;
}

View File

@@ -0,0 +1,752 @@
.btn,
.navbar .navbar-nav > li > a.btn {
border: none;
border-radius: 3px;
position: relative;
padding: 12px 30px;
margin: 10px 1px;
font-size: 12px;
font-weight: 400;
text-transform: uppercase;
letter-spacing: 0;
will-change: box-shadow, transform;
transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn::-moz-focus-inner,
.navbar .navbar-nav > li > a.btn::-moz-focus-inner {
border: 0;
}
.btn, .btn.btn-default,
.navbar .navbar-nav > li > a.btn,
.navbar .navbar-nav > li > a.btn.btn-default {
box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12);
}
.btn, .btn:hover, .btn:focus, .btn:active, .btn.active, .btn:active:focus, .btn:active:hover, .btn.active:focus, .btn.active:hover, .open > .btn.dropdown-toggle, .open > .btn.dropdown-toggle:focus, .open > .btn.dropdown-toggle:hover, .btn.btn-default, .btn.btn-default:hover, .btn.btn-default:focus, .btn.btn-default:active, .btn.btn-default.active, .btn.btn-default:active:focus, .btn.btn-default:active:hover, .btn.btn-default.active:focus, .btn.btn-default.active:hover, .open > .btn.btn-default.dropdown-toggle, .open > .btn.btn-default.dropdown-toggle:focus, .open > .btn.btn-default.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn,
.navbar .navbar-nav > li > a.btn:hover,
.navbar .navbar-nav > li > a.btn:focus,
.navbar .navbar-nav > li > a.btn:active,
.navbar .navbar-nav > li > a.btn.active,
.navbar .navbar-nav > li > a.btn:active:focus,
.navbar .navbar-nav > li > a.btn:active:hover,
.navbar .navbar-nav > li > a.btn.active:focus,
.navbar .navbar-nav > li > a.btn.active:hover, .open >
.navbar .navbar-nav > li > a.btn.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn.btn-default,
.navbar .navbar-nav > li > a.btn.btn-default:hover,
.navbar .navbar-nav > li > a.btn.btn-default:focus,
.navbar .navbar-nav > li > a.btn.btn-default:active,
.navbar .navbar-nav > li > a.btn.btn-default.active,
.navbar .navbar-nav > li > a.btn.btn-default:active:focus,
.navbar .navbar-nav > li > a.btn.btn-default:active:hover,
.navbar .navbar-nav > li > a.btn.btn-default.active:focus,
.navbar .navbar-nav > li > a.btn.btn-default.active:hover, .open >
.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle:hover {
background-color: #999999;
color: #FFFFFF;
}
.btn:focus, .btn:active, .btn:hover, .btn.btn-default:focus, .btn.btn-default:active, .btn.btn-default:hover,
.navbar .navbar-nav > li > a.btn:focus,
.navbar .navbar-nav > li > a.btn:active,
.navbar .navbar-nav > li > a.btn:hover,
.navbar .navbar-nav > li > a.btn.btn-default:focus,
.navbar .navbar-nav > li > a.btn.btn-default:active,
.navbar .navbar-nav > li > a.btn.btn-default:hover {
box-shadow: 0 14px 26px -12px rgba(153, 153, 153, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(153, 153, 153, 0.2);
}
.btn.disabled, .btn.disabled:hover, .btn.disabled:focus, .btn.disabled.focus, .btn.disabled:active, .btn.disabled.active, .btn:disabled, .btn:disabled:hover, .btn:disabled:focus, .btn:disabled.focus, .btn:disabled:active, .btn:disabled.active, .btn[disabled], .btn[disabled]:hover, .btn[disabled]:focus, .btn[disabled].focus, .btn[disabled]:active, .btn[disabled].active, fieldset[disabled] .btn, fieldset[disabled] .btn:hover, fieldset[disabled] .btn:focus, fieldset[disabled] .btn.focus, fieldset[disabled] .btn:active, fieldset[disabled] .btn.active, .btn.btn-default.disabled, .btn.btn-default.disabled:hover, .btn.btn-default.disabled:focus, .btn.btn-default.disabled.focus, .btn.btn-default.disabled:active, .btn.btn-default.disabled.active, .btn.btn-default:disabled, .btn.btn-default:disabled:hover, .btn.btn-default:disabled:focus, .btn.btn-default:disabled.focus, .btn.btn-default:disabled:active, .btn.btn-default:disabled.active, .btn.btn-default[disabled], .btn.btn-default[disabled]:hover, .btn.btn-default[disabled]:focus, .btn.btn-default[disabled].focus, .btn.btn-default[disabled]:active, .btn.btn-default[disabled].active, fieldset[disabled] .btn.btn-default, fieldset[disabled] .btn.btn-default:hover, fieldset[disabled] .btn.btn-default:focus, fieldset[disabled] .btn.btn-default.focus, fieldset[disabled] .btn.btn-default:active, fieldset[disabled] .btn.btn-default.active,
.navbar .navbar-nav > li > a.btn.disabled,
.navbar .navbar-nav > li > a.btn.disabled:hover,
.navbar .navbar-nav > li > a.btn.disabled:focus,
.navbar .navbar-nav > li > a.btn.disabled.focus,
.navbar .navbar-nav > li > a.btn.disabled:active,
.navbar .navbar-nav > li > a.btn.disabled.active,
.navbar .navbar-nav > li > a.btn:disabled,
.navbar .navbar-nav > li > a.btn:disabled:hover,
.navbar .navbar-nav > li > a.btn:disabled:focus,
.navbar .navbar-nav > li > a.btn:disabled.focus,
.navbar .navbar-nav > li > a.btn:disabled:active,
.navbar .navbar-nav > li > a.btn:disabled.active,
.navbar .navbar-nav > li > a.btn[disabled],
.navbar .navbar-nav > li > a.btn[disabled]:hover,
.navbar .navbar-nav > li > a.btn[disabled]:focus,
.navbar .navbar-nav > li > a.btn[disabled].focus,
.navbar .navbar-nav > li > a.btn[disabled]:active,
.navbar .navbar-nav > li > a.btn[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn, fieldset[disabled]
.navbar .navbar-nav > li > a.btn:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.active,
.navbar .navbar-nav > li > a.btn.btn-default.disabled,
.navbar .navbar-nav > li > a.btn.btn-default.disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-default.disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-default.disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-default.disabled:active,
.navbar .navbar-nav > li > a.btn.btn-default.disabled.active,
.navbar .navbar-nav > li > a.btn.btn-default:disabled,
.navbar .navbar-nav > li > a.btn.btn-default:disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-default:disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-default:disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-default:disabled:active,
.navbar .navbar-nav > li > a.btn.btn-default:disabled.active,
.navbar .navbar-nav > li > a.btn.btn-default[disabled],
.navbar .navbar-nav > li > a.btn.btn-default[disabled]:hover,
.navbar .navbar-nav > li > a.btn.btn-default[disabled]:focus,
.navbar .navbar-nav > li > a.btn.btn-default[disabled].focus,
.navbar .navbar-nav > li > a.btn.btn-default[disabled]:active,
.navbar .navbar-nav > li > a.btn.btn-default[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-default, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-default:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-default:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-default.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-default:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-default.active {
box-shadow: none;
}
.btn.btn-simple, .btn.btn-default.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple {
background-color: transparent;
color: #999999;
box-shadow: none;
}
.btn.btn-simple:hover, .btn.btn-simple:focus, .btn.btn-simple:active, .btn.btn-default.btn-simple:hover, .btn.btn-default.btn-simple:focus, .btn.btn-default.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:active {
background-color: transparent;
color: #999999;
}
.btn.btn-primary,
.navbar .navbar-nav > li > a.btn.btn-primary {
box-shadow: 0 2px 2px 0 rgba(156, 39, 176, 0.14), 0 3px 1px -2px rgba(156, 39, 176, 0.2), 0 1px 5px 0 rgba(156, 39, 176, 0.12);
}
.btn.btn-primary, .btn.btn-primary:hover, .btn.btn-primary:focus, .btn.btn-primary:active, .btn.btn-primary.active, .btn.btn-primary:active:focus, .btn.btn-primary:active:hover, .btn.btn-primary.active:focus, .btn.btn-primary.active:hover, .open > .btn.btn-primary.dropdown-toggle, .open > .btn.btn-primary.dropdown-toggle:focus, .open > .btn.btn-primary.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn.btn-primary,
.navbar .navbar-nav > li > a.btn.btn-primary:hover,
.navbar .navbar-nav > li > a.btn.btn-primary:focus,
.navbar .navbar-nav > li > a.btn.btn-primary:active,
.navbar .navbar-nav > li > a.btn.btn-primary.active,
.navbar .navbar-nav > li > a.btn.btn-primary:active:focus,
.navbar .navbar-nav > li > a.btn.btn-primary:active:hover,
.navbar .navbar-nav > li > a.btn.btn-primary.active:focus,
.navbar .navbar-nav > li > a.btn.btn-primary.active:hover, .open >
.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle:hover {
background-color: #9c27b0;
color: #FFFFFF;
}
.btn.btn-primary:focus, .btn.btn-primary:active, .btn.btn-primary:hover,
.navbar .navbar-nav > li > a.btn.btn-primary:focus,
.navbar .navbar-nav > li > a.btn.btn-primary:active,
.navbar .navbar-nav > li > a.btn.btn-primary:hover {
box-shadow: 0 14px 26px -12px rgba(156, 39, 176, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(156, 39, 176, 0.2);
}
.btn.btn-primary.disabled, .btn.btn-primary.disabled:hover, .btn.btn-primary.disabled:focus, .btn.btn-primary.disabled.focus, .btn.btn-primary.disabled:active, .btn.btn-primary.disabled.active, .btn.btn-primary:disabled, .btn.btn-primary:disabled:hover, .btn.btn-primary:disabled:focus, .btn.btn-primary:disabled.focus, .btn.btn-primary:disabled:active, .btn.btn-primary:disabled.active, .btn.btn-primary[disabled], .btn.btn-primary[disabled]:hover, .btn.btn-primary[disabled]:focus, .btn.btn-primary[disabled].focus, .btn.btn-primary[disabled]:active, .btn.btn-primary[disabled].active, fieldset[disabled] .btn.btn-primary, fieldset[disabled] .btn.btn-primary:hover, fieldset[disabled] .btn.btn-primary:focus, fieldset[disabled] .btn.btn-primary.focus, fieldset[disabled] .btn.btn-primary:active, fieldset[disabled] .btn.btn-primary.active,
.navbar .navbar-nav > li > a.btn.btn-primary.disabled,
.navbar .navbar-nav > li > a.btn.btn-primary.disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-primary.disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-primary.disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-primary.disabled:active,
.navbar .navbar-nav > li > a.btn.btn-primary.disabled.active,
.navbar .navbar-nav > li > a.btn.btn-primary:disabled,
.navbar .navbar-nav > li > a.btn.btn-primary:disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-primary:disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-primary:disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-primary:disabled:active,
.navbar .navbar-nav > li > a.btn.btn-primary:disabled.active,
.navbar .navbar-nav > li > a.btn.btn-primary[disabled],
.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:hover,
.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:focus,
.navbar .navbar-nav > li > a.btn.btn-primary[disabled].focus,
.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:active,
.navbar .navbar-nav > li > a.btn.btn-primary[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-primary, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-primary:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-primary:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-primary.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-primary:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-primary.active {
box-shadow: none;
}
.btn.btn-primary.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple {
background-color: transparent;
color: #9c27b0;
box-shadow: none;
}
.btn.btn-primary.btn-simple:hover, .btn.btn-primary.btn-simple:focus, .btn.btn-primary.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:active {
background-color: transparent;
color: #9c27b0;
}
.btn.btn-info,
.navbar .navbar-nav > li > a.btn.btn-info {
box-shadow: 0 2px 2px 0 rgba(0, 188, 212, 0.14), 0 3px 1px -2px rgba(0, 188, 212, 0.2), 0 1px 5px 0 rgba(0, 188, 212, 0.12);
}
.btn.btn-info, .btn.btn-info:hover, .btn.btn-info:focus, .btn.btn-info:active, .btn.btn-info.active, .btn.btn-info:active:focus, .btn.btn-info:active:hover, .btn.btn-info.active:focus, .btn.btn-info.active:hover, .open > .btn.btn-info.dropdown-toggle, .open > .btn.btn-info.dropdown-toggle:focus, .open > .btn.btn-info.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn.btn-info,
.navbar .navbar-nav > li > a.btn.btn-info:hover,
.navbar .navbar-nav > li > a.btn.btn-info:focus,
.navbar .navbar-nav > li > a.btn.btn-info:active,
.navbar .navbar-nav > li > a.btn.btn-info.active,
.navbar .navbar-nav > li > a.btn.btn-info:active:focus,
.navbar .navbar-nav > li > a.btn.btn-info:active:hover,
.navbar .navbar-nav > li > a.btn.btn-info.active:focus,
.navbar .navbar-nav > li > a.btn.btn-info.active:hover, .open >
.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle:hover {
background-color: #00bcd4;
color: #FFFFFF;
}
.btn.btn-info:focus, .btn.btn-info:active, .btn.btn-info:hover,
.navbar .navbar-nav > li > a.btn.btn-info:focus,
.navbar .navbar-nav > li > a.btn.btn-info:active,
.navbar .navbar-nav > li > a.btn.btn-info:hover {
box-shadow: 0 14px 26px -12px rgba(0, 188, 212, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 188, 212, 0.2);
}
.btn.btn-info.disabled, .btn.btn-info.disabled:hover, .btn.btn-info.disabled:focus, .btn.btn-info.disabled.focus, .btn.btn-info.disabled:active, .btn.btn-info.disabled.active, .btn.btn-info:disabled, .btn.btn-info:disabled:hover, .btn.btn-info:disabled:focus, .btn.btn-info:disabled.focus, .btn.btn-info:disabled:active, .btn.btn-info:disabled.active, .btn.btn-info[disabled], .btn.btn-info[disabled]:hover, .btn.btn-info[disabled]:focus, .btn.btn-info[disabled].focus, .btn.btn-info[disabled]:active, .btn.btn-info[disabled].active, fieldset[disabled] .btn.btn-info, fieldset[disabled] .btn.btn-info:hover, fieldset[disabled] .btn.btn-info:focus, fieldset[disabled] .btn.btn-info.focus, fieldset[disabled] .btn.btn-info:active, fieldset[disabled] .btn.btn-info.active,
.navbar .navbar-nav > li > a.btn.btn-info.disabled,
.navbar .navbar-nav > li > a.btn.btn-info.disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-info.disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-info.disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-info.disabled:active,
.navbar .navbar-nav > li > a.btn.btn-info.disabled.active,
.navbar .navbar-nav > li > a.btn.btn-info:disabled,
.navbar .navbar-nav > li > a.btn.btn-info:disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-info:disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-info:disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-info:disabled:active,
.navbar .navbar-nav > li > a.btn.btn-info:disabled.active,
.navbar .navbar-nav > li > a.btn.btn-info[disabled],
.navbar .navbar-nav > li > a.btn.btn-info[disabled]:hover,
.navbar .navbar-nav > li > a.btn.btn-info[disabled]:focus,
.navbar .navbar-nav > li > a.btn.btn-info[disabled].focus,
.navbar .navbar-nav > li > a.btn.btn-info[disabled]:active,
.navbar .navbar-nav > li > a.btn.btn-info[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-info, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-info:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-info:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-info.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-info:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-info.active {
box-shadow: none;
}
.btn.btn-info.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple {
background-color: transparent;
color: #00bcd4;
box-shadow: none;
}
.btn.btn-info.btn-simple:hover, .btn.btn-info.btn-simple:focus, .btn.btn-info.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:active {
background-color: transparent;
color: #00bcd4;
}
.btn.btn-success,
.navbar .navbar-nav > li > a.btn.btn-success {
box-shadow: 0 2px 2px 0 rgba(76, 175, 80, 0.14), 0 3px 1px -2px rgba(76, 175, 80, 0.2), 0 1px 5px 0 rgba(76, 175, 80, 0.12);
}
.btn.btn-success, .btn.btn-success:hover, .btn.btn-success:focus, .btn.btn-success:active, .btn.btn-success.active, .btn.btn-success:active:focus, .btn.btn-success:active:hover, .btn.btn-success.active:focus, .btn.btn-success.active:hover, .open > .btn.btn-success.dropdown-toggle, .open > .btn.btn-success.dropdown-toggle:focus, .open > .btn.btn-success.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn.btn-success,
.navbar .navbar-nav > li > a.btn.btn-success:hover,
.navbar .navbar-nav > li > a.btn.btn-success:focus,
.navbar .navbar-nav > li > a.btn.btn-success:active,
.navbar .navbar-nav > li > a.btn.btn-success.active,
.navbar .navbar-nav > li > a.btn.btn-success:active:focus,
.navbar .navbar-nav > li > a.btn.btn-success:active:hover,
.navbar .navbar-nav > li > a.btn.btn-success.active:focus,
.navbar .navbar-nav > li > a.btn.btn-success.active:hover, .open >
.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle:hover {
background-color: #4caf50;
color: #FFFFFF;
}
.btn.btn-success:focus, .btn.btn-success:active, .btn.btn-success:hover,
.navbar .navbar-nav > li > a.btn.btn-success:focus,
.navbar .navbar-nav > li > a.btn.btn-success:active,
.navbar .navbar-nav > li > a.btn.btn-success:hover {
box-shadow: 0 14px 26px -12px rgba(76, 175, 80, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(76, 175, 80, 0.2);
}
.btn.btn-success.disabled, .btn.btn-success.disabled:hover, .btn.btn-success.disabled:focus, .btn.btn-success.disabled.focus, .btn.btn-success.disabled:active, .btn.btn-success.disabled.active, .btn.btn-success:disabled, .btn.btn-success:disabled:hover, .btn.btn-success:disabled:focus, .btn.btn-success:disabled.focus, .btn.btn-success:disabled:active, .btn.btn-success:disabled.active, .btn.btn-success[disabled], .btn.btn-success[disabled]:hover, .btn.btn-success[disabled]:focus, .btn.btn-success[disabled].focus, .btn.btn-success[disabled]:active, .btn.btn-success[disabled].active, fieldset[disabled] .btn.btn-success, fieldset[disabled] .btn.btn-success:hover, fieldset[disabled] .btn.btn-success:focus, fieldset[disabled] .btn.btn-success.focus, fieldset[disabled] .btn.btn-success:active, fieldset[disabled] .btn.btn-success.active,
.navbar .navbar-nav > li > a.btn.btn-success.disabled,
.navbar .navbar-nav > li > a.btn.btn-success.disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-success.disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-success.disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-success.disabled:active,
.navbar .navbar-nav > li > a.btn.btn-success.disabled.active,
.navbar .navbar-nav > li > a.btn.btn-success:disabled,
.navbar .navbar-nav > li > a.btn.btn-success:disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-success:disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-success:disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-success:disabled:active,
.navbar .navbar-nav > li > a.btn.btn-success:disabled.active,
.navbar .navbar-nav > li > a.btn.btn-success[disabled],
.navbar .navbar-nav > li > a.btn.btn-success[disabled]:hover,
.navbar .navbar-nav > li > a.btn.btn-success[disabled]:focus,
.navbar .navbar-nav > li > a.btn.btn-success[disabled].focus,
.navbar .navbar-nav > li > a.btn.btn-success[disabled]:active,
.navbar .navbar-nav > li > a.btn.btn-success[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-success, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-success:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-success:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-success.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-success:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-success.active {
box-shadow: none;
}
.btn.btn-success.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple {
background-color: transparent;
color: #4caf50;
box-shadow: none;
}
.btn.btn-success.btn-simple:hover, .btn.btn-success.btn-simple:focus, .btn.btn-success.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:active {
background-color: transparent;
color: #4caf50;
}
.btn.btn-warning,
.navbar .navbar-nav > li > a.btn.btn-warning {
box-shadow: 0 2px 2px 0 rgba(255, 152, 0, 0.14), 0 3px 1px -2px rgba(255, 152, 0, 0.2), 0 1px 5px 0 rgba(255, 152, 0, 0.12);
}
.btn.btn-warning, .btn.btn-warning:hover, .btn.btn-warning:focus, .btn.btn-warning:active, .btn.btn-warning.active, .btn.btn-warning:active:focus, .btn.btn-warning:active:hover, .btn.btn-warning.active:focus, .btn.btn-warning.active:hover, .open > .btn.btn-warning.dropdown-toggle, .open > .btn.btn-warning.dropdown-toggle:focus, .open > .btn.btn-warning.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn.btn-warning,
.navbar .navbar-nav > li > a.btn.btn-warning:hover,
.navbar .navbar-nav > li > a.btn.btn-warning:focus,
.navbar .navbar-nav > li > a.btn.btn-warning:active,
.navbar .navbar-nav > li > a.btn.btn-warning.active,
.navbar .navbar-nav > li > a.btn.btn-warning:active:focus,
.navbar .navbar-nav > li > a.btn.btn-warning:active:hover,
.navbar .navbar-nav > li > a.btn.btn-warning.active:focus,
.navbar .navbar-nav > li > a.btn.btn-warning.active:hover, .open >
.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle:hover {
background-color: #ff9800;
color: #FFFFFF;
}
.btn.btn-warning:focus, .btn.btn-warning:active, .btn.btn-warning:hover,
.navbar .navbar-nav > li > a.btn.btn-warning:focus,
.navbar .navbar-nav > li > a.btn.btn-warning:active,
.navbar .navbar-nav > li > a.btn.btn-warning:hover {
box-shadow: 0 14px 26px -12px rgba(255, 152, 0, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(255, 152, 0, 0.2);
}
.btn.btn-warning.disabled, .btn.btn-warning.disabled:hover, .btn.btn-warning.disabled:focus, .btn.btn-warning.disabled.focus, .btn.btn-warning.disabled:active, .btn.btn-warning.disabled.active, .btn.btn-warning:disabled, .btn.btn-warning:disabled:hover, .btn.btn-warning:disabled:focus, .btn.btn-warning:disabled.focus, .btn.btn-warning:disabled:active, .btn.btn-warning:disabled.active, .btn.btn-warning[disabled], .btn.btn-warning[disabled]:hover, .btn.btn-warning[disabled]:focus, .btn.btn-warning[disabled].focus, .btn.btn-warning[disabled]:active, .btn.btn-warning[disabled].active, fieldset[disabled] .btn.btn-warning, fieldset[disabled] .btn.btn-warning:hover, fieldset[disabled] .btn.btn-warning:focus, fieldset[disabled] .btn.btn-warning.focus, fieldset[disabled] .btn.btn-warning:active, fieldset[disabled] .btn.btn-warning.active,
.navbar .navbar-nav > li > a.btn.btn-warning.disabled,
.navbar .navbar-nav > li > a.btn.btn-warning.disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-warning.disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-warning.disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-warning.disabled:active,
.navbar .navbar-nav > li > a.btn.btn-warning.disabled.active,
.navbar .navbar-nav > li > a.btn.btn-warning:disabled,
.navbar .navbar-nav > li > a.btn.btn-warning:disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-warning:disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-warning:disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-warning:disabled:active,
.navbar .navbar-nav > li > a.btn.btn-warning:disabled.active,
.navbar .navbar-nav > li > a.btn.btn-warning[disabled],
.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:hover,
.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:focus,
.navbar .navbar-nav > li > a.btn.btn-warning[disabled].focus,
.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:active,
.navbar .navbar-nav > li > a.btn.btn-warning[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-warning, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-warning:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-warning:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-warning.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-warning:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-warning.active {
box-shadow: none;
}
.btn.btn-warning.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple {
background-color: transparent;
color: #ff9800;
box-shadow: none;
}
.btn.btn-warning.btn-simple:hover, .btn.btn-warning.btn-simple:focus, .btn.btn-warning.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:active {
background-color: transparent;
color: #ff9800;
}
.btn.btn-danger,
.navbar .navbar-nav > li > a.btn.btn-danger {
box-shadow: 0 2px 2px 0 rgba(244, 67, 54, 0.14), 0 3px 1px -2px rgba(244, 67, 54, 0.2), 0 1px 5px 0 rgba(244, 67, 54, 0.12);
}
.btn.btn-danger, .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active, .btn.btn-danger.active, .btn.btn-danger:active:focus, .btn.btn-danger:active:hover, .btn.btn-danger.active:focus, .btn.btn-danger.active:hover, .open > .btn.btn-danger.dropdown-toggle, .open > .btn.btn-danger.dropdown-toggle:focus, .open > .btn.btn-danger.dropdown-toggle:hover,
.navbar .navbar-nav > li > a.btn.btn-danger,
.navbar .navbar-nav > li > a.btn.btn-danger:hover,
.navbar .navbar-nav > li > a.btn.btn-danger:focus,
.navbar .navbar-nav > li > a.btn.btn-danger:active,
.navbar .navbar-nav > li > a.btn.btn-danger.active,
.navbar .navbar-nav > li > a.btn.btn-danger:active:focus,
.navbar .navbar-nav > li > a.btn.btn-danger:active:hover,
.navbar .navbar-nav > li > a.btn.btn-danger.active:focus,
.navbar .navbar-nav > li > a.btn.btn-danger.active:hover, .open >
.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle, .open >
.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle:focus, .open >
.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle:hover {
background-color: #f44336;
color: #FFFFFF;
}
.btn.btn-danger:focus, .btn.btn-danger:active, .btn.btn-danger:hover,
.navbar .navbar-nav > li > a.btn.btn-danger:focus,
.navbar .navbar-nav > li > a.btn.btn-danger:active,
.navbar .navbar-nav > li > a.btn.btn-danger:hover {
box-shadow: 0 14px 26px -12px rgba(244, 67, 54, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(244, 67, 54, 0.2);
}
.btn.btn-danger.disabled, .btn.btn-danger.disabled:hover, .btn.btn-danger.disabled:focus, .btn.btn-danger.disabled.focus, .btn.btn-danger.disabled:active, .btn.btn-danger.disabled.active, .btn.btn-danger:disabled, .btn.btn-danger:disabled:hover, .btn.btn-danger:disabled:focus, .btn.btn-danger:disabled.focus, .btn.btn-danger:disabled:active, .btn.btn-danger:disabled.active, .btn.btn-danger[disabled], .btn.btn-danger[disabled]:hover, .btn.btn-danger[disabled]:focus, .btn.btn-danger[disabled].focus, .btn.btn-danger[disabled]:active, .btn.btn-danger[disabled].active, fieldset[disabled] .btn.btn-danger, fieldset[disabled] .btn.btn-danger:hover, fieldset[disabled] .btn.btn-danger:focus, fieldset[disabled] .btn.btn-danger.focus, fieldset[disabled] .btn.btn-danger:active, fieldset[disabled] .btn.btn-danger.active,
.navbar .navbar-nav > li > a.btn.btn-danger.disabled,
.navbar .navbar-nav > li > a.btn.btn-danger.disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-danger.disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-danger.disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-danger.disabled:active,
.navbar .navbar-nav > li > a.btn.btn-danger.disabled.active,
.navbar .navbar-nav > li > a.btn.btn-danger:disabled,
.navbar .navbar-nav > li > a.btn.btn-danger:disabled:hover,
.navbar .navbar-nav > li > a.btn.btn-danger:disabled:focus,
.navbar .navbar-nav > li > a.btn.btn-danger:disabled.focus,
.navbar .navbar-nav > li > a.btn.btn-danger:disabled:active,
.navbar .navbar-nav > li > a.btn.btn-danger:disabled.active,
.navbar .navbar-nav > li > a.btn.btn-danger[disabled],
.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:hover,
.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:focus,
.navbar .navbar-nav > li > a.btn.btn-danger[disabled].focus,
.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:active,
.navbar .navbar-nav > li > a.btn.btn-danger[disabled].active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-danger, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-danger:hover, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-danger:focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-danger.focus, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-danger:active, fieldset[disabled]
.navbar .navbar-nav > li > a.btn.btn-danger.active {
box-shadow: none;
}
.btn.btn-danger.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple {
background-color: transparent;
color: #f44336;
box-shadow: none;
}
.btn.btn-danger.btn-simple:hover, .btn.btn-danger.btn-simple:focus, .btn.btn-danger.btn-simple:active,
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:hover,
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:focus,
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:active {
background-color: transparent;
color: #f44336;
}
.btn.btn-white, .btn.btn-white:focus, .btn.btn-white:hover,
.navbar .navbar-nav > li > a.btn.btn-white,
.navbar .navbar-nav > li > a.btn.btn-white:focus,
.navbar .navbar-nav > li > a.btn.btn-white:hover {
background-color: #FFFFFF;
color: #999999;
}
.btn.btn-white.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-white.btn-simple {
color: #FFFFFF;
background: transparent;
box-shadow: none;
}
.btn.btn-facebook,
.navbar .navbar-nav > li > a.btn.btn-facebook {
background-color: #3b5998;
color: #fff;
box-shadow: 0 2px 2px 0 rgba(59, 89, 152, 0.14), 0 3px 1px -2px rgba(59, 89, 152, 0.2), 0 1px 5px 0 rgba(59, 89, 152, 0.12);
}
.btn.btn-facebook:focus, .btn.btn-facebook:active, .btn.btn-facebook:hover,
.navbar .navbar-nav > li > a.btn.btn-facebook:focus,
.navbar .navbar-nav > li > a.btn.btn-facebook:active,
.navbar .navbar-nav > li > a.btn.btn-facebook:hover {
background-color: #3b5998;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(59, 89, 152, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(59, 89, 152, 0.2);
}
.btn.btn-facebook.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-facebook.btn-simple {
color: #3b5998;
background-color: transparent;
box-shadow: none;
}
.btn.btn-twitter,
.navbar .navbar-nav > li > a.btn.btn-twitter {
background-color: #55acee;
color: #fff;
box-shadow: 0 2px 2px 0 rgba(85, 172, 238, 0.14), 0 3px 1px -2px rgba(85, 172, 238, 0.2), 0 1px 5px 0 rgba(85, 172, 238, 0.12);
}
.btn.btn-twitter:focus, .btn.btn-twitter:active, .btn.btn-twitter:hover,
.navbar .navbar-nav > li > a.btn.btn-twitter:focus,
.navbar .navbar-nav > li > a.btn.btn-twitter:active,
.navbar .navbar-nav > li > a.btn.btn-twitter:hover {
background-color: #55acee;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(85, 172, 238, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(85, 172, 238, 0.2);
}
.btn.btn-twitter.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-twitter.btn-simple {
color: #55acee;
background-color: transparent;
box-shadow: none;
}
.btn.btn-pinterest,
.navbar .navbar-nav > li > a.btn.btn-pinterest {
background-color: #cc2127;
color: #fff;
box-shadow: 0 2px 2px 0 rgba(204, 33, 39, 0.14), 0 3px 1px -2px rgba(204, 33, 39, 0.2), 0 1px 5px 0 rgba(204, 33, 39, 0.12);
}
.btn.btn-pinterest:focus, .btn.btn-pinterest:active, .btn.btn-pinterest:hover,
.navbar .navbar-nav > li > a.btn.btn-pinterest:focus,
.navbar .navbar-nav > li > a.btn.btn-pinterest:active,
.navbar .navbar-nav > li > a.btn.btn-pinterest:hover {
background-color: #cc2127;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(204, 33, 39, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(204, 33, 39, 0.2);
}
.btn.btn-pinterest.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-pinterest.btn-simple {
color: #cc2127;
background-color: transparent;
box-shadow: none;
}
.btn.btn-google,
.navbar .navbar-nav > li > a.btn.btn-google {
background-color: #dd4b39;
color: #fff;
box-shadow: 0 2px 2px 0 rgba(221, 75, 57, 0.14), 0 3px 1px -2px rgba(221, 75, 57, 0.2), 0 1px 5px 0 rgba(221, 75, 57, 0.12);
}
.btn.btn-google:focus, .btn.btn-google:active, .btn.btn-google:hover,
.navbar .navbar-nav > li > a.btn.btn-google:focus,
.navbar .navbar-nav > li > a.btn.btn-google:active,
.navbar .navbar-nav > li > a.btn.btn-google:hover {
background-color: #dd4b39;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(221, 75, 57, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(221, 75, 57, 0.2);
}
.btn.btn-google.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-google.btn-simple {
color: #dd4b39;
background-color: transparent;
box-shadow: none;
}
.btn.btn-instagram,
.navbar .navbar-nav > li > a.btn.btn-instagram {
background-color: #125688;
color: #fff;
box-shadow: 0 2px 2px 0 rgba(18, 86, 136, 0.14), 0 3px 1px -2px rgba(18, 86, 136, 0.2), 0 1px 5px 0 rgba(18, 86, 136, 0.12);
}
.btn.btn-instagram:focus, .btn.btn-instagram:active, .btn.btn-instagram:hover,
.navbar .navbar-nav > li > a.btn.btn-instagram:focus,
.navbar .navbar-nav > li > a.btn.btn-instagram:active,
.navbar .navbar-nav > li > a.btn.btn-instagram:hover {
background-color: #125688;
color: #fff;
box-shadow: 0 14px 26px -12px rgba(18, 86, 136, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(18, 86, 136, 0.2);
}
.btn.btn-instagram.btn-simple,
.navbar .navbar-nav > li > a.btn.btn-instagram.btn-simple {
color: #125688;
background-color: transparent;
box-shadow: none;
}
.btn:focus, .btn:active, .btn:active:focus,
.navbar .navbar-nav > li > a.btn:focus,
.navbar .navbar-nav > li > a.btn:active,
.navbar .navbar-nav > li > a.btn:active:focus {
outline: 0;
}
.btn.btn-round,
.navbar .navbar-nav > li > a.btn.btn-round {
border-radius: 30px;
}
.btn:not(.btn-just-icon):not(.btn-fab) .fa,
.navbar .navbar-nav > li > a.btn:not(.btn-just-icon):not(.btn-fab) .fa {
font-size: 18px;
margin-top: -2px;
position: relative;
top: 2px;
}
.btn.btn-fab,
.navbar .navbar-nav > li > a.btn.btn-fab {
border-radius: 50%;
font-size: 24px;
height: 56px;
margin: auto;
min-width: 56px;
width: 56px;
padding: 0;
overflow: hidden;
position: relative;
line-height: normal;
}
.btn.btn-fab .ripple-container,
.navbar .navbar-nav > li > a.btn.btn-fab .ripple-container {
border-radius: 50%;
}
.btn.btn-fab.btn-fab-mini, .btn-group-sm .btn.btn-fab,
.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini, .btn-group-sm
.navbar .navbar-nav > li > a.btn.btn-fab {
height: 40px;
min-width: 40px;
width: 40px;
}
.btn.btn-fab.btn-fab-mini.material-icons, .btn-group-sm .btn.btn-fab.material-icons,
.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini.material-icons, .btn-group-sm
.navbar .navbar-nav > li > a.btn.btn-fab.material-icons {
top: -3.5px;
left: -3.5px;
}
.btn.btn-fab.btn-fab-mini .material-icons, .btn-group-sm .btn.btn-fab .material-icons,
.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini .material-icons, .btn-group-sm
.navbar .navbar-nav > li > a.btn.btn-fab .material-icons {
font-size: 17px;
}
.btn.btn-fab i.material-icons,
.navbar .navbar-nav > li > a.btn.btn-fab i.material-icons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-12px, -12px);
line-height: 24px;
width: 24px;
font-size: 24px;
}
.btn.btn-lg, .btn-group-lg .btn,
.navbar .navbar-nav > li > a.btn.btn-lg, .btn-group-lg
.navbar .navbar-nav > li > a.btn {
font-size: 14px;
padding: 18px 36px;
}
.btn.btn-sm, .btn-group-sm .btn,
.navbar .navbar-nav > li > a.btn.btn-sm, .btn-group-sm
.navbar .navbar-nav > li > a.btn {
padding: 5px 20px;
font-size: 11px;
}
.btn.btn-xs, .btn-group-xs .btn,
.navbar .navbar-nav > li > a.btn.btn-xs, .btn-group-xs
.navbar .navbar-nav > li > a.btn {
padding: 4px 15px;
font-size: 10px;
}
.btn.btn-just-icon,
.navbar .navbar-nav > li > a.btn.btn-just-icon {
font-size: 20px;
padding: 12px 12px;
line-height: 1em;
}
.btn.btn-just-icon i,
.navbar .navbar-nav > li > a.btn.btn-just-icon i {
width: 20px;
}
.btn.btn-just-icon.btn-lg,
.navbar .navbar-nav > li > a.btn.btn-just-icon.btn-lg {
font-size: 22px;
padding: 13px 18px;
}
.btn .material-icons {
vertical-align: middle;
font-size: 17px;
top: -1px;
position: relative;
}
.navbar .navbar-nav > li > a.btn {
margin-top: 2px;
margin-bottom: 2px;
}
.navbar .navbar-nav > li > a.btn.btn-fab {
margin: 5px 2px;
}
.navbar .navbar-nav > li > a:not(.btn) .material-icons {
margin-top: -3px;
top: 0px;
position: relative;
margin-right: 3px;
}
.navbar .navbar-nav > li > .profile-photo {
margin: 5px 2px;
}
.navbar-default:not(.navbar-transparent) .navbar-nav > li > a.btn.btn-white.btn-simple {
color: #555555;
}
.btn-group,
.btn-group-vertical {
position: relative;
margin: 10px 1px;
}
.btn-group.open > .dropdown-toggle.btn, .btn-group.open > .dropdown-toggle.btn.btn-default,
.btn-group-vertical.open > .dropdown-toggle.btn,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-default {
background-color: #FFFFFF;
}
.btn-group.open > .dropdown-toggle.btn.btn-inverse,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-inverse {
background-color: #212121;
}
.btn-group.open > .dropdown-toggle.btn.btn-primary,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-primary {
background-color: #9c27b0;
}
.btn-group.open > .dropdown-toggle.btn.btn-success,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-success {
background-color: #4caf50;
}
.btn-group.open > .dropdown-toggle.btn.btn-info,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-info {
background-color: #00bcd4;
}
.btn-group.open > .dropdown-toggle.btn.btn-warning,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-warning {
background-color: #ff9800;
}
.btn-group.open > .dropdown-toggle.btn.btn-danger,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-danger {
background-color: #f44336;
}
.btn-group.open > .dropdown-toggle.btn.btn-rose,
.btn-group-vertical.open > .dropdown-toggle.btn.btn-rose {
background-color: #e91e63;
}
.btn-group .dropdown-menu,
.btn-group-vertical .dropdown-menu {
border-radius: 0 0 3px 3px;
}
.btn-group.btn-group-raised,
.btn-group-vertical.btn-group-raised {
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.btn-group .btn + .btn,
.btn-group .btn,
.btn-group .btn:active,
.btn-group .btn-group,
.btn-group-vertical .btn + .btn,
.btn-group-vertical .btn,
.btn-group-vertical .btn:active,
.btn-group-vertical .btn-group {
margin: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,12 @@
.login-panel{
}
.login-panel .qrcode {
width: 100%;
hight: 100%;
}
.login-panel .validateCode {
/*height:20px;*/
}

View File

@@ -0,0 +1,420 @@
/*
_____ _ _ _ _
|_ _| | | | | | | | |
| | | |__ __ _| |_ ___ | |_ ___ _ __ ___ __ _| |_ ___ ___ ___
| | | '_ \ / _` | __/ _ \ | __/ _ \| '_ ` _ \ / _` | __/ _ \ / _ \/ __|
_| |_ | | | | (_| | || __/ | || (_) | | | | | | (_| | || (_) | __/\__ \
\___/ |_| |_|\__,_|\__\___| \__\___/|_| |_| |_|\__,_|\__\___/ \___||___/
Oh nice, welcome to the stylesheet of dreams.
It has it all. Classes, ID's, comments...the whole lot:)
Enjoy responsibly!
@ihatetomatoes
*/
/* ==========================================================================
Chrome Frame prompt
========================================================================== */
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
/* ==========================================================================
Author's custom styles
========================================================================== */
p {
line-height: 1.33em;
color: #7E7E7E;
}
h1 {
color: #EEEEEE;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #3498db;
-webkit-animation: spin 2s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
animation: spin 2s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
z-index: 1001;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #e74c3c;
-webkit-animation: spin 3s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
animation: spin 3s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #f9c922;
-webkit-animation: spin 1.5s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
animation: spin 1.5s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg); /* IE 9 */
transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg); /* IE 9 */
transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg); /* IE 9 */
transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg); /* IE 9 */
transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */
}
}
#loader-wrapper .loader-section {
position: fixed;
top: 0;
width: 51%;
height: 100%;
background: #222222;
z-index: 1000;
-webkit-transform: translateX(0); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(0); /* IE 9 */
transform: translateX(0); /* Firefox 16+, IE 10+, Opera */
}
#loader-wrapper .loader-section.section-left {
left: 0;
}
#loader-wrapper .loader-section.section-right {
right: 0;
}
/* Loaded */
.loaded #loader-wrapper .loader-section.section-left {
-webkit-transform: translateX(-100%); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(-100%); /* IE 9 */
transform: translateX(-100%); /* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader-wrapper .loader-section.section-right {
-webkit-transform: translateX(100%); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(100%); /* IE 9 */
transform: translateX(100%); /* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
}
.loaded #loader {
opacity: 0;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.loaded #loader-wrapper {
visibility: hidden;
-webkit-transform: translateY(-100%); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateY(-100%); /* IE 9 */
transform: translateY(-100%); /* Firefox 16+, IE 10+, Opera */
-webkit-transition: all 0.3s 1s ease-out;
transition: all 0.3s 1s ease-out;
}
/* JavaScript Turned Off */
.no-js #loader-wrapper {
display: none;
}
.no-js h1 {
color: #222222;
}
#content {
margin: 0 auto;
padding-bottom: 50px;
width: 80%;
max-width: 978px;
}
/* ==========================================================================
Helper classes
========================================================================== */
/*
* Image replacement
*/
.ir {
background-color: transparent;
border: 0;
overflow: hidden;
/* IE 6/7 fallback */
*text-indent: -9999px;
}
.ir:before {
content: "";
display: block;
width: 0;
height: 150%;
}
/*
* Hide from both screenreaders and browsers: h5bp.com/u
*/
.hidden {
display: none !important;
visibility: hidden;
}
/*
* Hide only visually, but have it available for screenreaders: h5bp.com/v
*/
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
/*
* Extends the .visuallyhidden class to allow the element to be focusable
* when navigated to via the keyboard: h5bp.com/p
*/
.visuallyhidden.focusable:active,
.visuallyhidden.focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto;
}
/*
* Hide visually and from screenreaders, but maintain layout
*/
.invisible {
visibility: hidden;
}
/*
* Clearfix: contain floats
*
* For modern browsers
* 1. The space content is one way to avoid an Opera bug when the
* `contenteditable` attribute is included anywhere else in the document.
* Otherwise it causes space to appear at the top and bottom of elements
* that receive the `clearfix` class.
* 2. The use of `table` rather than `block` is only necessary if using
* `:before` to contain the top-margins of child elements.
*/
.clearfix:before,
.clearfix:after {
content: " "; /* 1 */
display: table; /* 2 */
}
.clearfix:after {
clear: both;
}
/*
* For IE 6/7 only
* Include this rule to trigger hasLayout and contain floats.
*/
.clearfix {
*zoom: 1;
}
/* ==========================================================================
EXAMPLE Media Queries for Responsive Design.
These examples override the primary ('mobile first') styles.
Modify as content requires.
========================================================================== */
@media only screen and (min-width: 35em) {
/* Style adjustments for viewports that meet the condition */
}
@media print,
(-o-min-device-pixel-ratio: 5/4),
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {
/* Style adjustments for high resolution devices */
}
/* ==========================================================================
Print styles.
Inlined to avoid required HTTP connection: h5bp.com/r
========================================================================== */
@media print {
* {
background: transparent !important;
color: #000 !important; /* Black prints faster: h5bp.com/s */
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
/*
* Don't show links for images, or javascript/internal links
*/
.ir a:after,
a[href^="javascript:"]:after,
a[href^="#"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group; /* h5bp.com/t */
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
@page {
margin: 0.5cm;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}
.back-link a {
color: #4ca340;
text-decoration: none;
border-bottom: 1px #4ca340 solid;
}
.back-link a:hover,
.back-link a:focus {
color: #408536;
text-decoration: none;
border-bottom: 1px #408536 solid;
}
h1 {
height: 100%;
/* The html and body elements cannot have any padding or margin. */
margin: 0;
font-size: 14px;
font-family: 'Open Sans', sans-serif;
font-size: 32px;
margin-bottom: 3px;
}
.entry-header {
text-align: left;
margin: 0 auto 50px auto;
width: 80%;
max-width: 978px;
position: relative;
z-index: 10001;
}
#demo-content {
padding-top: 100px;
}
/*
Ok so you have made it this far, that means you are very keen to on my code.
Anyway I don't really mind it. This is a great way to learn so you actually doing the right thing:)
Follow me @ihatetomatoes
*/

View File

@@ -0,0 +1,527 @@
/*! normalize.css v1.1.2 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
/**
* Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/**
* 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Address `font-family` inconsistency between `textarea` and other form
* elements.
*/
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/**
* Address margins handled incorrectly in IE 6/7.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/**
* Address font sizes and margins set differently in IE 6/7.
* Address font sizes within `section` and `article` in Firefox 4+, Safari 5,
* and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
h3 {
font-size: 1.17em;
margin: 1em 0;
}
h4 {
font-size: 1em;
margin: 1.33em 0;
}
h5 {
font-size: 0.83em;
margin: 1.67em 0;
}
h6 {
font-size: 0.67em;
margin: 2.33em 0;
}
/**
* Address styling not present in IE 7/8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/**
* Address styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address differences between Firefox and other browsers.
* Known issue: no IE 6/7 normalization.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Address styling not present in IE 6/7/8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address margins set differently in IE 6/7.
*/
p,
pre {
margin: 1em 0;
}
/**
* Correct font family set oddly in IE 6, Safari 4/5, and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em;
}
/**
* Improve readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/**
* Address CSS quotes not supported in IE 6/7.
*/
q {
quotes: none;
}
/**
* Address `quotes` property not supported in Safari 4.
*/
q:before,
q:after {
content: '';
content: none;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Lists
========================================================================== */
/**
* Address margins set differently in IE 6/7.
*/
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
/**
* Address paddings set differently in IE 6/7.
*/
menu,
ol,
ul {
padding: 0 0 0 40px;
}
/**
* Correct list images handled incorrectly in IE 7.
*/
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* ==========================================================================
Embedded content
========================================================================== */
/**
* 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improve image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/**
* Correct overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/**
* Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/**
* Correct margin displayed oddly in IE 6/7.
*/
form {
margin: 0;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct color not being inherited in IE 6/7/8/9.
* 2. Correct text not wrapping in Firefox 3.
* 3. Correct alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/**
* 1. Correct font size not being inherited in all browsers.
* 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improve appearance and consistency in all browsers.
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/**
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
* 4. Remove inner spacing in IE 7 without affecting normal text inputs.
* Known issue: inner spacing remains in IE 6.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
*overflow: visible; /* 4 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to content-box in IE 8/9.
* 2. Remove excess padding in IE 8/9.
* 3. Remove excess padding in IE 7.
* Known issue: excess padding remains in IE 6.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 6/7/8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@@ -0,0 +1,80 @@
body{
background-color: #ffffff;
}
.navbar.navbar-app {
background-color: #F26E5F;
color: rgba(255,255,255, 0.84);
}
.foot-wrap{
background-color: #5B90E7;
height:100px;
color: #FFFFFF;
}
.foot-wrap > *{
margin-top:20px;
}
.avatar{
border-radius: 100%;
}
@media (min-width: 1024px){
.left{
/*background-color: #5B90E7;*/
/*color: #FFFFFF;*/
/*margin-top:-20px;*/
margin-left: 20px;
display: block;
}
}
.left .separator{
clear: both;
overflow: hidden;
margin-top: 10px;
margin-bottom: 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
content: "";
}
.left .nav {
font-size: 20px;
color: #000000;
}
@media (min-width: 1024px) {
.right{
width: 61.8%;
margin-left: 23.2%;
} }
.right{
}
.right .separator{
/*clear: both;*/
overflow: hidden;
margin-top: 10px;
margin-bottom: 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
content: "";
}
a, a:hover, a:focus {
color: #000000;
}
a {
color: #337ab7;
text-decoration: none;
}
.form-group .checkbox label, .form-group .radio label, .form-group label{
color:#333333;
}
.form-group.is-focused .radio label:hover, .form-group.is-focused label.radio-inline:hover, .form-group.is-focused .radio label:focus, .form-group.is-focused label.radio-inline:focus{
color:#333333;
}

View File

@@ -0,0 +1,126 @@
.timeline {
list-style: none;
padding: 20px 0 20px;
position: relative;
}
.timeline:before {
top: 0;
bottom: 0;
position: absolute;
content: " ";
width: 3px;
background-color: #eeeeee;
left: 50%;
margin-left: -1.5px;
}
.timeline > li {
margin-bottom: 20px;
position: relative;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li > .timeline-panel {
width: 46%;
float: left;
border: 1px solid #d4d4d4;
border-radius: 2px;
padding: 20px;
position: relative;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
}
.timeline > li > .timeline-panel:before {
position: absolute;
top: 26px;
right: -15px;
display: inline-block;
border-top: 15px solid transparent;
border-left: 15px solid #ccc;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
content: " ";
}
.timeline > li > .timeline-panel:after {
position: absolute;
top: 27px;
right: -14px;
display: inline-block;
border-top: 14px solid transparent;
border-left: 14px solid #fff;
border-right: 0 solid #fff;
border-bottom: 14px solid transparent;
content: " ";
}
.timeline > li > .timeline-badge {
color: #fff;
width: 50px;
height: 50px;
line-height: 50px;
font-size: 1.4em;
text-align: center;
position: absolute;
top: 16px;
left: 50%;
margin-left: -25px;
background-color: #999999;
z-index: 100;
border-top-right-radius: 50%;
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.timeline > li.timeline-inverted > .timeline-panel {
float: right;
}
.timeline > li.timeline-inverted > .timeline-panel:before {
border-left-width: 0;
border-right-width: 15px;
left: -15px;
right: auto;
}
.timeline > li.timeline-inverted > .timeline-panel:after {
border-left-width: 0;
border-right-width: 14px;
left: -14px;
right: auto;
}
.timeline-badge.primary {
background-color: #2e6da4 !important;
}
.timeline-badge.success {
background-color: #3f903f !important;
}
.timeline-badge.warning {
background-color: #f0ad4e !important;
}
.timeline-badge.danger {
background-color: #d9534f !important;
}
.timeline-badge.info {
background-color: #5bc0de !important;
}
.timeline-title {
margin-top: 0;
color: inherit;
}
.timeline-body > p,
.timeline-body > ul {
margin-bottom: 0;
}
.timeline-body > p + p {
margin-top: 5px;
}

View File

@@ -0,0 +1,56 @@
/*
AngularJS v1.5.3
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(E,w,Va){'use strict';function ya(a,b,c){if(!a)throw Ka("areq",b||"?",c||"required");return a}function za(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ba(a)&&(a=a.join(" "));ba(b)&&(b=b.join(" "));return a+" "+b}function La(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function V(a,b,c){var d="";a=ba(a)?a:a&&I(a)&&a.length?a.split(/\s+/):[];q(a,function(a,f){a&&0<a.length&&(d+=0<f?" ":"",d+=c?b+a:a+b)});return d}function Ma(a){if(a instanceof H)switch(a.length){case 0:return[];
case 1:if(1===a[0].nodeType)return a;break;default:return H(ga(a))}if(1===a.nodeType)return H(a)}function ga(a){if(!a[0])return a;for(var b=0;b<a.length;b++){var c=a[b];if(1==c.nodeType)return c}}function Na(a,b,c){q(b,function(b){a.addClass(b,c)})}function Oa(a,b,c){q(b,function(b){a.removeClass(b,c)})}function Q(a){return function(b,c){c.addClass&&(Na(a,b,c.addClass),c.addClass=null);c.removeClass&&(Oa(a,b,c.removeClass),c.removeClass=null)}}function oa(a){a=a||{};if(!a.$$prepared){var b=a.domOperation||
O;a.domOperation=function(){a.$$domOperationFired=!0;b();b=O};a.$$prepared=!0}return a}function ha(a,b){Aa(a,b);Ba(a,b)}function Aa(a,b){b.from&&(a.css(b.from),b.from=null)}function Ba(a,b){b.to&&(a.css(b.to),b.to=null)}function T(a,b,c){var d=b.options||{};c=c.options||{};var e=(d.addClass||"")+" "+(c.addClass||""),f=(d.removeClass||"")+" "+(c.removeClass||"");a=Pa(a.attr("class"),e,f);c.preparationClasses&&(d.preparationClasses=W(c.preparationClasses,d.preparationClasses),delete c.preparationClasses);
e=d.domOperation!==O?d.domOperation:null;Ca(d,c);e&&(d.domOperation=e);d.addClass=a.addClass?a.addClass:null;d.removeClass=a.removeClass?a.removeClass:null;b.addClass=d.addClass;b.removeClass=d.removeClass;return d}function Pa(a,b,c){function d(a){I(a)&&(a=a.split(" "));var b={};q(a,function(a){a.length&&(b[a]=!0)});return b}var e={};a=d(a);b=d(b);q(b,function(a,b){e[b]=1});c=d(c);q(c,function(a,b){e[b]=1===e[b]?null:-1});var f={addClass:"",removeClass:""};q(e,function(b,c){var d,e;1===b?(d="addClass",
e=!a[c]):-1===b&&(d="removeClass",e=a[c]);e&&(f[d].length&&(f[d]+=" "),f[d]+=c)});return f}function A(a){return a instanceof w.element?a[0]:a}function Qa(a,b,c){var d="";b&&(d=V(b,"ng-",!0));c.addClass&&(d=W(d,V(c.addClass,"-add")));c.removeClass&&(d=W(d,V(c.removeClass,"-remove")));d.length&&(c.preparationClasses=d,a.addClass(d))}function pa(a,b){var c=b?"-"+b+"s":"";la(a,[ma,c]);return[ma,c]}function ra(a,b){var c=b?"paused":"",d=X+"PlayState";la(a,[d,c]);return[d,c]}function la(a,b){a.style[b[0]]=
b[1]}function W(a,b){return a?b?a+" "+b:a:b}function Da(a,b,c){var d=Object.create(null),e=a.getComputedStyle(b)||{};q(c,function(a,b){var c=e[a];if(c){var F=c.charAt(0);if("-"===F||"+"===F||0<=F)c=Ra(c);0===c&&(c=null);d[b]=c}});return d}function Ra(a){var b=0;a=a.split(/\s*,\s*/);q(a,function(a){"s"==a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function sa(a){return 0===a||null!=a}function Ea(a,b){var c=R,d=a+"s";b?c+="Duration":d+=" linear all";
return[c,d]}function Fa(){var a=Object.create(null);return{flush:function(){a=Object.create(null)},count:function(b){return(b=a[b])?b.total:0},get:function(b){return(b=a[b])&&b.value},put:function(b,c){a[b]?a[b].total++:a[b]={total:1,value:c}}}}function Ga(a,b,c){q(c,function(c){a[c]=Y(a[c])?a[c]:b.style.getPropertyValue(c)})}var O=w.noop,Ha=w.copy,Ca=w.extend,H=w.element,q=w.forEach,ba=w.isArray,I=w.isString,ca=w.isObject,N=w.isUndefined,Y=w.isDefined,Ia=w.isFunction,ta=w.isElement,R,ua,X,va;N(E.ontransitionend)&&
Y(E.onwebkittransitionend)?(R="WebkitTransition",ua="webkitTransitionEnd transitionend"):(R="transition",ua="transitionend");N(E.onanimationend)&&Y(E.onwebkitanimationend)?(X="WebkitAnimation",va="webkitAnimationEnd animationend"):(X="animation",va="animationend");var qa=X+"Delay",wa=X+"Duration",ma=R+"Delay";E=R+"Duration";var Ka=w.$$minErr("ng"),Sa={transitionDuration:E,transitionDelay:ma,transitionProperty:R+"Property",animationDuration:wa,animationDelay:qa,animationIterationCount:X+"IterationCount"},
Ta={transitionDuration:E,transitionDelay:ma,animationDuration:wa,animationDelay:qa};w.module("ngAnimate",[]).directive("ngAnimateSwap",["$animate","$rootScope",function(a,b){return{restrict:"A",transclude:"element",terminal:!0,priority:600,link:function(b,d,e,f,r){var x,F;b.$watchCollection(e.ngAnimateSwap||e["for"],function(e){x&&a.leave(x);F&&(F.$destroy(),F=null);if(e||0===e)F=b.$new(),r(F,function(b){x=b;a.enter(b,null,d)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,
c,d){function e(a){c.data("$$ngAnimateChildren","on"===a||"true"===a)}var f=d.ngAnimateChildren;w.isString(f)&&0===f.length?c.data("$$ngAnimateChildren",!0):(e(a(f)(b)),d.$observe("ngAnimateChildren",e))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),r=0;r<b.length;r++)b[r]();e||a(function(){e||c()})}}var d,e;d=b.queue=[];b.waitUntilQuiet=function(b){e&&e();e=a(function(){e=null;b();c()})};return b}]).provider("$$animateQueue",
["$animateProvider",function(a){function b(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);q(a,function(a){b[a]=!0});return b}function c(a,c){if(a&&c){var d=b(c);return a.split(" ").some(function(a){return d[a]})}}function d(a,b,c,d){return f[a].some(function(a){return a(b,c,d)})}function e(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var f=this.rules={skip:[],cancel:[],join:[]};f.join.push(function(a,b,c){return!b.structural&&e(b)});f.skip.push(function(a,
b,c){return!b.structural&&!e(b)});f.skip.push(function(a,b,c){return"leave"==c.event&&b.structural});f.skip.push(function(a,b,c){return c.structural&&2===c.state&&!b.structural});f.cancel.push(function(a,b,c){return c.structural&&b.structural});f.cancel.push(function(a,b,c){return 2===c.state&&b.structural});f.cancel.push(function(a,b,d){if(d.structural)return!1;a=b.addClass;b=b.removeClass;var e=d.addClass;d=d.removeClass;return N(a)&&N(b)||N(e)&&N(d)?!1:c(a,d)||c(b,e)});this.$get=["$$rAF","$rootScope",
"$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(b,c,f,u,l,w,s,M,v,h){function P(){var a=!1;return function(b){a?b():c.$$postDigest(function(){a=!0;b()})}}function y(a,b,c){var g=A(b),d=A(a),k=[];(a=G[c])&&q(a,function(a){t.call(a.node,g)?k.push(a.callback):"leave"===c&&t.call(a.node,d)&&k.push(a.callback)});return k}function k(a,g,k){function m(c,g,d,k){F(function(){var c=y(J,a,g);c.length&&b(function(){q(c,function(b){b(a,
d,k)})})});c.progress(g,d,k)}function G(b){var c=a,g=h;g.preparationClasses&&(c.removeClass(g.preparationClasses),g.preparationClasses=null);g.activeClasses&&(c.removeClass(g.activeClasses),g.activeClasses=null);Ja(a,h);ha(a,h);h.domOperation();f.complete(!b)}var h=Ha(k),t,J;if(a=Ma(a))t=A(a),J=a.parent();var h=oa(h),f=new s,F=P();ba(h.addClass)&&(h.addClass=h.addClass.join(" "));h.addClass&&!I(h.addClass)&&(h.addClass=null);ba(h.removeClass)&&(h.removeClass=h.removeClass.join(" "));h.removeClass&&
!I(h.removeClass)&&(h.removeClass=null);h.from&&!ca(h.from)&&(h.from=null);h.to&&!ca(h.to)&&(h.to=null);if(!t)return G(),f;k=[t.className,h.addClass,h.removeClass].join(" ");if(!Ua(k))return G(),f;var v=0<=["enter","move","leave"].indexOf(g),l=!K||u[0].hidden||C.get(t);k=!l&&z.get(t)||{};var Z=!!k.state;l||Z&&1==k.state||(l=!n(a,J,g));if(l)return G(),f;v&&xa(a);l={structural:v,element:a,event:g,addClass:h.addClass,removeClass:h.removeClass,close:G,options:h,runner:f};if(Z){if(d("skip",a,l,k)){if(2===
k.state)return G(),f;T(a,k,l);return k.runner}if(d("cancel",a,l,k))if(2===k.state)k.runner.end();else if(k.structural)k.close();else return T(a,k,l),k.runner;else if(d("join",a,l,k))if(2===k.state)T(a,l,{});else return Qa(a,v?g:null,h),g=l.event=k.event,h=T(a,k,l),k.runner}else T(a,l,{});(Z=l.structural)||(Z="animate"===l.event&&0<Object.keys(l.options.to||{}).length||e(l));if(!Z)return G(),ka(a),f;var M=(k.counter||0)+1;l.counter=M;D(a,1,l);c.$$postDigest(function(){var b=z.get(t),c=!b,b=b||{},d=
0<(a.parent()||[]).length&&("animate"===b.event||b.structural||e(b));if(c||b.counter!==M||!d){c&&(Ja(a,h),ha(a,h));if(c||v&&b.event!==g)h.domOperation(),f.end();d||ka(a)}else g=!b.structural&&e(b,!0)?"setClass":b.event,D(a,2),b=w(a,g,b.options),b.done(function(b){G(!b);(b=z.get(t))&&b.counter===M&&ka(A(a));m(f,g,"close",{})}),f.setHost(b),m(f,g,"start",{})});return f}function xa(a){a=A(a).querySelectorAll("[data-ng-animate]");q(a,function(a){var b=parseInt(a.getAttribute("data-ng-animate")),c=z.get(a);
if(c)switch(b){case 2:c.runner.end();case 1:z.remove(a)}})}function ka(a){a=A(a);a.removeAttribute("data-ng-animate");z.remove(a)}function J(a,b){return A(a)===A(b)}function n(a,b,c){c=H(u[0].body);var g=J(a,c)||"HTML"===a[0].nodeName,d=J(a,f),k=!1,h,m=C.get(A(a));(a=H.data(a[0],"$ngAnimatePin"))&&(b=a);for(b=A(b);b;){d||(d=J(b,f));if(1!==b.nodeType)break;a=z.get(b)||{};if(!k){var e=C.get(b);if(!0===e&&!1!==m){m=!0;break}else!1===e&&(m=!1);k=a.structural}if(N(h)||!0===h)a=H.data(b,"$$ngAnimateChildren"),
Y(a)&&(h=a);if(k&&!1===h)break;g||(g=J(b,c));if(g&&d)break;if(!d&&(a=H.data(b,"$ngAnimatePin"))){b=A(a);continue}b=b.parentNode}return(!k||h)&&!0!==m&&d&&g}function D(a,b,c){c=c||{};c.state=b;a=A(a);a.setAttribute("data-ng-animate",b);c=(b=z.get(a))?Ca(b,c):c;z.put(a,c)}var z=new l,C=new l,K=null,g=c.$watch(function(){return 0===M.totalPendingRequests},function(a){a&&(g(),c.$$postDigest(function(){c.$$postDigest(function(){null===K&&(K=!0)})}))}),G={},m=a.classNameFilter(),Ua=m?function(a){return m.test(a)}:
function(){return!0},Ja=Q(v),t=Node.prototype.contains||function(a){return this===a||!!(this.compareDocumentPosition(a)&16)},Z={on:function(a,b,c){var g=ga(b);G[a]=G[a]||[];G[a].push({node:g,callback:c});H(b).on("$destroy",function(){Z.off(a,b,c)})},off:function(a,b,c){function g(a,b,c){var d=ga(b);return a.filter(function(a){return!(a.node===d&&(!c||a.callback===c))})}var d=G[a];d&&(G[a]=1===arguments.length?null:g(d,b,c))},pin:function(a,b){ya(ta(a),"element","not an element");ya(ta(b),"parentElement",
"not an element");a.data("$ngAnimatePin",b)},push:function(a,b,c,g){c=c||{};c.domOperation=g;return k(a,b,c)},enabled:function(a,b){var c=arguments.length;if(0===c)b=!!K;else if(ta(a)){var g=A(a),d=C.get(g);1===c?b=!d:C.put(g,!b)}else b=K=!!a;return b}};return Z}]}]).provider("$$animation",["$animateProvider",function(a){function b(a){return a.data("$$animationRunner")}var c=this.drivers=[];this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(a,e,
f,r,x,F){function u(a){function b(a){if(a.processed)return a;a.processed=!0;var d=a.domNode,h=d.parentNode;e.put(d,a);for(var f;h;){if(f=e.get(h)){f.processed||(f=b(f));break}h=h.parentNode}(f||c).children.push(a);return a}var c={children:[]},d,e=new x;for(d=0;d<a.length;d++){var f=a[d];e.put(f.domNode,a[d]={domNode:f.domNode,fn:f.fn,children:[]})}for(d=0;d<a.length;d++)b(a[d]);return function(a){var b=[],c=[],d;for(d=0;d<a.children.length;d++)c.push(a.children[d]);a=c.length;var h=0,e=[];for(d=0;d<
c.length;d++){var f=c[d];0>=a&&(a=h,h=0,b.push(e),e=[]);e.push(f.fn);f.children.forEach(function(a){h++;c.push(a)});a--}e.length&&b.push(e);return b}(c)}var l=[],w=Q(a);return function(s,x,v){function h(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];q(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function P(a){var b=[],c={};q(a,function(a,g){var d=A(a.element),k=0<=["enter","move"].indexOf(a.event),d=a.structural?
h(d):[];if(d.length){var e=k?"to":"from";q(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][e]={animationID:g,element:H(a)}})}else b.push(a)});var d={},k={};q(c,function(c,h){var e=c.from,f=c.to;if(e&&f){var m=a[e.animationID],C=a[f.animationID],n=e.animationID.toString();if(!k[n]){var D=k[n]={structural:!0,beforeStart:function(){m.beforeStart();C.beforeStart()},close:function(){m.close();C.close()},classes:y(m.classes,C.classes),from:m,to:C,anchors:[]};D.classes.length?b.push(D):
(b.push(m),b.push(C))}k[n].anchors.push({out:e.element,"in":f.element})}else e=e?e.animationID:f.animationID,f=e.toString(),d[f]||(d[f]=!0,b.push(a[e]))});return b}function y(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d<a.length;d++){var k=a[d];if("ng-"!==k.substring(0,3))for(var e=0;e<b.length;e++)if(k===b[e]){c.push(k);break}}return c.join(" ")}function k(a){for(var b=c.length-1;0<=b;b--){var d=c[b];if(f.has(d)&&(d=f.get(d)(a)))return d}}function xa(a,c){a.from&&a.to?(b(a.from.element).setHost(c),
b(a.to.element).setHost(c)):b(a.element).setHost(c)}function ka(){var a=b(s);!a||"leave"===x&&v.$$domOperationFired||a.end()}function J(b){s.off("$destroy",ka);s.removeData("$$animationRunner");w(s,v);ha(s,v);v.domOperation();C&&a.removeClass(s,C);s.removeClass("ng-animate");D.complete(!b)}v=oa(v);var n=0<=["enter","move","leave"].indexOf(x),D=new r({end:function(){J()},cancel:function(){J(!0)}});if(!c.length)return J(),D;s.data("$$animationRunner",D);var z=za(s.attr("class"),za(v.addClass,v.removeClass)),
C=v.tempClasses;C&&(z+=" "+C,v.tempClasses=null);var K;n&&(K="ng-"+x+"-prepare",a.addClass(s,K));l.push({element:s,classes:z,event:x,structural:n,options:v,beforeStart:function(){s.addClass("ng-animate");C&&a.addClass(s,C);K&&(a.removeClass(s,K),K=null)},close:J});s.on("$destroy",ka);if(1<l.length)return D;e.$$postDigest(function(){var a=[];q(l,function(c){b(c.element)?a.push(c):c.close()});l.length=0;var c=P(a),d=[];q(c,function(a){d.push({domNode:A(a.from?a.from.element:a.element),fn:function(){a.beforeStart();
var c,d=a.close;if(b(a.anchors?a.from.element||a.to.element:a.element)){var g=k(a);g&&(c=g.start)}c?(c=c(),c.done(function(a){d(!a)}),xa(a,c)):d()}})});F(u(d))});return D}}]}]).provider("$animateCss",["$animateProvider",function(a){var b=Fa(),c=Fa();this.$get=["$window","$$jqLite","$$AnimateRunner","$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(a,e,f,r,x,F,u,l){function w(a,b){var c=a.parentNode;return(c.$$ngAnimateParentKey||(c.$$ngAnimateParentKey=++P))+"-"+a.getAttribute("class")+
"-"+b}function s(k,h,f,l){var n;0<b.count(f)&&(n=c.get(f),n||(h=V(h,"-stagger"),e.addClass(k,h),n=Da(a,k,l),n.animationDuration=Math.max(n.animationDuration,0),n.transitionDuration=Math.max(n.transitionDuration,0),e.removeClass(k,h),c.put(f,n)));return n||{}}function M(a){y.push(a);u.waitUntilQuiet(function(){b.flush();c.flush();for(var a=x(),d=0;d<y.length;d++)y[d](a);y.length=0})}function v(c,h,e){h=b.get(e);h||(h=Da(a,c,Sa),"infinite"===h.animationIterationCount&&(h.animationIterationCount=1));
b.put(e,h);c=h;e=c.animationDelay;h=c.transitionDelay;c.maxDelay=e&&h?Math.max(e,h):e||h;c.maxDuration=Math.max(c.animationDuration*c.animationIterationCount,c.transitionDuration);return c}var h=Q(e),P=0,y=[];return function(a,c){function d(){n()}function u(){n(!0)}function n(b){if(!(P||H&&da)){P=!0;da=!1;g.$$skipPreparationClasses||e.removeClass(a,fa);e.removeClass(a,ga);ra(m,!1);pa(m,!1);q(y,function(a){m.style[a[0]]=""});h(a,g);ha(a,g);Object.keys(G).length&&q(G,function(a,b){a?m.style.setProperty(b,
a):m.style.removeProperty(b)});if(g.onDone)g.onDone();ea&&ea.length&&a.off(ea.join(" "),C);var c=a.data("$$animateCss");c&&(r.cancel(c[0].timer),a.removeData("$$animateCss"));E&&E.complete(!b)}}function D(a){p.blockTransition&&pa(m,a);p.blockKeyframeAnimation&&ra(m,!!a)}function z(){E=new f({end:d,cancel:u});M(O);n();return{$$willAnimate:!1,start:function(){return E},end:d}}function C(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));
Math.max(a-W,0)>=Q&&b>=L&&(H=!0,n())}function K(){function b(){if(!P){D(!1);q(y,function(a){m.style[a[0]]=a[1]});h(a,g);e.addClass(a,ga);if(p.recalculateTimingStyles){na=m.className+" "+fa;ia=w(m,na);B=v(m,na,ia);$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;if(0===L){n();return}p.hasTransitions=0<B.transitionDuration;p.hasAnimations=0<B.animationDuration}p.applyAnimationDelay&&($="boolean"!==typeof g.delay&&sa(g.delay)?parseFloat(g.delay):$,I=Math.max($,0),B.animationDelay=$,aa=[qa,$+"s"],y.push(aa),
m.style[aa[0]]=aa[1]);Q=1E3*I;T=1E3*L;if(g.easing){var d,f=g.easing;p.hasTransitions&&(d=R+"TimingFunction",y.push([d,f]),m.style[d]=f);p.hasAnimations&&(d=X+"TimingFunction",y.push([d,f]),m.style[d]=f)}B.transitionDuration&&ea.push(ua);B.animationDuration&&ea.push(va);W=Date.now();var l=Q+1.5*T;d=W+l;var f=a.data("$$animateCss")||[],K=!0;if(f.length){var z=f[0];(K=d>z.expectedEndTime)?r.cancel(z.timer):f.push(n)}K&&(l=r(c,l,!1),f[0]={timer:l,expectedEndTime:d},f.push(n),a.data("$$animateCss",f));
if(ea.length)a.on(ea.join(" "),C);g.to&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.to)),Ba(a,g))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d<b.length;d++)b[d]();a.removeData("$$animateCss")}}if(!P)if(m.parentNode){var d=function(a){if(H)da&&a&&(da=!1,n());else if(da=!a,B.animationDuration)if(a=ra(m,da),da)y.push(a);else{var b=y,c=b.indexOf(a);0<=a&&b.splice(c,1)}},f=0<ca&&(B.transitionDuration&&0===U.transitionDuration||B.animationDuration&&0===U.animationDuration)&&Math.max(U.animationDelay,
U.transitionDelay);f?r(b,Math.floor(f*ca*1E3),!1):b();N.resume=function(){d(!0)};N.pause=function(){d(!1)}}else n()}var g=c||{};g.$$prepared||(g=oa(Ha(g)));var G={},m=A(a);if(!m||!m.parentNode||!l.enabled())return z();var y=[],x=a.attr("class"),t=La(g),P,da,H,E,N,I,Q,L,T,W,ea=[];if(0===g.duration||!F.animations&&!F.transitions)return z();var ja=g.event&&ba(g.event)?g.event.join(" "):g.event,Y="",S="";ja&&g.structural?Y=V(ja,"ng-",!0):ja&&(Y=ja);g.addClass&&(S+=V(g.addClass,"-add"));g.removeClass&&
(S.length&&(S+=" "),S+=V(g.removeClass,"-remove"));g.applyClassesEarly&&S.length&&h(a,g);var fa=[Y,S].join(" ").trim(),na=x+" "+fa,ga=V(fa,"-active"),x=t.to&&0<Object.keys(t.to).length;if(!(0<(g.keyframeStyle||"").length||x||fa))return z();var ia,U;0<g.stagger?(t=parseFloat(g.stagger),U={transitionDelay:t,animationDelay:t,transitionDuration:0,animationDuration:0}):(ia=w(m,na),U=s(m,fa,ia,Ta));g.$$skipPreparationClasses||e.addClass(a,fa);g.transitionStyle&&(t=[R,g.transitionStyle],la(m,t),y.push(t));
0<=g.duration&&(t=0<m.style[R].length,t=Ea(g.duration,t),la(m,t),y.push(t));g.keyframeStyle&&(t=[X,g.keyframeStyle],la(m,t),y.push(t));var ca=U?0<=g.staggerIndex?g.staggerIndex:b.count(ia):0;(ja=0===ca)&&!g.skipBlocking&&pa(m,9999);var B=v(m,na,ia),$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;var p={};p.hasTransitions=0<B.transitionDuration;p.hasAnimations=0<B.animationDuration;p.hasTransitionAll=p.hasTransitions&&"all"==B.transitionProperty;p.applyTransitionDuration=x&&(p.hasTransitions&&!p.hasTransitionAll||
p.hasAnimations&&!p.hasTransitions);p.applyAnimationDuration=g.duration&&p.hasAnimations;p.applyTransitionDelay=sa(g.delay)&&(p.applyTransitionDuration||p.hasTransitions);p.applyAnimationDelay=sa(g.delay)&&p.hasAnimations;p.recalculateTimingStyles=0<S.length;if(p.applyTransitionDuration||p.applyAnimationDuration)L=g.duration?parseFloat(g.duration):L,p.applyTransitionDuration&&(p.hasTransitions=!0,B.transitionDuration=L,t=0<m.style[R+"Property"].length,y.push(Ea(L,t))),p.applyAnimationDuration&&(p.hasAnimations=
!0,B.animationDuration=L,y.push([wa,L+"s"]));if(0===L&&!p.recalculateTimingStyles)return z();if(null!=g.delay){var aa;"boolean"!==typeof g.delay&&(aa=parseFloat(g.delay),I=Math.max(aa,0));p.applyTransitionDelay&&y.push([ma,aa+"s"]);p.applyAnimationDelay&&y.push([qa,aa+"s"])}null==g.duration&&0<B.transitionDuration&&(p.recalculateTimingStyles=p.recalculateTimingStyles||ja);Q=1E3*I;T=1E3*L;g.skipBlocking||(p.blockTransition=0<B.transitionDuration,p.blockKeyframeAnimation=0<B.animationDuration&&0<U.animationDelay&&
0===U.animationDuration);g.from&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.from)),Aa(a,g));p.blockTransition||p.blockKeyframeAnimation?D(L):g.skipBlocking||pa(m,!1);return{$$willAnimate:!0,end:d,start:function(){if(!P)return N={end:d,cancel:u,resume:null,pause:null},E=new f(N),M(K),E}}}}]}]).provider("$$animateCssDriver",["$$animationProvider",function(a){a.drivers.push("$$animateCssDriver");this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(a,
c,d,e,f,r,x){function F(a){return a.replace(/\bng-\S+\b/g,"")}function u(a,b){I(a)&&(a=a.split(" "));I(b)&&(b=b.split(" "));return a.filter(function(a){return-1===b.indexOf(a)}).join(" ")}function l(c,e,f){function k(a){var b={},c=A(a).getBoundingClientRect();q(["width","height","top","left"],function(a){var d=c[a];switch(a){case "top":d+=M.scrollTop;break;case "left":d+=M.scrollLeft}b[a]=Math.floor(d)+"px"});return b}function l(){var c=F(f.attr("class")||""),d=u(c,n),c=u(n,c),d=a(r,{to:k(f),addClass:"ng-anchor-in "+
d,removeClass:"ng-anchor-out "+c,delay:!0});return d.$$willAnimate?d:null}function x(){r.remove();e.removeClass("ng-animate-shim");f.removeClass("ng-animate-shim")}var r=H(A(e).cloneNode(!0)),n=F(r.attr("class")||"");e.addClass("ng-animate-shim");f.addClass("ng-animate-shim");r.addClass("ng-anchor");v.append(r);var D;c=function(){var c=a(r,{addClass:"ng-anchor-out",delay:!0,from:k(e)});return c.$$willAnimate?c:null}();if(!c&&(D=l(),!D))return x();var z=c||D;return{start:function(){function a(){c&&
c.end()}var b,c=z.start();c.done(function(){c=null;if(!D&&(D=l()))return c=D.start(),c.done(function(){c=null;x();b.complete()}),c;x();b.complete()});return b=new d({end:a,cancel:a})}}}function w(a,b,c,e){var f=s(a,O),r=s(b,O),x=[];q(e,function(a){(a=l(c,a.out,a["in"]))&&x.push(a)});if(f||r||0!==x.length)return{start:function(){function a(){q(b,function(a){a.end()})}var b=[];f&&b.push(f.start());r&&b.push(r.start());q(x,function(a){b.push(a.start())});var c=new d({end:a,cancel:a});d.all(b,function(a){c.complete(a)});
return c}}}function s(c){var d=c.element,e=c.options||{};c.structural&&(e.event=c.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===c.event&&(e.onDone=e.domOperation));e.preparationClasses&&(e.event=W(e.event,e.preparationClasses));c=a(d,e);return c.$$willAnimate?c:null}if(!f.animations&&!f.transitions)return O;var M=x[0].body;c=A(e);var v=H(c.parentNode&&11===c.parentNode.nodeType||M.contains(c)?c:M);Q(r);return function(a){return a.from&&a.to?w(a.from,a.to,a.classes,a.anchors):s(a)}}]}]).provider("$$animateJs",
["$animateProvider",function(a){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(b,c,d){function e(c){c=ba(c)?c:c.split(" ");for(var d=[],e={},f=0;f<c.length;f++){var l=c[f],q=a.$$registeredAnimations[l];q&&!e[l]&&(d.push(b.get(q)),e[l]=!0)}return d}var f=Q(d);return function(a,b,d,u){function l(){u.domOperation();f(a,u)}function w(a,b,d,e,g){switch(d){case "animate":b=[b,e.from,e.to,g];break;case "setClass":b=[b,h,H,g];break;case "addClass":b=[b,h,g];break;case "removeClass":b=[b,H,g];
break;default:b=[b,g]}b.push(e);if(a=a.apply(a,b))if(Ia(a.start)&&(a=a.start()),a instanceof c)a.done(g);else if(Ia(a))return a;return O}function s(a,b,d,e,g){var f=[];q(e,function(e){var h=e[g];h&&f.push(function(){var e,g,f=!1,k=function(a){f||(f=!0,(g||O)(a),e.complete(!a))};e=new c({end:function(){k()},cancel:function(){k(!0)}});g=w(h,a,b,d,function(a){k(!1===a)});return e})});return f}function A(a,b,d,e,g){var f=s(a,b,d,e,g);if(0===f.length){var h,k;"beforeSetClass"===g?(h=s(a,"removeClass",
d,e,"beforeRemoveClass"),k=s(a,"addClass",d,e,"beforeAddClass")):"setClass"===g&&(h=s(a,"removeClass",d,e,"removeClass"),k=s(a,"addClass",d,e,"addClass"));h&&(f=f.concat(h));k&&(f=f.concat(k))}if(0!==f.length)return function(a){var b=[];f.length&&q(f,function(a){b.push(a())});b.length?c.all(b,a):a();return function(a){q(b,function(b){a?b.cancel():b.end()})}}}var v=!1;3===arguments.length&&ca(d)&&(u=d,d=null);u=oa(u);d||(d=a.attr("class")||"",u.addClass&&(d+=" "+u.addClass),u.removeClass&&(d+=" "+
u.removeClass));var h=u.addClass,H=u.removeClass,y=e(d),k,E;if(y.length){var I,J;"leave"==b?(J="leave",I="afterLeave"):(J="before"+b.charAt(0).toUpperCase()+b.substr(1),I=b);"enter"!==b&&"move"!==b&&(k=A(a,b,u,y,J));E=A(a,b,u,y,I)}if(k||E){var n;return{$$willAnimate:!0,end:function(){n?n.end():(v=!0,l(),ha(a,u),n=new c,n.complete(!0));return n},start:function(){function b(c){v=!0;l();ha(a,u);n.complete(c)}if(n)return n;n=new c;var d,e=[];k&&e.push(function(a){d=k(a)});e.length?e.push(function(a){l();
a(!0)}):l();E&&e.push(function(a){d=E(a)});n.setHost({end:function(){v||((d||O)(void 0),b(void 0))},cancel:function(){v||((d||O)(!0),b(!0))}});c.chain(e,b);return n}}}}}]}]).provider("$$animateJsDriver",["$$animationProvider",function(a){a.drivers.push("$$animateJsDriver");this.$get=["$$animateJs","$$AnimateRunner",function(a,c){function d(c){return a(c.element,c.event,c.classes,c.options)}return function(a){if(a.from&&a.to){var b=d(a.from),r=d(a.to);if(b||r)return{start:function(){function a(){return function(){q(d,
function(a){a.end()})}}var d=[];b&&d.push(b.start());r&&d.push(r.start());c.all(d,function(a){e.complete(a)});var e=new c({end:a(),cancel:a()});return e}}}else return d(a)}}]}])})(window,window.angular);
//# sourceMappingURL=angular-animate.min.js.map

View File

@@ -0,0 +1,14 @@
/*
AngularJS v1.5.3
(c) 2010-2016 Google, Inc. http://angularjs.org
License: MIT
*/
(function(s,q,t){'use strict';var f="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};q.module("ngAria",["ng"]).provider("$aria",function(){function a(a,h,p,n){return function(d,e,b){var g=b.$normalize(h);!c[g]||l(e,p)||b[g]||d.$watch(b[a],function(b){b=n?!b:!!b;e.attr(h,b)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};this.config=
function(a){c=q.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",f,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",f,!1)}]).directive("ngRequired",
["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",f,!1)}]).directive("ngModel",["$aria",function(a){function c(c,n,d,e){return a.config(n)&&!d.attr(c)&&(e||!l(d,f))}function m(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function h(a,c){var d=a.type,e=a.role;return"checkbox"===(d||e)||"menuitemcheckbox"===e?"checkbox":"radio"===(d||e)||"menuitemradio"===e?"radio":"range"===d||"progressbar"===e||"slider"===e?"range":""}return{restrict:"A",require:"ngModel",
priority:200,compile:function(f,n){var d=h(n,f);return{pre:function(a,b,c,k){"checkbox"===d&&(k.$isEmpty=function(a){return!1===a})},post:function(e,b,g,k){function f(){return k.$modelValue}function h(a){b.attr("aria-checked",g.value==k.$viewValue)}function n(){b.attr("aria-checked",!k.$isEmpty(k.$viewValue))}var l=c("tabindex","tabindex",b,!1);switch(d){case "radio":case "checkbox":m(d,b)&&b.attr("role",d);c("aria-checked","ariaChecked",b,!1)&&e.$watch(f,"radio"===d?h:n);l&&b.attr("tabindex",0);
break;case "range":m(d,b)&&b.attr("role","slider");if(a.config("ariaValue")){var p=!b.attr("aria-valuemin")&&(g.hasOwnProperty("min")||g.hasOwnProperty("ngMin")),q=!b.attr("aria-valuemax")&&(g.hasOwnProperty("max")||g.hasOwnProperty("ngMax")),r=!b.attr("aria-valuenow");p&&g.$observe("min",function(a){b.attr("aria-valuemin",a)});q&&g.$observe("max",function(a){b.attr("aria-valuemax",a)});r&&e.$watch(f,function(a){b.attr("aria-valuenow",a)})}l&&b.attr("tabindex",0)}!g.hasOwnProperty("ngRequired")&&
k.$validators.required&&c("aria-required","ariaRequired",b,!1)&&g.$observe("required",function(){b.attr("aria-required",!!g.required)});c("aria-invalid","ariaInvalid",b,!0)&&e.$watch(function(){return k.$invalid},function(a){b.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",f,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",link:function(a,c,f,h){c.attr("aria-live")||c.attr("aria-live",
"assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(m,h){var p=c(h.ngClick,null,!0);return function(c,d,e){if(!l(d,f)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!e.ngKeypress))d.on("keypress",function(a){function d(){p(c,{$event:a})}var e=a.which||a.keyCode;32!==e&&13!==e||c.$apply(d)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,
m,h){!a.config("tabindex")||m.attr("tabindex")||l(m,f)||m.attr("tabindex",0)}}])})(window,window.angular);
//# sourceMappingURL=angular-aria.min.js.map

Some files were not shown because too many files have changed in this diff Show More