You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2016/11/29 17:06:18 UTC

[1/2] lucene-solr:feature/metrics: SOLR-4735 Improve Solr metric reporting (Alan Woodward, Kelvin Wong, Christine Poerschke, Jeff Wartes, ab)

Repository: lucene-solr
Updated Branches:
  refs/heads/feature/metrics 540a73a6f -> db1339ff6


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-json-LICENSE-ASL.txt
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-json-LICENSE-ASL.txt b/solr/licenses/metrics-json-LICENSE-ASL.txt
new file mode 100644
index 0000000..ccb320c
--- /dev/null
+++ b/solr/licenses/metrics-json-LICENSE-ASL.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (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 2010-2012 Coda Hale and Yammer, Inc.
+
+   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.
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-json-NOTICE.txt
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-json-NOTICE.txt b/solr/licenses/metrics-json-NOTICE.txt
new file mode 100644
index 0000000..b4c6298
--- /dev/null
+++ b/solr/licenses/metrics-json-NOTICE.txt
@@ -0,0 +1,12 @@
+Metrics
+Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+This product includes software developed by Coda Hale and Yammer, Inc.
+
+This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
+LongAdder), which was released with the following comments:
+
+    Written by Doug Lea with assistance from members of JCP JSR-166
+    Expert Group and released to the public domain, as explained at
+    http://creativecommons.org/publicdomain/zero/1.0/
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-servlets-3.1.2.jar.sha1
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-servlets-3.1.2.jar.sha1 b/solr/licenses/metrics-servlets-3.1.2.jar.sha1
new file mode 100644
index 0000000..ba941e5
--- /dev/null
+++ b/solr/licenses/metrics-servlets-3.1.2.jar.sha1
@@ -0,0 +1 @@
+f552e662d790b805f01a8b2997dc4ae9409cd4c8

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-servlets-LICENSE-ASL.txt
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-servlets-LICENSE-ASL.txt b/solr/licenses/metrics-servlets-LICENSE-ASL.txt
new file mode 100644
index 0000000..ccb320c
--- /dev/null
+++ b/solr/licenses/metrics-servlets-LICENSE-ASL.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (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 2010-2012 Coda Hale and Yammer, Inc.
+
+   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.
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-servlets-NOTICE.txt
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-servlets-NOTICE.txt b/solr/licenses/metrics-servlets-NOTICE.txt
new file mode 100644
index 0000000..b4c6298
--- /dev/null
+++ b/solr/licenses/metrics-servlets-NOTICE.txt
@@ -0,0 +1,12 @@
+Metrics
+Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+This product includes software developed by Coda Hale and Yammer, Inc.
+
+This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
+LongAdder), which was released with the following comments:
+
+    Written by Doug Lea with assistance from members of JCP JSR-166
+    Expert Group and released to the public domain, as explained at
+    http://creativecommons.org/publicdomain/zero/1.0/
+


[2/2] lucene-solr:feature/metrics: SOLR-4735 Improve Solr metric reporting (Alan Woodward, Kelvin Wong, Christine Poerschke, Jeff Wartes, ab)

Posted by ab...@apache.org.
SOLR-4735 Improve Solr metric reporting (Alan Woodward, Kelvin Wong,
Christine Poerschke, Jeff Wartes, ab)


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/db1339ff
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/db1339ff
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/db1339ff

Branch: refs/heads/feature/metrics
Commit: db1339ff622cc73871897f8b345a9be19134a95e
Parents: 540a73a
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Tue Nov 29 18:02:04 2016 +0100
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Tue Nov 29 18:02:04 2016 +0100

----------------------------------------------------------------------
 lucene/ivy-versions.properties                  |   2 +-
 .../src/java/org/apache/solr/core/SolrCore.java |  40 +++
 .../apache/solr/handler/RequestHandlerBase.java |  54 ++--
 .../solr/metrics/SolrCoreMetricManager.java     | 161 ++++++++++++
 .../org/apache/solr/metrics/SolrMetricInfo.java | 104 ++++++++
 .../apache/solr/metrics/SolrMetricManager.java  | 198 +++++++++++++++
 .../apache/solr/metrics/SolrMetricProducer.java |  37 +++
 .../apache/solr/metrics/SolrMetricReporter.java |  66 +++++
 .../org/apache/solr/metrics/package-info.java   |  23 ++
 .../solr/metrics/reporters/SolrJmxReporter.java | 254 +++++++++++++++++++
 .../solr/metrics/reporters/package-info.java    |  22 ++
 .../src/java/org/apache/solr/util/JmxUtil.java  |  93 +++++++
 .../solr/metrics/SolrCoreMetricManagerTest.java | 151 +++++++++++
 .../solr/metrics/SolrMetricReporterTest.java    | 115 +++++++++
 .../solr/metrics/SolrMetricTestUtils.java       | 142 +++++++++++
 .../metrics/reporters/SolrJmxReporterTest.java  | 158 ++++++++++++
 solr/licenses/metrics-jetty-LICENSE-ASL.txt     | 203 +++++++++++++++
 solr/licenses/metrics-jetty-NOTICE.txt          |  12 +
 solr/licenses/metrics-jetty9-3.1.2.jar.sha1     |   1 +
 solr/licenses/metrics-json-3.1.2.jar.sha1       |   1 +
 solr/licenses/metrics-json-LICENSE-ASL.txt      | 203 +++++++++++++++
 solr/licenses/metrics-json-NOTICE.txt           |  12 +
 solr/licenses/metrics-servlets-3.1.2.jar.sha1   |   1 +
 solr/licenses/metrics-servlets-LICENSE-ASL.txt  | 203 +++++++++++++++
 solr/licenses/metrics-servlets-NOTICE.txt       |  12 +
 25 files changed, 2248 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/lucene/ivy-versions.properties
----------------------------------------------------------------------
diff --git a/lucene/ivy-versions.properties b/lucene/ivy-versions.properties
index 4f07f09..1f3953d 100644
--- a/lucene/ivy-versions.properties
+++ b/lucene/ivy-versions.properties
@@ -77,8 +77,8 @@ io.dropwizard.metrics.version = 3.1.2
 /io.dropwizard.metrics/metrics-core = ${io.dropwizard.metrics.version}
 /io.dropwizard.metrics/metrics-healthchecks = ${io.dropwizard.metrics.version}
 /io.dropwizard.metrics/metrics-jetty9 = ${io.dropwizard.metrics.version}
-/io.dropwizard.metrics/metrics-servlets = ${io.dropwizard.metrics.version}
 /io.dropwizard.metrics/metrics-json = ${io.dropwizard.metrics.version}
+/io.dropwizard.metrics/metrics-servlets = ${io.dropwizard.metrics.version}
 
 io.netty.netty-all.version = 4.0.36.Final
 /io.netty/netty-all = ${io.netty.netty-all.version}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/core/SolrCore.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index e5bc53d..e2bf222 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -94,6 +94,8 @@ import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.component.HighlightComponent;
 import org.apache.solr.handler.component.SearchComponent;
 import org.apache.solr.logging.MDCLoggingContext;
+import org.apache.solr.metrics.SolrCoreMetricManager;
+import org.apache.solr.metrics.SolrMetricProducer;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestHandler;
 import org.apache.solr.response.BinaryResponseWriter;
