mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2026-02-01 14:56:20 +08:00
Remove the 'ng' suffix of sub projects.
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/.idea/
|
||||
/target/
|
||||
*.iml
|
||||
16
.travis.yml
Normal file
16
.travis.yml
Normal 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
579
LICENSE
Normal 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
5
NOTICE
Normal 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
61
README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
## RocketMQ-Console-Ng[](https://travis-ci.org/rocketmq/rocketmq-console-ng) [](https://coveralls.io/github/rocketmq/rocketmq-console-ng?branch=master)
|
||||
[](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
102
doc/1_0_0/Milestone.md
Normal 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
64
doc/1_0_0/UserGuide_CN.md
Normal 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
65
doc/1_0_0/UserGuide_EN.md
Normal 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 count(topic 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 router(this topic send to which broker,the broker's queue info)
|
||||
* CONSUMER MANAGE(this topic consume by which group,how about the consume state)
|
||||
* TOPIC CONFIG(check or change the topic's config)
|
||||
* SEND MESSAGE(send 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 Messages,the 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
276
pom.xml
Normal 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>
|
||||
6
src/main/docker/Dockerfile
Normal file
6
src/main/docker/Dockerfile
Normal 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" ]
|
||||
31
src/main/java/org/apache/rocketmq/console/App.java
Normal file
31
src/main/java/org/apache/rocketmq/console/App.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
196
src/main/java/org/apache/rocketmq/console/model/MessageView.java
Normal file
196
src/main/java/org/apache/rocketmq/console/model/MessageView.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>>() {
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
156
src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
Normal file
156
src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
Normal 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");
|
||||
}
|
||||
}
|
||||
16
src/main/resources/application.properties
Normal file
16
src/main/resources/application.properties
Normal 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
|
||||
33
src/main/resources/logback.xml
Normal file
33
src/main/resources/logback.xml
Normal 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>
|
||||
117
src/main/resources/static/index.html
Normal file
117
src/main/resources/static/index.html
Normal 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>
|
||||
208
src/main/resources/static/src/app.js
Normal file
208
src/main/resources/static/src/app.js
Normal 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;
|
||||
};
|
||||
72
src/main/resources/static/src/cluster.js
Normal file
72
src/main/resources/static/src/cluster.js
Normal 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});
|
||||
}
|
||||
})
|
||||
}
|
||||
}])
|
||||
350
src/main/resources/static/src/consumer.js
Normal file
350
src/main/resources/static/src/consumer.js
Normal 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});
|
||||
}
|
||||
});
|
||||
};
|
||||
}]
|
||||
);
|
||||
|
||||
|
||||
|
||||
557
src/main/resources/static/src/controller.js
Normal file
557
src/main/resources/static/src/controller.js
Normal 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);
|
||||
|
||||
|
||||
}]);
|
||||
|
||||
|
||||
4
src/main/resources/static/src/data/dict.json
Normal file
4
src/main/resources/static/src/data/dict.json
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
{"TYPE":"DEMO_TYPE","DICT_VALUE":"0","DICT_NAME":"test1"},
|
||||
{"TYPE":"DEMO_TYPE","DICT_VALUE":"1","DICT_NAME":"test2"}
|
||||
]
|
||||
80
src/main/resources/static/src/i18n/en.js
Normal file
80
src/main/resources/static/src/i18n/en.js
Normal 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"
|
||||
}
|
||||
80
src/main/resources/static/src/i18n/zh.js
Normal file
80
src/main/resources/static/src/i18n/zh.js
Normal 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":"刷新"
|
||||
}
|
||||
200
src/main/resources/static/src/message.js
Normal file
200
src/main/resources/static/src/message.js
Normal 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
|
||||
}
|
||||
});
|
||||
};
|
||||
}]
|
||||
);
|
||||
59
src/main/resources/static/src/ops.js
Normal file
59
src/main/resources/static/src/ops.js
Normal 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});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]);
|
||||
49
src/main/resources/static/src/producer.js
Normal file
49
src/main/resources/static/src/producer.js
Normal 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});
|
||||
}
|
||||
});
|
||||
}
|
||||
} ]);
|
||||
75
src/main/resources/static/src/remoteApi/remoteApi.js
Normal file
75
src/main/resources/static/src/remoteApi/remoteApi.js
Normal 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
|
||||
}
|
||||
}])
|
||||
|
||||
87
src/main/resources/static/src/tools/tools.js
Normal file
87
src/main/resources/static/src/tools/tools.js
Normal 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
|
||||
}
|
||||
}])
|
||||
|
||||
|
||||
|
||||
406
src/main/resources/static/src/topic.js
Normal file
406
src/main/resources/static/src/topic.js
Normal 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});
|
||||
}
|
||||
})
|
||||
};
|
||||
}]
|
||||
|
||||
);
|
||||
32
src/main/resources/static/style/animate.css
vendored
Normal file
32
src/main/resources/static/style/animate.css
vendored
Normal 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);
|
||||
}
|
||||
274
src/main/resources/static/style/app.css
Normal file
274
src/main/resources/static/style/app.css
Normal 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;
|
||||
}
|
||||
752
src/main/resources/static/style/btn.css
Normal file
752
src/main/resources/static/style/btn.css
Normal 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;
|
||||
}
|
||||
BIN
src/main/resources/static/style/img/alipay.png
Normal file
BIN
src/main/resources/static/style/img/alipay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/main/resources/static/style/img/alipay_active.png
Normal file
BIN
src/main/resources/static/style/img/alipay_active.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/main/resources/static/style/img/icon_user_center.png
Normal file
BIN
src/main/resources/static/style/img/icon_user_center.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
src/main/resources/static/style/img/weipay.png
Normal file
BIN
src/main/resources/static/style/img/weipay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/resources/static/style/img/weipay_active.png
Normal file
BIN
src/main/resources/static/style/img/weipay_active.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
12
src/main/resources/static/style/login.css
Normal file
12
src/main/resources/static/style/login.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.login-panel{
|
||||
|
||||
}
|
||||
|
||||
.login-panel .qrcode {
|
||||
width: 100%;
|
||||
hight: 100%;
|
||||
}
|
||||
|
||||
.login-panel .validateCode {
|
||||
/*height:20px;*/
|
||||
}
|
||||
420
src/main/resources/static/style/preLoading/main.css
Executable file
420
src/main/resources/static/style/preLoading/main.css
Executable 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
|
||||
*/
|
||||
527
src/main/resources/static/style/preLoading/normalize.css
vendored
Executable file
527
src/main/resources/static/style/preLoading/normalize.css
vendored
Executable 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;
|
||||
}
|
||||
80
src/main/resources/static/style/theme.css
Normal file
80
src/main/resources/static/style/theme.css
Normal 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;
|
||||
}
|
||||
126
src/main/resources/static/style/timeline.css
Normal file
126
src/main/resources/static/style/timeline.css
Normal 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;
|
||||
}
|
||||
56
src/main/resources/static/vendor/angular-material/angular-animate.min.js
vendored
Normal file
56
src/main/resources/static/vendor/angular-material/angular-animate.min.js
vendored
Normal 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
|
||||
14
src/main/resources/static/vendor/angular-material/angular-aria.min.js
vendored
Normal file
14
src/main/resources/static/vendor/angular-material/angular-aria.min.js
vendored
Normal 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
Reference in New Issue
Block a user