@@ -188,6 +190,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
   private final PluginBag<SearchComponent> searchComponents = new PluginBag<>(SearchComponent.class, this);
   private final PluginBag<UpdateRequestProcessorFactory> updateProcessors = new PluginBag<>(UpdateRequestProcessorFactory.class, this, true);
   private final Map<String,UpdateRequestProcessorChain> updateProcessorChains;
+  private final SolrCoreMetricManager metricManager;
   private final Map<String, SolrInfoMBean> infoRegistry;
   private final IndexDeletionPolicyWrapper solrDelPolicy;
   private final SolrSnapshotMetaDataManager snapshotMgr;
@@ -839,6 +842,9 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
 
     checkVersionFieldExistsInSchema(schema, coreDescriptor);
 
+    // Initialize the metrics manager
+    this.metricManager = initMetricManager(config);
+
     // Initialize JMX
     this.infoRegistry = initInfoRegistry(name, config);
     infoRegistry.put("fieldCache", new SolrFieldCacheMBean());
@@ -1042,6 +1048,26 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
     setLatestSchema(schema);
   }
 
+  /**
+   * Initializes the core's {@link SolrCoreMetricManager} with a given configuration.
+   * If metric reporters are configured, they are also registered with the manager.
+   *
+   * @param config the given configuration
+   * @return an instance of {@link SolrCoreMetricManager}
+   */
+  private SolrCoreMetricManager initMetricManager(SolrConfig config) {
+    SolrCoreMetricManager metricManager = new SolrCoreMetricManager(this);
+    for (PluginInfo pluginInfo : config.readPluginInfos("metricReporter", true, true)) {
+      try {
+        metricManager.loadReporter(pluginInfo);
+      } catch (IOException e) {
+        log.error("Failed to load reporter for plugin info = {}.", pluginInfo);
+        // TODO: shall we re-throw the exception here?
+      }
+    }
+    return metricManager;
+  }
+
   private Map<String,SolrInfoMBean> initInfoRegistry(String name, SolrConfig config) {
     if (config.jmxConfig.enabled) {
       return new JmxMonitoredMap<String, SolrInfoMBean>(name, String.valueOf(this.hashCode()), config.jmxConfig);
@@ -1362,6 +1388,15 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
       }
     }
 
+    try {
+      metricManager.close();
+    } catch (Throwable e) {
+      SolrException.log(log, e);
+      if (e instanceof  Error) {
+        throw (Error) e;
+      }
+    }
+
     // Close the snapshots meta-data directory.
     Directory snapshotsDir = snapshotMgr.getSnapshotsDir();
     try {
@@ -2752,6 +2787,11 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
 
   public void registerInfoBean(String name, SolrInfoMBean solrInfoMBean) {
     infoRegistry.put(name, solrInfoMBean);
+
+    if (solrInfoMBean instanceof SolrMetricProducer) {
+      SolrMetricProducer producer = (SolrMetricProducer) solrInfoMBean;
+      metricManager.registerMetricProducer(name, producer);
+    }
   }
 
   private static boolean checkStale(SolrZkClient zkClient,  String zkPath, int currentVersion)  {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java b/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
index 218b6de..2f00767 100644
--- a/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
+++ b/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
@@ -18,8 +18,11 @@ package org.apache.solr.handler;
 
 import java.lang.invoke.MethodHandles;
 import java.net.URL;
-import java.util.concurrent.atomic.LongAdder;
+import java.util.Arrays;
+import java.util.Collection;
 
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Meter;
 import com.codahale.metrics.Timer;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.SolrParams;
@@ -29,6 +32,8 @@ import org.apache.solr.common.util.SuppressForbidden;
 import org.apache.solr.core.PluginBag;
 import org.apache.solr.core.PluginInfo;
 import org.apache.solr.core.SolrInfoMBean;
+import org.apache.solr.metrics.SolrMetricManager;
+import org.apache.solr.metrics.SolrMetricProducer;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestHandler;
 import org.apache.solr.response.SolrQueryResponse;
@@ -43,7 +48,7 @@ import static org.apache.solr.core.RequestParams.USEPARAM;
 /**
  *
  */
-public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoMBean, NestedRequestHandler {
+public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoMBean, SolrMetricProducer, NestedRequestHandler {
 
   protected NamedList initArgs = null;
   protected SolrParams defaults;
@@ -52,11 +57,12 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
   protected boolean httpCaching = true;
 
   // Statistics
-  private final LongAdder numRequests = new LongAdder();
-  private final LongAdder numServerErrors = new LongAdder();
-  private final LongAdder numClientErrors = new LongAdder();
-  private final LongAdder numTimeouts = new LongAdder();
-  private final Timer requestTimes = new Timer();
+  private Meter numErrors = new Meter();
+  private Meter numServerErrors = new Meter();
+  private Meter numClientErrors = new Meter();
+  private Meter numTimeouts = new Meter();
+  private Counter requests = new Counter();
+  private Timer requestTimes = new Timer();
 
   private final long handlerStart;
 
@@ -126,6 +132,17 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
 
   }
 
+  @Override
+  public Collection<String> initializeMetrics(String registryName, String scope) {
+    numErrors = SolrMetricManager.getOrCreateMeter(registryName, "errors", getCategory().toString(), scope);
+    numServerErrors = SolrMetricManager.getOrCreateMeter(registryName, "serverErrors", getCategory().toString(), scope);
+    numClientErrors = SolrMetricManager.getOrCreateMeter(registryName, "clientErrors", getCategory().toString(), scope);
+    numTimeouts = SolrMetricManager.getOrCreateMeter(registryName, "timeouts", getCategory().toString(), scope);
+    requests = SolrMetricManager.getOrCreateCounter(registryName, "requests", getCategory().toString(), scope);
+    requestTimes = SolrMetricManager.getOrCreateTimer(registryName, "requestTimes", getCategory().toString(), scope);
+    return Arrays.asList("errors", "serverErrors", "clientErrors", "timeouts", "requestTimes", "requests");
+  }
+
   public static SolrParams getSolrParamsFromNamedList(NamedList args, String key) {
     Object o = args.get(key);
     if (o != null && o instanceof NamedList) {
@@ -142,7 +159,7 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
 
   @Override
   public void handleRequest(SolrQueryRequest req, SolrQueryResponse rsp) {
-    numRequests.increment();
+    requests.inc();
     Timer.Context timer = requestTimes.time();
     try {
       if(pluginInfo != null && pluginInfo.attributes.containsKey(USEPARAM)) req.getContext().put(USEPARAM,pluginInfo.attributes.get(USEPARAM));
@@ -156,7 +173,7 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
         Object partialResults = header.get(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY);
         boolean timedOut = partialResults == null ? false : (Boolean)partialResults;
         if( timedOut ) {
-          numTimeouts.increment();
+          numTimeouts.mark();
           rsp.setHttpCaching(false);
         }
       }
@@ -182,14 +199,14 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
       if (incrementErrors) {
         SolrException.log(log, e);
 
+        numErrors.mark();
         if (isServerError) {
-          numServerErrors.increment();
+          numServerErrors.mark();
         } else {
-          numClientErrors.increment();
+          numClientErrors.mark();
         }
       }
-    }
-    finally {
+    } finally {
       timer.stop();
     }
   }
@@ -268,15 +285,14 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
   public NamedList<Object> getStatistics() {
     NamedList<Object> lst = new SimpleOrderedMap<>();
     lst.add("handlerStart",handlerStart);
-    lst.add("requests", numRequests.longValue());
-    lst.add("errors", numServerErrors.longValue() + numClientErrors.longValue());
-    lst.add("serverErrors", numServerErrors.longValue());
-    lst.add("clientErrors", numClientErrors.longValue());
-    lst.add("timeouts", numTimeouts.longValue());
+    lst.add("requests", requests.getCount());
+    lst.add("errors", numErrors.getCount());
+    lst.add("serverErrors", numServerErrors.getCount());
+    lst.add("clientErrors", numClientErrors.getCount());
+    lst.add("timeouts", numTimeouts.getCount());
     TimerUtils.addMetrics(lst, requestTimes);
     return lst;
   }
-
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
new file mode 100644
index 0000000..d7322a3
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
@@ -0,0 +1,161 @@
+/*
+ * 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.solr.metrics;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.lang.invoke.MethodHandles;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.codahale.metrics.MetricRegistry;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrResourceLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Responsible for collecting metrics from {@link SolrMetricProducer}'s
+ * and exposing metrics to {@link SolrMetricReporter}'s.
+ */
+public class SolrCoreMetricManager implements Closeable {
+
+  public static final String REGISTRY_PREFIX = "core";
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private final SolrCore core;
+  private final String registryName;
+  private final ConcurrentHashMap<String, SolrMetricReporter> reporters;
+
+  /**
+   * Constructs a metric manager.
+   *
+   * @param core the metric manager's core
+   */
+  public SolrCoreMetricManager(SolrCore core) {
+    this.core = core;
+    this.registryName = MetricRegistry.name(REGISTRY_PREFIX, core.getName());
+    this.reporters = new ConcurrentHashMap<>();
+  }
+
+  /**
+   * Registers a mapping of name/metric's with the manager's metric registry.
+   * If a metric with the same metric name has already been registered, this method
+   * replaces the original metric with the new metric.
+   *
+   * @param scope     the scope of the metrics to be registered (e.g. `/admin/ping`)
+   * @param producer  producer of metrics to be registered
+   */
+  public void registerMetricProducer(String scope, SolrMetricProducer producer) {
+    if (scope == null || producer == null || producer.getCategory() == null) {
+      throw new IllegalArgumentException("registerMetricProducer() called with illegal arguments: " +
+          "scope = " + scope + ", producer = " + producer);
+    }
+    Collection<String> registered = producer.initializeMetrics(registryName, scope);
+    if (registered == null || registered.isEmpty()) {
+      throw new IllegalArgumentException("registerMetricProducer() did not register any metrics " +
+      "for scope = " + scope + ", producer = " + producer);
+    }
+  }
+
+  /**
+   * Loads a reporter and registers it to listen to the manager's metric registry.
+   * If a reporter with the same name has already been registered, this method
+   * closes the original reporter and registers the new reporter.
+   *
+   * @param pluginInfo the configuration of the reporter
+   * @throws IOException if the reporter could not be loaded
+   */
+  public void loadReporter(PluginInfo pluginInfo) throws IOException {
+    if (pluginInfo == null) {
+      throw new IllegalArgumentException("loadReporter called with null plugin info.");
+    }
+
+    SolrResourceLoader resourceLoader = core.getResourceLoader();
+    SolrMetricReporter reporter = resourceLoader.newInstance(
+      pluginInfo.className,
+      SolrMetricReporter.class,
+      new String[0],
+      new Class[] { String.class },
+      new Object[] { registryName }
+    );
+
+    try {
+      reporter.init(pluginInfo);
+    } catch (IllegalStateException e) {
+      throw new IllegalArgumentException("loadReporter called with invalid plugin info = " + pluginInfo);
+    }
+
+    SolrMetricReporter existing = reporters.putIfAbsent(pluginInfo.name, reporter);
+    if (existing != null) {
+      log.warn("{} has already been register; replacing existing reporter = {}.", pluginInfo.name, existing);
+      synchronized (reporters) {
+        reporters.get(pluginInfo.name).close(); // Get the existing reporter again in case it was replaced
+        reporters.put(pluginInfo.name, reporter); // Replace the existing reporter with the new one
+      }
+    }
+
+    log.info("{} is successfully registered.", pluginInfo.name);
+  }
+
+  /**
+   * Closes registered reporters.
+   */
+  @Override
+  public void close() throws IOException {
+    // Close reporters first to ensure no reporter is listening to the registry.
+    Iterator<Map.Entry<String, SolrMetricReporter>> it = reporters.entrySet().iterator();
+    while (it.hasNext()) {
+      Map.Entry<String, SolrMetricReporter> entry = it.next();
+      entry.getValue().close();
+      it.remove();
+    }
+  }
+
+  /**
+   * Retrieves the solr core of the manager.
+   *
+   * @return the solr core of the manager.
+   */
+  public SolrCore getCore() {
+    return core;
+  }
+
+  /**
+   * Retrieves the metric registry name of the manager.
+   *
+   * @return the metric registry name of the manager.
+   */
+  public String getRegistryName() {
+    return registryName;
+  }
+
+  /**
+   * Retrieves all registered reporters and their names.
+   *
+   * @return all registered reporters and their names
+   */
+  public Map<String, SolrMetricReporter> getReporters() {
+    return Collections.unmodifiableMap(reporters);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/SolrMetricInfo.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricInfo.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricInfo.java
new file mode 100644
index 0000000..f0bc8a1
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricInfo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.solr.metrics;
+
+import com.codahale.metrics.MetricRegistry;
+import org.apache.solr.core.SolrInfoMBean;
+
+/**
+ * Wraps meta-data for a metric.
+ */
+public final class SolrMetricInfo {
+  public final String name;
+  public final String scope;
+  public final SolrInfoMBean.Category category;
+
+  /**
+   * Creates a new instance of {@link SolrMetricInfo}.
+   *
+   * @param category the category of the metric (e.g. `QUERYHANDLERS`)
+   * @param scope    the scope of the metric (e.g. `/admin/ping`)
+   * @param name     the name of the metric (e.g. `Requests`)
+   */
+  public SolrMetricInfo(SolrInfoMBean.Category category, String scope, String name) {
+    this.name = name;
+    this.scope = scope;
+    this.category = category;
+  }
+
+  public static SolrMetricInfo of(String fullName) {
+    if (fullName == null || fullName.isEmpty()) {
+      return null;
+    }
+    String[] names = fullName.split("\\.");
+    if (names.length < 3) { // not a valid info
+      return null;
+    }
+    // check top-level name for valid category
+    SolrInfoMBean.Category category;
+    try {
+      category = SolrInfoMBean.Category.valueOf(names[0]);
+    } catch (IllegalArgumentException e) { // not a valid category
+      return null;
+    }
+    String scope = names[1];
+    String name = fullName.substring(names[0].length() + names[1].length() + 2);
+    return new SolrMetricInfo(category, scope, name);
+  }
+
+  /**
+   * Returns the metric name defined by this object.
+   * For example, if the name is `Requests`, scope is `/admin/ping`,
+   * and category is `QUERYHANDLERS`, then the metric name is
+   * `QUERYHANDLERS./admin/ping.Requests`.
+   *
+   * @return the metric name defined by this object
+   */
+  public String getMetricName() {
+    return MetricRegistry.name(category.toString(), scope, name);
+  }
+
+  @Override
+  public String toString() {
+    return "SolrMetricInfo{" +
+        "name='" + name + '\'' +
+        ", scope='" + scope + '\'' +
+        ", category=" + category +
+        '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    SolrMetricInfo that = (SolrMetricInfo) o;
+
+    if (name != null ? !name.equals(that.name) : that.name != null) return false;
+    if (scope != null ? !scope.equals(that.scope) : that.scope != null) return false;
+    return category == that.category;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = name != null ? name.hashCode() : 0;
+    result = 31 * result + (scope != null ? scope.hashCode() : 0);
+    result = 31 * result + (category != null ? category.hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
new file mode 100644
index 0000000..872db18
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.metrics;
+
+import java.util.Set;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.SharedMetricRegistries;
+import com.codahale.metrics.Snapshot;
+import com.codahale.metrics.Timer;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ *
+ */
+public class SolrMetricManager {
+
+  public static final String REGISTRY_NAME_PREFIX = "solr";
+  public static final String DEFAULT_REGISTRY = MetricRegistry.name(REGISTRY_NAME_PREFIX, "default");
+
+  // don't create instances of this class
+  private SolrMetricManager() { }
+
+
+  /**
+   * Return a set of existing registry names.
+   */
+  public static Set<String> registryNames() {
+    return SharedMetricRegistries.names();
+  }
+
+  /**
+   * Get (or create if not present) a named registry
+   * @param registry name of the registry
+   * @return existing or newly created registry
+   */
+  public static MetricRegistry registryFor(String registry) {
+    return SharedMetricRegistries.getOrCreate(overridableRegistryName(registry));
+  }
+
+  /**
+   * Remove all metrics from a specified registry.
+   * @param registry registry name
+   */
+  public static void clearRegistryFor(String registry) {
+    SharedMetricRegistries.getOrCreate(overridableRegistryName(registry)).removeMatching(MetricFilter.ALL);
+  }
+
+  /**
+   * Remove a single specific metric from a named registry
+   * @param registry registry name
+   * @param metricName metric name, either final name or a fully-qualified name
+   *                   using dotted notation
+   * @param metricPath (optional) additional top-most metric name path elements
+   */
+  public static void clearMetric(String registry, String metricName, String... metricPath) {
+    SharedMetricRegistries.getOrCreate(overridableRegistryName(registry)).
+        remove(mkName(metricName, metricPath));
+  }
+
+  /**
+   * Create or get an existing named {@link Meter}
+   * @param registry registry name
+   * @param metricName metric name, either final name or a fully-qualified name
+   *                   using dotted notation
+   * @param metricPath (optional) additional top-most metric name path elements
+   * @return existing or a newly created {@link Meter}
+   */
+  public static Meter getOrCreateMeter(String registry, String metricName, String... metricPath) {
+    return SharedMetricRegistries.getOrCreate(overridableRegistryName(registry)).
+        meter(mkName(metricName, metricPath));
+  }
+
+  /**
+   * Create or get an existing named {@link Timer}
+   * @param registry registry name
+   * @param metricName metric name, either final name or a fully-qualified name
+   *                   using dotted notation
+   * @param metricPath (optional) additional top-most metric name path elements
+   * @return existing or a newly created {@link Timer}
+   */
+  public static Timer getOrCreateTimer(String registry, String metricName, String... metricPath) {
+    return SharedMetricRegistries.getOrCreate(overridableRegistryName(registry)).
+        timer(mkName(metricName, metricPath));
+  }
+
+  /**
+   * Create or get an existing named {@link Counter}
+   * @param registry registry name
+   * @param metricName metric name, either final name or a fully-qualified name
+   *                   using dotted notation
+   * @param metricPath (optional) additional top-most metric name path elements
+   * @return existing or a newly created {@link Counter}
+   */
+  public static Counter getOrCreateCounter(String registry, String metricName, String... metricPath) {
+    return SharedMetricRegistries.getOrCreate(overridableRegistryName(registry)).
+        counter(mkName(metricName, metricPath));
+  }
+
+  /**
+   * Create or get an existing named {@link Histogram}
+   * @param registry registry name
+   * @param metricName metric name, either final name or a fully-qualified name
+   *                   using dotted notation
+   * @param metricPath (optional) additional top-most metric name path elements
+   * @return existing or a newly created {@link Histogram}
+   */
+  public static Histogram getOrCreateHistogram(String registry, String metricName, String... metricPath) {
+    return SharedMetricRegistries.getOrCreate(overridableRegistryName(registry)).
+        histogram(mkName(metricName, metricPath));
+  }
+
+  /**
+   * This method creates a hierarchical name with arbitrary levels of hierarchy
+   * @param name the final segment of the name, must not be null or empty.
+   * @param path optional path segments, starting from the top level. Empty or null
+   *             segments will be skipped.
+   * @return fully-qualified name using dotted notation, with all valid hierarchy
+   * segments prepended to the name.
+   */
+  public static String mkName(String name, String... path) {
+    if (name == null || name.isEmpty()) {
+      throw new IllegalArgumentException("name must not be empty");
+    }
+    if (path == null || path.length == 0) {
+      return name;
+    } else {
+      StringBuilder sb = new StringBuilder();
+      for (String s : path) {
+        if (s == null || s.isEmpty()) {
+          continue;
+        }
+        if (sb.length() > 0) {
+          sb.append('.');
+        }
+        sb.append(s);
+      }
+      if (sb.length() > 0) {
+        sb.append('.');
+      }
+      sb.append(name);
+      return sb.toString();
+    }
+  }
+
+  /**
+   * Allows named registries to be renamed using System properties.
+   * This would be mostly be useful if you want to combine the metrics from a few registries for a single
+   * reporter.
+   * @param registry The name of the registry
+   * @return A potentially overridden (via System properties) registry name
+   */
+  public static String overridableRegistryName(String registry) {
+    String fqRegistry = enforcePrefix(registry);
+    return enforcePrefix(System.getProperty(fqRegistry,fqRegistry));
+  }
+
+  /**
+   * Enforces the leading {@link #REGISTRY_NAME_PREFIX} in a name.
+   * @param name input name, possibly without the prefix
+   * @return original name if it contained the prefix, or the
+   * input name with the prefix prepended.
+   */
+  public static String enforcePrefix(String name) {
+    if (name.startsWith(REGISTRY_NAME_PREFIX))
+      return name;
+    else
+      return MetricRegistry.name(REGISTRY_NAME_PREFIX, name);
+  }
+
+  /**
+   * Timers return measurements as a double representing nanos.
+   * This converts that to a double representing millis.
+   * @param ns Nanoseconds
+   * @return Milliseconds
+   */
+  public static double nsToMs(double ns) {
+    return ns / 1000000;  // Using TimeUnit involves casting to Long, so don't bother.
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java
new file mode 100644
index 0000000..929e6a2
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java
@@ -0,0 +1,37 @@
+/*
+ * 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.solr.metrics;
+
+import java.util.Collection;
+
+import org.apache.solr.core.SolrInfoMBean;
+
+/**
+ * Extension of {@link SolrInfoMBean} for use by objects that
+ * expose metrics through {@link SolrCoreMetricManager}.
+ */
+public interface SolrMetricProducer extends SolrInfoMBean {
+
+  /**
+   * Initializes metrics specific to this producer
+   * @param registry registry name where metrics are registered
+   * @param scope scope of the metrics (eg. handler name) to separate metrics of
+   *              instances of the same component executing in different contexts
+   * @return registered (or existing) unqualified names of metrics specific to this producer.
+   */
+  Collection<String> initializeMetrics(String registry, String scope);
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
new file mode 100644
index 0000000..0adb7bf
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.solr.metrics;
+
+import java.io.Closeable;
+import java.util.Locale;
+
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.util.SolrPluginUtils;
+import org.apache.solr.util.plugin.PluginInfoInitialized;
+
+/**
+ * Interface for 'pluggable' metric reporters.
+ */
+public abstract class SolrMetricReporter implements Closeable, PluginInfoInitialized {
+
+  protected final String registryName;
+
+  /**
+   * Create a reporter for metrics managed in a named registry.
+   * @param registryName registry to use, one of registries managed by
+   *                     {@link SolrMetricManager}
+   */
+  protected SolrMetricReporter(String registryName) {
+    this.registryName = registryName;
+  }
+  /**
+   * Initializes a {@link SolrMetricReporter} with the plugin's configuration.
+   *
+   * @param pluginInfo the plugin's configuration
+   */
+  @SuppressWarnings("unchecked")
+  public void init(PluginInfo pluginInfo) {
+    if (pluginInfo != null && pluginInfo.initArgs != null) {
+      SolrPluginUtils.invokeSetters(this, pluginInfo.initArgs);
+    }
+
+    validate();
+  }
+
+  /**
+   * Validates that the reporter has been correctly configured.
+   *
+   * @throws IllegalStateException if the reporter is not properly configured
+   */
+  protected abstract void validate() throws IllegalStateException;
+
+  @Override
+  public String toString() {
+    return String.format(Locale.ENGLISH, "[%s@%s]", getClass().getName(), Integer.toHexString(hashCode()));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/package-info.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/package-info.java b/solr/core/src/java/org/apache/solr/metrics/package-info.java
new file mode 100644
index 0000000..a007b8b
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/package-info.java
@@ -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.
+ */
+
+/**
+ * The {@link org.apache.solr.metrics.SolrCoreMetricManager} is responsible for
+ * collecting metrics from {@link org.apache.solr.metrics.SolrMetricProducer}'s
+ * and exposing metrics to {@link org.apache.solr.metrics.SolrMetricReporter}'s.
+ */
+package org.apache.solr.metrics;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
new file mode 100644
index 0000000..1bedc56
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
@@ -0,0 +1,254 @@
+/*
+ * 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.solr.metrics.reporters;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Hashtable;
+import java.util.Locale;
+
+import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.ObjectNameFactory;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.metrics.SolrMetricInfo;
+import org.apache.solr.metrics.SolrCoreMetricManager;
+import org.apache.solr.metrics.SolrMetricManager;
+import org.apache.solr.metrics.SolrMetricReporter;
+import org.apache.solr.util.JmxUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link SolrMetricReporter} that finds (or creates) a MBeanServer from
+ * the given configuration and registers metrics to it with JMX.
+ */
+public class SolrJmxReporter extends SolrMetricReporter {
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private String domain;
+  private String agentId;
+  private String serviceUrl;
+
+  private JmxReporter reporter;
+  private MBeanServer mBeanServer;
+
+  /**
+   * Creates a new instance of {@link SolrJmxReporter}.
+   *
+   * @param registryName name of the registry to report
+   */
+  public SolrJmxReporter(String registryName) {
+    super(registryName);
+    this.domain = registryName;
+  }
+
+  /**
+   * Initializes the reporter by finding (or creating) a MBeanServer
+   * and registering the metricManager's metric registry.
+   *
+   * @param pluginInfo the configuration for the reporter
+   */
+  @Override
+  public synchronized void init(PluginInfo pluginInfo) {
+    super.init(pluginInfo);
+
+    if (serviceUrl != null && agentId != null) {
+      mBeanServer = JmxUtil.findFirstMBeanServer();
+      log.warn("No more than one of serviceUrl(%s) and agentId(%s) should be configured, using first MBeanServer instead of configuration.",
+          serviceUrl, agentId, mBeanServer);
+    }
+    else if (serviceUrl != null) {
+      try {
+        mBeanServer = JmxUtil.findMBeanServerForServiceUrl(serviceUrl);
+      } catch (IOException e) {
+        log.warn("findMBeanServerForServiceUrl(%s) exception: %s", serviceUrl, e);
+        mBeanServer = null;
+      }
+    }
+    else if (agentId != null) {
+      mBeanServer = JmxUtil.findMBeanServerForAgentId(agentId);
+    } else {
+      mBeanServer = JmxUtil.findFirstMBeanServer();
+      log.warn("No serviceUrl or agentId was configured, using first MBeanServer.", mBeanServer);
+    }
+
+    if (mBeanServer == null) {
+      log.warn("No JMX server found. Not exposing Solr metrics.");
+      return;
+    }
+
+    JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(registryName);
+
+    reporter = JmxReporter.forRegistry(SolrMetricManager.registryFor(registryName))
+                          .registerWith(mBeanServer)
+                          .inDomain(domain)
+                          .createsObjectNamesWith(jmxObjectNameFactory)
+                          .build();
+    reporter.start();
+
+    log.info("JMX monitoring enabled at server: " + mBeanServer);
+  }
+
+  /**
+   * Stops the reporter from publishing metrics.
+   */
+  @Override
+  public synchronized void close() {
+    if (reporter != null) {
+      reporter.stop();
+      // TODO: stop() vs. close() // change or add comment re: why stop instead of close is called
+      // maybe TODO: reporter = null;
+    }
+  }
+
+  /**
+   * Validates that the reporter has been correctly configured.
+   * Note that all configurable arguments are currently optional.
+   *
+   * @throws IllegalStateException if the reporter is not properly configured
+   */
+  @Override
+  protected void validate() throws IllegalStateException {
+    // Nothing to validate
+  }
+
+  /**
+   * Sets the domain with which MBeans are published. If none is set,
+   * the domain defaults to the name of the core.
+   *
+   * @param domain the domain
+   */
+  public void setDomain(String domain) {
+    if (domain != null) {
+      this.domain = domain;
+    } else {
+      this.domain = registryName;
+    }
+  }
+
+  /**
+   * Sets the service url for a JMX server.
+   * Note that this configuration is optional.
+   *
+   * @param serviceUrl the service url
+   */
+  public void setServiceUrl(String serviceUrl) {
+    this.serviceUrl = serviceUrl;
+  }
+
+  /**
+   * Sets the agent id for a JMX server.
+   * Note that this configuration is optional.
+   *
+   * @param agentId the agent id
+   */
+  public void setAgentId(String agentId) {
+    this.agentId = agentId;
+  }
+
+  /**
+   * Retrieves the reporter's MBeanServer.
+   *
+   * @return the reporter's MBeanServer
+   */
+  public MBeanServer getMBeanServer() {
+    return mBeanServer;
+  }
+
+  @Override
+  public String toString() {
+    return String.format(Locale.ENGLISH, "[%s@%s: domain = %s, service url = %s, agent id = %s]",
+        getClass().getName(), Integer.toHexString(hashCode()), domain, serviceUrl, agentId);
+  }
+
+  /**
+   * Factory to create MBean names for a given metric.
+   */
+  private static class JmxObjectNameFactory implements ObjectNameFactory {
+
+    private final String registryName;
+
+    JmxObjectNameFactory(String registryName) {
+      this.registryName = registryName;
+    }
+
+    /**
+     * TODO description
+     *
+     * @param type    TODO description, example
+     * @param domain  TODO description, example
+     * @param name    TODO description, example
+     */
+    @Override
+    public ObjectName createName(String type, String domain, String name) {
+      SolrMetricInfo metricInfo = SolrMetricInfo.of(name);
+
+      // It turns out that ObjectName(String) mostly preserves key ordering
+      // as specified in the constructor (except for the 'type' key that ends
+      // up at top level) - unlike ObjectName(String, Map) constructor
+      // that seems to have a mind of its own...
+      StringBuilder sb = new StringBuilder(domain);
+      sb.append(':');
+      if (metricInfo != null) {
+        sb.append("category=");
+        sb.append(metricInfo.category.toString());
+        sb.append(",scope=");
+        sb.append(metricInfo.scope);
+        // split by type, but don't call it 'type' :)
+        sb.append(",class=");
+        sb.append(type);
+        sb.append(",name=");
+        sb.append(metricInfo.name);
+      } else {
+        // make dotted names into hierarchies
+        String[] path = name.split("\\.");
+        for (int i = 0; i < path.length - 1; i++) {
+          if (i > 0) {
+            sb.append(',');
+          }
+          sb.append("name"); sb.append(String.valueOf(i));
+          sb.append('=');
+          sb.append(path[i]);
+        }
+        if (path.length > 1) {
+          sb.append(',');
+        }
+        // split by type
+        sb.append("class=");
+        sb.append(type);
+        sb.append(",name=");
+        sb.append(path[path.length - 1]);
+      }
+
+      ObjectName objectName;
+
+      try {
+        objectName = new ObjectName(sb.toString());
+      } catch (MalformedObjectNameException e) {
+        throw new RuntimeException(e);
+      }
+
+      return objectName;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/metrics/reporters/package-info.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/package-info.java b/solr/core/src/java/org/apache/solr/metrics/reporters/package-info.java
new file mode 100644
index 0000000..5ad7a80
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * This package houses 'pluggable' metric reporters that
+ * inherit from the {@link org.apache.solr.metrics.SolrMetricReporter} class.
+ */
+package org.apache.solr.metrics.reporters;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/java/org/apache/solr/util/JmxUtil.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/JmxUtil.java b/solr/core/src/java/org/apache/solr/util/JmxUtil.java
new file mode 100644
index 0000000..2224022
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/util/JmxUtil.java
@@ -0,0 +1,93 @@
+/*
+ * 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.solr.util;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.List;
+
+/**
+ * Utility methods to find a MBeanServer.
+ *
+ * This was factored out from {@link org.apache.solr.core.JmxMonitoredMap}
+ * and can eventually replace the logic used there.
+ */
+public final class JmxUtil {
+
+  /**
+   * Retrieve the first MBeanServer found.
+   *
+   * @return the first MBeanServer found
+   */
+  public static MBeanServer findFirstMBeanServer() {
+    // TODO: does findMBeanServerForAgentId need the getPlatformMBeanServer call also?
+    // TODO: can findFirstMBeanServer be implemented as findMBeanServerForAgentId(null)?
+    // Ensure we have at least one MBeanServer available
+    MBeanServer platformServer = ManagementFactory.getPlatformMBeanServer();
+
+    List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
+    if (servers == null || servers.isEmpty()) {
+      return null;
+    }
+
+    return servers.get(0);
+  }
+
+  /**
+   * Find a MBeanServer given a service url.
+   *
+   * @param serviceUrl the service url
+   * @return a MBeanServer
+   */
+  public static MBeanServer findMBeanServerForServiceUrl(String serviceUrl) throws IOException {
+    if (serviceUrl == null) {
+      return null;
+    }
+
+    MBeanServer server = MBeanServerFactory.newMBeanServer();
+    JMXConnectorServer connector = JMXConnectorServerFactory
+        .newJMXConnectorServer(new JMXServiceURL(serviceUrl), null, server);
+    connector.start();
+
+    return server;
+  }
+
+  /**
+   * Find a MBeanServer given an agent id.
+   *
+   * @param agentId the agent id
+   * @return a MBeanServer
+   */
+  public static MBeanServer findMBeanServerForAgentId(String agentId) {
+    if (agentId == null) {
+      return null;
+    }
+
+    List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(agentId);
+    if (servers == null || servers.isEmpty()) {
+      return null;
+    }
+
+    return servers.get(0);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricManagerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricManagerTest.java b/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricManagerTest.java
new file mode 100644
index 0000000..d046916
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricManagerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.solr.metrics;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricRegistry;
+import org.apache.lucene.util.TestUtil;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.SolrInfoMBean;
+import org.apache.solr.metrics.reporters.SolrJmxReporter;
+import org.apache.solr.schema.FieldType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SolrCoreMetricManagerTest extends SolrTestCaseJ4 {
+  private static final int MAX_ITERATIONS = 100;
+
+  private SolrCoreMetricManager metricManager;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-basic.xml", "schema.xml");
+  }
+
+  @Before
+  public void beforeTest() {
+    metricManager = new SolrCoreMetricManager(h.getCore());
+  }
+
+  @After
+  public void afterTest() throws IOException {
+    metricManager.close();
+    assertTrue(metricManager.getReporters().isEmpty());
+  }
+
+  @Test
+  public void testRegisterMetrics() {
+    Random random = random();
+
+    String scope = SolrMetricTestUtils.getRandomScope(random);
+    SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random);
+    Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(random);
+    SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(category, scope, metrics);
+    try {
+      metricManager.registerMetricProducer(scope, producer);
+      assertNotNull(scope);
+      assertNotNull(category);
+      assertNotNull(metrics);
+      assertRegistered(scope, metrics, metricManager);
+    } catch (final IllegalArgumentException e) {
+      assertTrue("expected at least one null but got: scope="+scope+" category="+category+" metrics="+metrics,
+          (scope == null || category == null || metrics == null));
+      assertRegistered(scope, new HashMap<>(), metricManager);
+    }
+  }
+
+  @Test
+  public void testRegisterMetricsWithReplacements() {
+    Random random = random();
+
+    Map<String, Counter> registered = new HashMap<>();
+    String scope = SolrMetricTestUtils.getRandomScope(random, true);
+    SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random, true);
+
+    int iterations = TestUtil.nextInt(random, 0, MAX_ITERATIONS);
+    for (int i = 0; i < iterations; ++i) {
+      Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetricsWithReplacements(random, registered);
+      if (metrics.isEmpty()) {
+        continue;
+      }
+      SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(category, scope, metrics);
+      metricManager.registerMetricProducer(scope, producer);
+      registered.putAll(metrics);
+      assertRegistered(scope, registered, metricManager);
+    }
+  }
+
+  @Test
+  public void testLoadReporter() throws Exception {
+    Random random = random();
+
+    String className = SolrJmxReporter.class.getName();
+    String reporterName = TestUtil.randomUnicodeString(random);
+
+    Map<String, Object> attrs = new HashMap<>();
+    attrs.put(FieldType.CLASS_NAME, className);
+    attrs.put(CoreAdminParams.NAME, reporterName);
+
+    boolean shouldDefinePlugin = random.nextBoolean();
+    PluginInfo pluginInfo = shouldDefinePlugin ? new PluginInfo(TestUtil.randomUnicodeString(random), attrs) : null;
+
+    try {
+      metricManager.loadReporter(pluginInfo);
+      assertNotNull(pluginInfo);
+      assertEquals(1, metricManager.getReporters().size());
+      assertNotNull(metricManager.getReporters().get(reporterName));
+      assertTrue(metricManager.getReporters().get(reporterName) instanceof SolrJmxReporter);
+    } catch (IllegalArgumentException e) {
+      assertNull(pluginInfo);
+      assertTrue(metricManager.getReporters().isEmpty());
+    }
+  }
+
+  private static void assertRegistered(String scope, Map<String, Counter> newMetrics, SolrCoreMetricManager metricManager) {
+    if (scope == null) {
+      return;
+    }
+    String filter = "." + scope + ".";
+    MetricRegistry registry = SolrMetricManager.registryFor(metricManager.getRegistryName());
+    assertEquals(newMetrics.size(), registry.getMetrics().
+        keySet().stream().filter(s -> s.contains(filter)).count());
+
+    Map<String, Metric> registeredMetrics = registry.getMetrics().
+        entrySet().stream().filter(e -> e.getKey() != null && e.getKey().contains(filter)).
+        collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+    for (Map.Entry<String, Metric> entry : registeredMetrics.entrySet()) {
+      String name = entry.getKey();
+      Metric expectedMetric = entry.getValue();
+
+      Metric actualMetric = registry.getMetrics().get(name);
+
+      assertNotNull(actualMetric);
+      assertEquals(expectedMetric, actualMetric);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
new file mode 100644
index 0000000..76344c4
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.solr.metrics;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import org.apache.lucene.util.TestUtil;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.schema.FieldType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SolrMetricReporterTest extends SolrTestCaseJ4 {
+
+  private SolrCoreMetricManager metricManager;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-basic.xml", "schema.xml");
+  }
+
+  @Before
+  public void beforeTest() {
+    metricManager = new SolrCoreMetricManager(h.getCore());
+  }
+
+  @After
+  public void afterTest() throws Exception {
+    metricManager.close();
+  }
+
+  @Test
+  public void testInit() throws Exception {
+    Random random = random();
+
+    MockReporter reporter = new MockReporter(h.getCore().getName());
+
+    Map<String, Object> attrs = new HashMap<>();
+    attrs.put(FieldType.CLASS_NAME, MockReporter.class.getName());
+    attrs.put(CoreAdminParams.NAME, TestUtil.randomUnicodeString(random));
+
+    boolean shouldDefineConfigurable = random.nextBoolean();
+    String configurable = TestUtil.randomUnicodeString(random);
+    if (shouldDefineConfigurable) attrs.put("configurable", configurable);
+
+    boolean shouldDefinePlugin = random.nextBoolean();
+    String type = TestUtil.randomUnicodeString(random);
+    PluginInfo pluginInfo = shouldDefinePlugin ? new PluginInfo(type, attrs) : null;
+
+    try {
+      reporter.init(pluginInfo);
+      assertTrue(reporter.didValidate);
+      assertNotNull(reporter.configurable);
+      assertEquals(configurable, reporter.configurable);
+      assertTrue(pluginInfo != null && attrs.get("configurable") == configurable);
+    } catch (IllegalStateException e) {
+      assertTrue(reporter.didValidate);
+      assertNull(reporter.configurable);
+      assertTrue(pluginInfo == null || attrs.get("configurable") == null);
+    } finally {
+      reporter.close();
+    }
+  }
+
+  public static class MockReporter extends SolrMetricReporter {
+    String configurable;
+    boolean didValidate = false;
+
+    MockReporter(String registryName) {
+      super(registryName);
+    }
+
+    @Override
+    public void init(PluginInfo pluginInfo) {
+      super.init(pluginInfo);
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+
+    @Override
+    protected void validate() throws IllegalStateException {
+      didValidate = true;
+      if (configurable == null) {
+        throw new IllegalStateException("MockReporter::configurable not configured.");
+      }
+    }
+
+    public void setConfigurable(String configurable) {
+      this.configurable = configurable;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/test/org/apache/solr/metrics/SolrMetricTestUtils.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrMetricTestUtils.java b/solr/core/src/test/org/apache/solr/metrics/SolrMetricTestUtils.java
new file mode 100644
index 0000000..7f69461
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/metrics/SolrMetricTestUtils.java
@@ -0,0 +1,142 @@
+/*
+ * 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.solr.metrics;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricRegistry;
+import org.apache.lucene.util.TestUtil;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrInfoMBean;
+
+public final class SolrMetricTestUtils {
+
+  private static final int                    MAX_ITERATIONS = 100;
+  private static final SolrInfoMBean.Category CATEGORIES[]   = SolrInfoMBean.Category.values();
+
+  public static String getRandomScope(Random random) {
+    return getRandomScope(random, random.nextBoolean());
+  }
+
+  public static String getRandomScope(Random random, boolean shouldDefineScope) {
+    return shouldDefineScope ? TestUtil.randomSimpleString(random, 1, 10) : null; // must be simple string for JMX publishing
+  }
+
+  public static SolrInfoMBean.Category getRandomCategory(Random random) {
+    return getRandomCategory(random, random.nextBoolean());
+  }
+
+  public static SolrInfoMBean.Category getRandomCategory(Random random, boolean shouldDefineCategory) {
+    return shouldDefineCategory ? CATEGORIES[TestUtil.nextInt(random, 0, CATEGORIES.length - 1)] : null;
+  }
+
+  public static Map<String, Counter> getRandomMetrics(Random random) {
+    return getRandomMetrics(random, random.nextBoolean());
+  }
+
+  public static Map<String, Counter> getRandomMetrics(Random random, boolean shouldDefineMetrics) {
+    return shouldDefineMetrics ? getRandomMetricsWithReplacements(random, new HashMap<>()) : null;
+  }
+
+  public static final String SUFFIX = "_testing";
+
+  public static Map<String, Counter> getRandomMetricsWithReplacements(Random random, Map<String, Counter> existing) {
+    HashMap<String, Counter> metrics = new HashMap<>();
+    ArrayList<String> existingKeys = new ArrayList<>(existing.keySet());
+
+    int numMetrics = TestUtil.nextInt(random, 1, MAX_ITERATIONS);
+    for (int i = 0; i < numMetrics; ++i) {
+      boolean shouldReplaceMetric = !existing.isEmpty() && random.nextBoolean();
+      String name = shouldReplaceMetric
+          ? existingKeys.get(TestUtil.nextInt(random, 0, existingKeys.size() - 1))
+          : TestUtil.randomSimpleString(random, 1, 10) + SUFFIX; // must be simple string for JMX publishing
+
+      Counter counter = new Counter();
+      counter.inc(random.nextLong());
+      metrics.put(name, counter);
+    }
+
+    return metrics;
+  }
+
+  public static SolrMetricProducer getProducerOf(SolrInfoMBean.Category category, String scope, Map<String, Counter> metrics) {
+    return new SolrMetricProducer() {
+      @Override
+      public Collection<String> initializeMetrics(String registry, String scope) {
+        if (metrics == null || metrics.isEmpty()) {
+          return Collections.emptyList();
+        }
+        for (Map.Entry<String, Counter> entry : metrics.entrySet()) {
+          SolrMetricManager.getOrCreateCounter(registry, entry.getKey(), category.toString(), scope);
+        }
+        return metrics.keySet();
+      }
+
+      @Override
+      public String getName() {
+        return scope;
+      }
+
+      @Override
+      public String getVersion() {
+        return "0.0";
+      }
+
+      @Override
+      public String getDescription() {
+        return "foo";
+      }
+
+      @Override
+      public Category getCategory() {
+        return category;
+      }
+
+      @Override
+      public String getSource() {
+        return null;
+      }
+
+      @Override
+      public URL[] getDocs() {
+        return new URL[0];
+      }
+
+      @Override
+      public NamedList getStatistics() {
+        return null;
+      }
+
+      @Override
+      public String toString() {
+        return "SolrMetricProducer.of{" +
+            "\ncategory=" + category +
+            "\nscope=" + scope +
+            "\nmetrics=" + metrics +
+            "\n}";
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
new file mode 100644
index 0000000..23d15ed
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.solr.metrics.reporters;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricRegistryListener;
+import org.apache.lucene.util.TestUtil;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrInfoMBean;
+import org.apache.solr.metrics.SolrCoreMetricManager;
+import org.apache.solr.metrics.SolrMetricManager;
+import org.apache.solr.metrics.SolrMetricProducer;
+import org.apache.solr.metrics.SolrMetricTestUtils;
+import org.apache.solr.schema.FieldType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SolrJmxReporterTest extends SolrTestCaseJ4 {
+
+  private static final int MAX_ITERATIONS = 20;
+
+  private String domain;
+
+  private SolrCoreMetricManager metricManager;
+  private SolrJmxReporter reporter;
+  private MBeanServer mBeanServer;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-basic.xml", "schema.xml");
+  }
+
+  @Before
+  public void beforeTest() throws Exception {
+    // Ensure we have at least one MBeanServer available
+    MBeanServer platformServer = ManagementFactory.getPlatformMBeanServer();
+    // TODO: can we skip this call? either always or randomly?
+
+    Random random = random();
+
+    final SolrCore core = h.getCore();
+    domain = core.getName();
+
+    String className = SolrJmxReporter.class.getName();
+    String reporterName = TestUtil.randomUnicodeString(random);
+
+    Map<String, Object> attrs = new HashMap<>();
+    attrs.put(FieldType.CLASS_NAME, className);
+    attrs.put(CoreAdminParams.NAME, reporterName);
+
+    boolean shouldOverrideDomain = random.nextBoolean();
+    if (shouldOverrideDomain) {
+      domain = TestUtil.randomSimpleString(random);
+      attrs.put("domain", domain);
+    }
+
+    PluginInfo pluginInfo = new PluginInfo(TestUtil.randomUnicodeString(random), attrs);
+    metricManager = new SolrCoreMetricManager(core);
+    metricManager.loadReporter(pluginInfo);
+
+    assertEquals(1, metricManager.getReporters().size());
+    assertNotNull(metricManager.getReporters().get(reporterName));
+    assertTrue(metricManager.getReporters().get(reporterName) instanceof SolrJmxReporter);
+
+    reporter = (SolrJmxReporter) metricManager.getReporters().get(reporterName);
+    mBeanServer = reporter.getMBeanServer();
+    assertNotNull("MBean server not found.", mBeanServer);
+  }
+
+  @After
+  public void afterTest() throws Exception {
+    reporter.close();
+    Set<ObjectInstance> objects =
+        mBeanServer.queryMBeans(ObjectName.getInstance(domain + ":*"), null);
+    assertTrue(objects.isEmpty());
+
+    metricManager.close();
+  }
+
+  @Test
+  public void testReportMetrics() throws Exception {
+    Random random = random();
+
+    Map<String, Counter> registered = new HashMap<>();
+    String scope = SolrMetricTestUtils.getRandomScope(random, true);
+    SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random, true);
+
+    int iterations = TestUtil.nextInt(random, 0, MAX_ITERATIONS);
+    for (int i = 0; i < iterations; ++i) {
+      Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetricsWithReplacements(random, registered);
+      SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(category, scope, metrics);
+      metricManager.registerMetricProducer(scope, producer);
+      registered.putAll(metrics);
+      //waitForListener();
+      Set<ObjectInstance> objects = mBeanServer.queryMBeans(null, null);
+      assertEquals(registered.size(), objects.stream().
+          filter(o -> o.getObjectName().getKeyProperty("scope") != null &&
+              o.getObjectName().getKeyProperty("scope").equals(scope)).count());
+    }
+  }
+
+  @Test
+  public void testReloadCore() throws Exception {
+    Random random = random();
+
+    String scope = SolrMetricTestUtils.getRandomScope(random, true);
+    SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random, true);
+    Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(random, true);
+    SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(category, scope, metrics);
+    metricManager.registerMetricProducer(scope, producer);
+    //waitForListener();
+
+    Set<ObjectInstance> objects = mBeanServer.queryMBeans(null, null);
+    assertEquals(metrics.size(), objects.stream().
+        filter(o -> o.getObjectName().getKeyProperty("scope") != null &&
+            o.getObjectName().getKeyProperty("scope").equals(scope)).count());
+
+    h.getCoreContainer().reload(h.getCore().getName());
+    metricManager.registerMetricProducer(scope, producer);
+
+    objects = mBeanServer.queryMBeans(null, null);
+    assertEquals(metrics.size(), objects.stream().
+        filter(o -> o.getObjectName().getKeyProperty("scope") != null &&
+            o.getObjectName().getKeyProperty("scope").equals(scope)).count());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-jetty-LICENSE-ASL.txt
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-jetty-LICENSE-ASL.txt b/solr/licenses/metrics-jetty-LICENSE-ASL.txt
new file mode 100644
index 0000000..ccb320c
--- /dev/null
+++ b/solr/licenses/metrics-jetty-LICENSE-ASL.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (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 2010-2012 Coda Hale and Yammer, Inc.
+
+   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.
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-jetty-NOTICE.txt
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-jetty-NOTICE.txt b/solr/licenses/metrics-jetty-NOTICE.txt
new file mode 100644
index 0000000..b4c6298
--- /dev/null
+++ b/solr/licenses/metrics-jetty-NOTICE.txt
@@ -0,0 +1,12 @@
+Metrics
+Copyright 2010-2013 Coda Hale and Yammer, Inc.
+
+This product includes software developed by Coda Hale and Yammer, Inc.
+
+This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
+LongAdder), which was released with the following comments:
+
+    Written by Doug Lea with assistance from members of JCP JSR-166
+    Expert Group and released to the public domain, as explained at
+    http://creativecommons.org/publicdomain/zero/1.0/
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-jetty9-3.1.2.jar.sha1
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-jetty9-3.1.2.jar.sha1 b/solr/licenses/metrics-jetty9-3.1.2.jar.sha1
new file mode 100644
index 0000000..0722b0b
--- /dev/null
+++ b/solr/licenses/metrics-jetty9-3.1.2.jar.sha1
@@ -0,0 +1 @@
+7f2fe1039424ca687bea5d09ec0bfa372bf7d062

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/db1339ff/solr/licenses/metrics-json-3.1.2.jar.sha1
----------------------------------------------------------------------
diff --git a/solr/licenses/metrics-json-3.1.2.jar.sha1 b/solr/licenses/metrics-json-3.1.2.jar.sha1
new file mode 100644
index 0000000..93168e9
--- /dev/null
+++ b/solr/licenses/metrics-json-3.1.2.jar.sha1
@@ -0,0 +1 @@
+88d9e57e1ef6431109d4030c717cf5f927900fd9