[
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release Jar\n\non:\n  push:\n    tags:\n      - '*'\n\njobs:\n  release:\n    runs-on: 'ubuntu-latest'\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: 'zulu'\n          java-version: '11'\n          cache: maven\n      - name: Set version\n        id: version\n        run: echo \"version=${GITHUB_REF#refs/tags/}\" >> $GITHUB_OUTPUT\n      - run: mvn clean package\n      - name: Get release\n        id: get_release\n        uses: bruceadams/get-release@v1.3.2\n        env:\n          GITHUB_TOKEN: ${{ github.token }}\n      - name: Upload Jar\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.get_release.outputs.upload_url }}\n          asset_path: target/jmeter-prometheus-plugin-${{ steps.version.outputs.version }}.jar\n          asset_name: jmeter-prometheus-plugin-${{ steps.version.outputs.version }}.jar\n          asset_content_type: application/java-archive\n      \n\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\n\non:\n  push:\n    branches: \n      - master\n      - main\n  pull_request:\n\njobs:\n  tests:\n    strategy:\n      matrix:\n        java_version:\n          - '8'\n          - '11'\n          # TODO: Fix for java version 17\n          # - '17'\n    runs-on: 'ubuntu-latest'\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - uses: actions/setup-java@v3\n        with:\n          distribution: 'zulu'\n          java-version: '${{ matrix.java_version }}'\n          cache: maven\n      - run: mvn clean test\n\n"
  },
  {
    "path": ".gitignore",
    "content": "target/\n.project\n.settings/*\n.classpath\n./bin/\npackage_and_mv.sh\ndependency-reduced-pom.xml\njmeter.log\npom.xml.releaseBackup\nrelease.properties\n/.idea/\n/jmeter-prometheus-plugin.iml\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 0.6.2\n\n* #117 is a bugfix in PrometheusListener.java to clear the collectors after the server is closed.\n\n# 0.6.1\n\n* #116 is a bugfix in PrometheusServer.java to close the server after the delay.\n  `prometheus.delay` will now work as expected.\n\n# 0.4.0\n\n* #47 - bugfix so to correctly rename gui elements.\\\n* #51 - enable listening to assertions in counter type metrics.\n\n# 0.3.0\n\n* #50 - listening to latency\n* #49 - listening to idle time\n* #48 - listening to connection time\n\n# 0.2.0\n\n* #24 - success ratios to send zeros.\n* #46 - code is a label keyword for response code.\n* #11 - save jvm stats and allow for configuration.\n\n\n# 0.2.0-rc3\n* #42 - implement counters (success, failure and total)\n* listener can now measure response sizes of samples in histogram and summary.\n* #41 - enhancement for metric re-use.\n\n# 0.2.0-rc2\nBugfixes for previous version.\n\n* #40\n* #34\n* undocumented problem copy/paste (serializable issues) of the config\n\n\n# 0.2.0-rc1\n* first real release with only response time functionality in the listener.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n## 1. Purpose\n\nA primary goal of Jmeter Prometheus Plugin is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).\n\nThis code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.\n\nWe invite all those who participate in Jmeter Prometheus Plugin to help us create safe and positive experiences for everyone.\n\n## 2. Open Source Citizenship\n\nA supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.\n\nCommunities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.\n\nIf you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.\n\n## 3. Expected Behavior\n\nThe following behaviors are expected and requested of all community members:\n\n*   Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.\n*   Exercise consideration and respect in your speech and actions.\n*   Attempt collaboration before conflict.\n*   Refrain from demeaning, discriminatory, or harassing behavior and speech.\n*   Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.\n*   Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.\n\n## 4. Unacceptable Behavior\n\nThe following behaviors are considered harassment and are unacceptable within our community:\n\n*   Violence, threats of violence or violent language directed against another person.\n*   Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.\n*   Posting or displaying sexually explicit or violent material.\n*   Posting or threatening to post other people’s personally identifying information (\"doxing\").\n*   Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.\n*   Inappropriate photography or recording.\n*   Inappropriate physical contact. You should have someone’s consent before touching them.\n*   Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.\n*   Deliberate intimidation, stalking or following (online or in person).\n*   Advocating for, or encouraging, any of the above behavior.\n*   Sustained disruption of community events, including talks and presentations.\n\n## 5. Consequences of Unacceptable Behavior\n\nUnacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.\n\nAnyone asked to stop unacceptable behavior is expected to comply immediately.\n\nIf a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).\n\n## 6. Reporting Guidelines\n\nIf you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. johrstrom@hotmail.com.\n\n\n\nAdditionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.\n\n## 7. Addressing Grievances\n\nIf you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Johrstrom with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.\n\n\n\n## 8. Scope\n\nWe expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.\n\nThis code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.\n\n## 9. Contact info\n\njohrstrom@hotmail.com\n\n## 10. License and attribution\n\nThis Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).\n\nPortions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).\n\nRetrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n\n* If you have a trivial fix or improvement, go ahead and create a pull request, addressing me (Jeff Ohrstrom) in the description of the pull request.\n* If you plan to do something more involved, you should probably open an issue so we can talk about it. However you can also just create the pull request and the issue and we can discuss the concept in the issue and the implementation in the pull request.\n* In terms of coding styles, I'm a native english speaker who just read [Clean Code](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)(tm) so I tend to value readable code.  This library specifically tends towards basic Java standards and JMeter conventions. \n\n\n# side/related \n* This is an open source project, so commits/pull request are welcome. Forks are awesome. Issues are abundant and highly desired. \n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017 Jeff Ohrstrom\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "NOTICE",
    "content": "Prometheus instrumentation library Apache JMeter\nCopyright 2016-2019 Jeff Ohrstrom\n\nThis product includes software developed at\nAT&T Inc. (https://www.att.com)\n"
  },
  {
    "path": "README.md",
    "content": "[![Build Status](https://github.com/johrstrom/jmeter-prometheus-plugin/workflows/Tests/badge.svg)](https://github.com/johrstrom/jmeter-prometheus-plugin/actions?query=workflow%3ATests)\n[![Current Version](https://img.shields.io/maven-central/v/com.github.johrstrom/jmeter-prometheus-plugin.svg)](maven-central)\n\n# Prometheus Listener for Jmeter\n\n# Overview\nThis JMeter plugin is highly configurable listener (and config element) to allow users define they're own metrics (names, types etc.) and expose them through a Prometheus /metrics API to be scraped by a Prometheus server.\n\n# Documentation\nMore documentation can be found on [this project's wiki](https://github.com/johrstrom/jmeter-prometheus-plugin/wiki).\n\n# Listener QuickDoc\nHere's a simple example to get us started.  This example [can be found here](/docs/examples/simple_prometheus_example.jmx).  All the documentation on this README is from this jmx file.\n\n![JMeter testplan](/docs/imgs/simple_testplan.png?raw=true)\n\nIf we look closer at the very first Prometheus listener, it looks like the image below.\n\n![JMeter testplan](/docs/imgs/listener_full.png?raw=true)\n\nLet's go through all the columns one by one.\n\n- **Name**: the name of the metric.\n- **Help**: The help message of the metric.\n- **Labels**: A comma seperated list of labels you want to apply to the metric.\n  - `label` is a keyword. In JMeter it means the *name* of the sampler.\n  - `code` is a keyword. It's the response code of the result.\n  - JMeter variables can be used here. See the section [below](#Using-JMeter-variables-as-labels).\n- **Type**: The type of metric you're creating.\n  - See [Prometheus documentation](https://prometheus.io/docs/concepts/metric_types/) about metric types.\n  - [Success Ratio](#Success-Ratio) is something specific to this plugin and you can see the documentation below.\n- **Buckets of Quantiles**:\n  - Buckets are comma seperated list of numbers. Can be integers or decimals.\n  - Quantiles are comma `,` separated pair of decimals separated by a vertical bar `|`. The first decimal being the quantile and the second being the error rating. Optionally, after a `;` separator the lenght of the window used to calculate the quantile can be specified. Sample: `0.8,0.01|0.9,0.01|0.95,0.005|0.99,0.001;60`\n- **Listen To**: Dropdown to listen to samples or assertions. This only applies to Counters and SuccessRatio type metrics.\n- **Measuring**: Dropdown menu of all the things you can measure\n  - See the [Type and Measuring compatibility matrix](#Type-and-Measuring-compatibility-matrix) section below.\n\n### Using JMeter variables as labels\n\nNotice in the image above `jsr223_rt_as_summary` (the 2nd down) has `category,label` in it's labels column.\n\nThis plugin allows you to use variables in the test plan as label values for a given metric.  You can see here in the above image, I simply generated a random string and assigned it to the `category` jmeter variable, and this plugin exposed it as a label.\n\nAs you can see below, it produced that metric with the `category=\"[A,B,C]\"`. Again, this example [can be found here](/docs/examples/simple_prometheus_example.jmx).\n\n![JMeter testplan](/docs/imgs/category_variable.png?raw=true)\n\n\n### Listener output\n\n![JMeter testplan](/docs/imgs/rt_as_sum.png?raw=true)\n\n### Success Ratio\n\nSuccess ratio is a concept specific to this plugin library.  Often we want measure success rates of samplers and it's difficult to do so when the failure for a given metric or labelset has never occurred.  It's difficult because it involves computations with NaNs.\n\nSo, we want to measure success ratios and be sure to emit zeros for both failures and success when the other is created for the first time.  That way you can always safely run computations like rate() and so on.\n\n[Here's prometheus](https://www.robustperception.io/existential-issues-with-metrics) documentation on why it's important.  [Here](https://github.com/johrstrom/jmeter-prometheus-plugin/issues/24) is this repositories issue for this feature.\n\nHere you can see from the example `jsr223_can_fail` turned into 3 metrics.  The names have appeneded `_success`, `_failure` and `_total`.  They're all counter type metrics that increment on success failure and total, respectively.  \n\nFrom this example you can see that `4 success + 2 failures = 6 total`.  Again, this metric guarantees a zero, for success or failure, for a metric that has a total of one or more.\n\n![JMeter testplan](/docs/imgs/success_ratio_output.png?raw=true)\n\n### Type and Measuring compatibility matrix\n\nDoes it make sense to have a Counter measuring Response Time? No. Does it make sense to have a Histogram of total successes? No.  \n\nThis is a matrix of what metric types can measure what metrics.  If you configure, say a histogram to measure count total, the plugin will likely do nothing to update that metric.\n\n**Bold** types can listen to samples or assertions (not both at the same time).  Note that if you don't use `label` when listening to assertions you may get strange results.  This is because *one* sample can generate many *assertion results* which are then counted. When there's no label to distinguish those counts, they'll be summed together which may or may not be expected.\n\n| | Histogram | Summary | Counter | Guage | Success Ratio |\n|:-----:|:------:|:------:|:------:|:------:|:------:|\n| Response time  | x | x |   |   |   |\n| Response size  | x | x |   |   |   |\n| Latency        | x | x |   |   |   |\n| Idle time      | x | x |   |   |   |\n| Connect time   | x | x |   |   |   |\n| Count total    |   |   | **x** |   |   |\n| Failure total  |   |   | **x** |   |   |\n| Success total  |   |   | **x** |   |   |\n| Success Ratio  |   |   |   |   | **x** |\n\n#### What about gauges\nI'm not quite sure how Guages make sense in the plugin.  If you have a use case, I'd love to hear it. I wrote them in without actually having one, so you can technically create one, I just don't know how the listener may update it.\n\n# Config QuickDoc\n\nThis library provides not only a listener, but a configuration element as well.  This is useful when users have to make some computation for a specific use case and then want to expose that metric in Prometheus.\n\nThese use cases for this are mostly functional, where say you're validating something about a response that's specific the thing under test.  \n\nIt works like this.  Define a metric with this configuration element, and at test run-time this library will place that object in the jmeter variables from which you can access it.\n\nLet's consider this use case.  You, have an API that tells you the current number animals categorized by `color` `size`and `mammal` - perhaps running down your street.  You may create a counter like this as you watch them go by.\n![JMeter testplan](/docs/imgs/prometheus_cfg.png?raw=true)\n\n\nSo I then access the object I've created above like so.  This is an absolutely trivial case of randomly generating the variables but the example is only trying to convey how one may interact with the objects created by the configuration element.  However you extract the data, you can access and interact with the Prometheus metric you've created.  \n\nSee the [Prometheus Javadocs](https://prometheus.github.io/client_java/) for more information on their API.\n\n![JMeter testplan](/docs/imgs/jsr223_use_prometheus_cfg.png?raw=true)\n\nWhich will expose a counter with all the appropriate labels.\n\n![JMeter testplan](/docs/imgs/prom_cfg_output.png?raw=true)\n\n# Usage Tips\n\n### Skipping samplers or other elements\n\nYou can re-use metrics in multiple listeners so long as they're defined in the **exact** same way. There will be undefined behavior if two or more listeners have the same metric (the same metric name) with different configurations.\n\n\nHere you see `first_random_sampler` and `second_random_sampler` in the labels of this metric, but you do not see `want_to_skip`, the thing that we're trying to skip.  Note the composition of the test plan as shown [at the top of this page](#Listener-QuickDoc)\n\n![JMeter testplan](/docs/imgs/rt_as_hist.png?raw=true)\n\n### Visualization\n\nThis plugin has limited \"out of the box\" functionality because it gives you, the user, total control over what the metric names may be.  That said, here's a sample dashboard given [here in examples](/docs/examples/grafana_jsr223_test.json) such that if you have a local prometheus/grafana stack you can a dashboard that looks something like this.\n\n![JMeter testplan](/docs/imgs/grafana_jsr223_test.png?raw=true)\n\n\n# Properties you can override\nTo overrider properties, add the Properties in the jmeter.properties file (JMETER_HOME/bin folder) and restart Jmeter to take effect\n\n|Property | default | description|\n|:----------:|:-----------:|:-------------------------------:|\n|prometheus.port|9270|The port the http server will bind to |\n|prometheus.ip|127.0.0.1|The ip the http server will bind to. Containers may need `0.0.0.0`|\n|prometheus.delay|0|The delay (in seconds) the http server will wait before being destroyed|\n|prometheus.save.threads|true|True or false value to save and collect jmeter thread metrics|\n|prometheus.save.threads.name|jmeter\\_threads|The name of the metric describing jmeter threads|\n|prometheus.save.jvm|true|Collect metrics from the JVM|\n\n# Download\n\n## Maven Dependency\n\nWe're now hosted on maven central! If you want to download this jar in a maven style project, simply add this dependency:\n\n```xml\n    <!-- you'll have to specify jmeter-prometheus-plugin.version here -->\n    <dependency>\n      <groupId>com.github.johrstrom</groupId>\n      <artifactId>jmeter-prometheus-plugin</artifactId>\n      <version>${jmeter-prometheus-plugin.version}</version>\n    </dependency>\n```\n\n## Programatically\n\nThis URL below seems to be the only way to download jars from maven through `curl` or `wget`.  Again, replace `0.6.0` here with the\ncurrent version, which can be viewed at the top of this README.\n\n`https://search.maven.org/remotecontent?filepath=com/github/johrstrom/jmeter-prometheus-plugin/0.6.0/jmeter-prometheus-plugin-0.6.0.jar`\n\n## Web Browser\n\nSearch [maven central](https://search.maven.org/search?q=a:jmeter-prometheus-plugin) to get the latest version.\n\nThis project is hosted [here](https://oss.sonatype.org/content/groups/public/com/github/johrstrom/jmeter-prometheus-plugin/) on \n[OSS sonatype org](https://oss.sonatype.org).\n\n## Verifying\n\nI sign these release jars so you can verify with this method (of course the version is going to change):\n\n```bash\ngpg --verify jmeter-prometheus-plugin-0.5.0.jar.asc\n```\n\nYou should see output similar to this.\n\n```bash\ngpg: assuming signed data in 'jmeter-prometheus-plugin-0.5.0.jar'\ngpg: Signature made Thu 22 Aug 2019 09:35:15 PM EDT\ngpg:                using RSA key 6F5EAC674B279301932EC1FEAC2AEC6C76D4AF12\ngpg: Good signature from \"Jeff Ohrstrom (Jeff Ohrstrom's personal key) <johrstrom@hotmail.com>\" [ultimate]\n```\n\n# Building\n\nTo build, simply maven package:\n```\nmvn clean package\n```\nThis creates 2 jars, a shaded jar that has all the dependencies within it (this is the one you want) and the original jar. Both are in the target directory.  Simply move the jar to your $JMETER\\_HOME/lib/ext directory as with any JMeter plugin and you're ready to go!\n\n## Feedback\n\nFeel free to open issues against this project, even to ask questions.\n"
  },
  {
    "path": "docs/examples/grafana.json",
    "content": "{\n  \"annotations\": {\n    \"list\": [\n      {\n        \"builtIn\": 1,\n        \"datasource\": \"-- Grafana --\",\n        \"enable\": true,\n        \"hide\": true,\n        \"iconColor\": \"rgba(0, 211, 255, 1)\",\n        \"name\": \"Annotations & Alerts\",\n        \"type\": \"dashboard\"\n      }\n    ]\n  },\n  \"description\": \"A grafana dashboard to inspect jmeter metrics via prometheus exporter\",\n  \"editable\": true,\n  \"gnetId\": 2492,\n  \"graphTooltip\": 1,\n  \"id\": 2,\n  \"iteration\": 1582126578426,\n  \"links\": [],\n  \"panels\": [\n    {\n      \"cacheTimeout\": null,\n      \"colorBackground\": true,\n      \"colorValue\": false,\n      \"colors\": [\n        \"#3274D9\",\n        \"#3274D9\",\n        \"#3274D9\"\n      ],\n      \"datasource\": \"Prometheus\",\n      \"format\": \"none\",\n      \"gauge\": {\n        \"maxValue\": 100,\n        \"minValue\": 0,\n        \"show\": false,\n        \"thresholdLabels\": false,\n        \"thresholdMarkers\": true\n      },\n      \"gridPos\": {\n        \"h\": 2,\n        \"w\": 6,\n        \"x\": 0,\n        \"y\": 0\n      },\n      \"id\": 12,\n      \"interval\": null,\n      \"links\": [],\n      \"mappingType\": 1,\n      \"mappingTypes\": [\n        {\n          \"name\": \"value to text\",\n          \"value\": 1\n        },\n        {\n          \"name\": \"range to text\",\n          \"value\": 2\n        }\n      ],\n      \"maxDataPoints\": 100,\n      \"nullPointMode\": \"connected\",\n      \"nullText\": null,\n      \"options\": {},\n      \"postfix\": \" vus\",\n      \"postfixFontSize\": \"100%\",\n      \"prefix\": \"\",\n      \"prefixFontSize\": \"50%\",\n      \"rangeMaps\": [\n        {\n          \"from\": \"null\",\n          \"text\": \"N/A\",\n          \"to\": \"null\"\n        }\n      ],\n      \"sparkline\": {\n        \"fillColor\": \"#447ebc\",\n        \"full\": true,\n        \"lineColor\": \"#447ebc\",\n        \"show\": false\n      },\n      \"tableColumn\": \"\",\n      \"targets\": [\n        {\n          \"expr\": \"sum(jmeter_threads{state=\\\"active\\\"})\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"instant\": true,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"\",\n          \"refId\": \"A\",\n          \"step\": 4\n        }\n      ],\n      \"thresholds\": \"\",\n      \"title\": \"\",\n      \"type\": \"singlestat\",\n      \"valueFontSize\": \"100%\",\n      \"valueMaps\": [\n        {\n          \"op\": \"=\",\n          \"text\": \"N/A\",\n          \"value\": \"null\"\n        }\n      ],\n      \"valueName\": \"current\"\n    },\n    {\n      \"cacheTimeout\": null,\n      \"colorBackground\": true,\n      \"colorValue\": false,\n      \"colors\": [\n        \"#A352CC\",\n        \"#A352CC\",\n        \"#A352CC\"\n      ],\n      \"datasource\": \"Prometheus\",\n      \"decimals\": 1,\n      \"format\": \"ops\",\n      \"gauge\": {\n        \"maxValue\": 100,\n        \"minValue\": 0,\n        \"show\": false,\n        \"thresholdLabels\": false,\n        \"thresholdMarkers\": true\n      },\n      \"gridPos\": {\n        \"h\": 2,\n        \"w\": 6,\n        \"x\": 6,\n        \"y\": 0\n      },\n      \"id\": 7,\n      \"interval\": null,\n      \"links\": [],\n      \"mappingType\": 1,\n      \"mappingTypes\": [\n        {\n          \"name\": \"value to text\",\n          \"value\": 1\n        },\n        {\n          \"name\": \"range to text\",\n          \"value\": 2\n        }\n      ],\n      \"maxDataPoints\": 100,\n      \"nullPointMode\": \"connected\",\n      \"nullText\": null,\n      \"options\": {},\n      \"postfix\": \"\",\n      \"postfixFontSize\": \"50%\",\n      \"prefix\": \"\",\n      \"prefixFontSize\": \"50%\",\n      \"rangeMaps\": [\n        {\n          \"from\": \"null\",\n          \"text\": \"N/A\",\n          \"to\": \"null\"\n        }\n      ],\n      \"sparkline\": {\n        \"fillColor\": \"#508642\",\n        \"full\": true,\n        \"lineColor\": \"#508642\",\n        \"show\": false\n      },\n      \"tableColumn\": \"\",\n      \"targets\": [\n        {\n          \"expr\": \"sum(irate(Ratio_success[$interval]))\",\n          \"format\": \"time_series\",\n          \"instant\": true,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"\",\n          \"refId\": \"E\"\n        }\n      ],\n      \"thresholds\": \"\",\n      \"title\": \"\",\n      \"type\": \"singlestat\",\n      \"valueFontSize\": \"100%\",\n      \"valueMaps\": [\n        {\n          \"op\": \"=\",\n          \"text\": \"N/A\",\n          \"value\": \"null\"\n        }\n      ],\n      \"valueName\": \"current\"\n    },\n    {\n      \"cacheTimeout\": null,\n      \"colorBackground\": true,\n      \"colorValue\": false,\n      \"colors\": [\n        \"#E0B400\",\n        \"#E0B400\",\n        \"#E0B400\"\n      ],\n      \"datasource\": \"Prometheus\",\n      \"decimals\": 1,\n      \"format\": \"ms\",\n      \"gauge\": {\n        \"maxValue\": 100,\n        \"minValue\": 0,\n        \"show\": false,\n        \"thresholdLabels\": false,\n        \"thresholdMarkers\": true\n      },\n      \"gridPos\": {\n        \"h\": 2,\n        \"w\": 6,\n        \"x\": 12,\n        \"y\": 0\n      },\n      \"id\": 14,\n      \"interval\": null,\n      \"links\": [],\n      \"mappingType\": 1,\n      \"mappingTypes\": [\n        {\n          \"name\": \"value to text\",\n          \"value\": 1\n        },\n        {\n          \"name\": \"range to text\",\n          \"value\": 2\n        }\n      ],\n      \"maxDataPoints\": 100,\n      \"nullPointMode\": \"connected\",\n      \"nullText\": null,\n      \"options\": {},\n      \"postfix\": \"\",\n      \"postfixFontSize\": \"50%\",\n      \"prefix\": \"Avg. \",\n      \"prefixFontSize\": \"100%\",\n      \"rangeMaps\": [\n        {\n          \"from\": \"null\",\n          \"text\": \"N/A\",\n          \"to\": \"null\"\n        }\n      ],\n      \"sparkline\": {\n        \"fillColor\": \"#e5ac0e\",\n        \"full\": true,\n        \"lineColor\": \"#e5ac0e\",\n        \"show\": false\n      },\n      \"tableColumn\": \"\",\n      \"targets\": [\n        {\n          \"expr\": \"avg((rate(ResponseTime_sum{code=\\\"200\\\"}[$interval])/(rate(ResponseTime_count{code=\\\"200\\\"}[$interval])>0)))\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"instant\": true,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Response time\",\n          \"metric\": \"\",\n          \"refId\": \"A\",\n          \"step\": 4\n        }\n      ],\n      \"thresholds\": \"\",\n      \"title\": \"\",\n      \"type\": \"singlestat\",\n      \"valueFontSize\": \"100%\",\n      \"valueMaps\": [\n        {\n          \"op\": \"=\",\n          \"text\": \"N/A\",\n          \"value\": \"null\"\n        }\n      ],\n      \"valueName\": \"current\"\n    },\n    {\n      \"cacheTimeout\": null,\n      \"colorBackground\": true,\n      \"colorValue\": false,\n      \"colors\": [\n        \"#37872D\",\n        \"#FF9830\",\n        \"#E02F44\"\n      ],\n      \"datasource\": \"Prometheus\",\n      \"decimals\": 1,\n      \"format\": \"percentunit\",\n      \"gauge\": {\n        \"maxValue\": 100,\n        \"minValue\": 0,\n        \"show\": false,\n        \"thresholdLabels\": false,\n        \"thresholdMarkers\": true\n      },\n      \"gridPos\": {\n        \"h\": 2,\n        \"w\": 6,\n        \"x\": 18,\n        \"y\": 0\n      },\n      \"id\": 8,\n      \"interval\": null,\n      \"links\": [],\n      \"mappingType\": 1,\n      \"mappingTypes\": [\n        {\n          \"name\": \"value to text\",\n          \"value\": 1\n        },\n        {\n          \"name\": \"range to text\",\n          \"value\": 2\n        }\n      ],\n      \"maxDataPoints\": 100,\n      \"nullPointMode\": \"connected\",\n      \"nullText\": null,\n      \"options\": {},\n      \"postfix\": \" error rate\",\n      \"postfixFontSize\": \"100%\",\n      \"prefix\": \"\",\n      \"prefixFontSize\": \"50%\",\n      \"rangeMaps\": [\n        {\n          \"from\": \"null\",\n          \"text\": \"N/A\",\n          \"to\": \"null\"\n        }\n      ],\n      \"sparkline\": {\n        \"fillColor\": \"#bf1b00\",\n        \"full\": true,\n        \"lineColor\": \"#bf1b00\",\n        \"show\": false\n      },\n      \"tableColumn\": \"\",\n      \"targets\": [\n        {\n          \"expr\": \"(avg(rate(Ratio_failure[$interval]))/avg(rate(Ratio_total[$interval])))\",\n          \"format\": \"time_series\",\n          \"instant\": true,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"\",\n          \"refId\": \"A\",\n          \"step\": 4\n        }\n      ],\n      \"thresholds\": \"0.1,10\",\n      \"title\": \"\",\n      \"type\": \"singlestat\",\n      \"valueFontSize\": \"100%\",\n      \"valueMaps\": [\n        {\n          \"op\": \"=\",\n          \"text\": \"N/A\",\n          \"value\": \"null\"\n        }\n      ],\n      \"valueName\": \"current\"\n    },\n    {\n      \"collapsed\": false,\n      \"datasource\": null,\n      \"gridPos\": {\n        \"h\": 1,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 2\n      },\n      \"id\": 17,\n      \"panels\": [],\n      \"repeat\": null,\n      \"title\": \"Summary\",\n      \"type\": \"row\"\n    },\n    {\n      \"aliasColors\": {\n        \"Failure \": \"#bf1b00\",\n        \"OK\": \"dark-green\"\n      },\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"description\": \"\",\n      \"fill\": 0,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 12,\n        \"x\": 0,\n        \"y\": 3\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 36,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"hideEmpty\": true,\n        \"hideZero\": true,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 3,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [\n        {\n          \"alias\": \"Virtual Users\",\n          \"color\": \"#3274D9\",\n          \"fill\": 4,\n          \"lines\": true,\n          \"linewidth\": 3,\n          \"yaxis\": 2\n        },\n        {\n          \"alias\": \"Request OK per second\",\n          \"color\": \"#19730E\"\n        },\n        {\n          \"alias\": \"Request KO per second\",\n          \"color\": \"#AD0317\"\n        }\n      ],\n      \"spaceLength\": 10,\n      \"stack\": false,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"sum(jmeter_threads{state=\\\"active\\\"})\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Virtual Users\",\n          \"refId\": \"B\",\n          \"step\": 1\n        },\n        {\n          \"expr\": \"sum(rate(Ratio_success[$interval]))\",\n          \"format\": \"time_series\",\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Request OK per second\",\n          \"refId\": \"A\"\n        },\n        {\n          \"expr\": \"sum(rate(Ratio_failure[$interval]))   \",\n          \"legendFormat\": \"Request KO per second\",\n          \"refId\": \"C\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Virtual Users vs OK/KO\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"none\",\n          \"label\": \"ops\",\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"decimals\": null,\n          \"format\": \"short\",\n          \"label\": \"VU\",\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {\n        \"KO\": \"#890F02\",\n        \"Requests KO\": \"#bf1b00\",\n        \"Requests OK\": \"#508642\",\n        \"Requests per second\": \"#447ebc\",\n        \"failure\": \"#890F02\",\n        \"success\": \"#7EB26D\"\n      },\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 10,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 12,\n        \"x\": 12,\n        \"y\": 3\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 3,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"max\": true,\n        \"min\": true,\n        \"show\": true,\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [\n        {\n          \"alias\": \"Total Requests per second\",\n          \"color\": \"#A352CC\",\n          \"fill\": 0,\n          \"linewidth\": 3,\n          \"steppedLine\": false\n        },\n        {\n          \"alias\": \"OK\",\n          \"yaxis\": 2\n        },\n        {\n          \"alias\": \"KO\",\n          \"yaxis\": 2\n        }\n      ],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": true,\n      \"targets\": [\n        {\n          \"expr\": \"(sum(rate(Ratio_success[$interval]))/sum(rate(Ratio_total[$interval])))\",\n          \"format\": \"time_series\",\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"OK\",\n          \"refId\": \"A\",\n          \"step\": 2\n        },\n        {\n          \"expr\": \"(sum(rate(Ratio_failure[$interval]))/sum(rate(Ratio_total[$interval])))\",\n          \"format\": \"time_series\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"KO\",\n          \"refId\": \"B\"\n        },\n        {\n          \"expr\": \"sum(rate(Ratio_total[$interval]))\",\n          \"format\": \"time_series\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Total Requests per second\",\n          \"refId\": \"C\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Total Requests vs OK/KO\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"none\",\n          \"label\": \"ops\",\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"decimals\": 0,\n          \"format\": \"percentunit\",\n          \"label\": \"\",\n          \"logBase\": 1,\n          \"max\": \"1\",\n          \"min\": \"0\",\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {\n        \"Failure \": \"#bf1b00\",\n        \"Virtual Users\": \"semi-dark-blue\"\n      },\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"description\": \"\",\n      \"fill\": 0,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 12,\n        \"x\": 0,\n        \"y\": 13\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 30,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"hideEmpty\": true,\n        \"hideZero\": true,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 3,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [\n        {\n          \"alias\": \"Avg Response Time\",\n          \"color\": \"#F2CC0C\"\n        },\n        {\n          \"alias\": \"Virtual Users\",\n          \"color\": \"#3274D9\",\n          \"fill\": 4,\n          \"yaxis\": 2\n        }\n      ],\n      \"spaceLength\": 10,\n      \"stack\": false,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"avg((rate(ResponseTime_sum{code=\\\"200\\\"}[$interval])/(rate(ResponseTime_count{code=\\\"200\\\"}[$interval])>0)))\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Avg Response Time\",\n          \"refId\": \"B\",\n          \"step\": 1\n        },\n        {\n          \"expr\": \"sum(jmeter_threads{state=\\\"active\\\"})\",\n          \"format\": \"time_series\",\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Virtual Users\",\n          \"refId\": \"A\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Avg. Response Time vs Virtual Users\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"ms\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": \"0\",\n          \"show\": true\n        },\n        {\n          \"decimals\": null,\n          \"format\": \"short\",\n          \"label\": \"VU\",\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {\n        \"Failure \": \"#bf1b00\"\n      },\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"description\": \"\",\n      \"fill\": 0,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 12,\n        \"x\": 12,\n        \"y\": 13\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 29,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"hideEmpty\": true,\n        \"hideZero\": true,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 3,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [\n        {\n          \"alias\": \"OK\",\n          \"color\": \"#19730E\",\n          \"yaxis\": 2\n        },\n        {\n          \"alias\": \"Avg Response Time\",\n          \"color\": \"#F2CC0C\"\n        },\n        {\n          \"alias\": \"KO\",\n          \"color\": \"#AD0317\",\n          \"yaxis\": 2\n        }\n      ],\n      \"spaceLength\": 10,\n      \"stack\": false,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"avg((rate(ResponseTime_sum{code=\\\"200\\\"}[$interval])/(rate(ResponseTime_count{code=\\\"200\\\"}[$interval])>0)))\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"instant\": false,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"Avg Response Time\",\n          \"refId\": \"B\",\n          \"step\": 1\n        },\n        {\n          \"expr\": \"sum(rate(Ratio_success[$interval]))\",\n          \"format\": \"time_series\",\n          \"hide\": true,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"OK\",\n          \"refId\": \"A\"\n        },\n        {\n          \"expr\": \"sum(rate(Ratio_failure[$interval]))   \",\n          \"format\": \"time_series\",\n          \"hide\": true,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"KO\",\n          \"refId\": \"C\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Avg. Response Time vs OK/KO\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"ms\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": \"0\",\n          \"show\": true\n        },\n        {\n          \"decimals\": null,\n          \"format\": \"short\",\n          \"label\": \"ops\",\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"columns\": [],\n      \"datasource\": \"Prometheus\",\n      \"fontSize\": \"100%\",\n      \"gridPos\": {\n        \"h\": 8,\n        \"w\": 12,\n        \"x\": 0,\n        \"y\": 23\n      },\n      \"id\": 33,\n      \"links\": [],\n      \"options\": {},\n      \"pageSize\": 20,\n      \"scroll\": true,\n      \"showHeader\": true,\n      \"sort\": {\n        \"col\": 1,\n        \"desc\": false\n      },\n      \"styles\": [\n        {\n          \"alias\": \"Time\",\n          \"align\": \"auto\",\n          \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n          \"link\": false,\n          \"pattern\": \"Time\",\n          \"type\": \"hidden\"\n        },\n        {\n          \"alias\": \"Label\",\n          \"align\": \"auto\",\n          \"colorMode\": null,\n          \"colors\": [\n            \"rgba(245, 54, 54, 0.9)\",\n            \"rgba(237, 129, 40, 0.89)\",\n            \"rgba(50, 172, 45, 0.97)\"\n          ],\n          \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n          \"decimals\": 2,\n          \"mappingType\": 1,\n          \"pattern\": \"label\",\n          \"thresholds\": [],\n          \"type\": \"number\",\n          \"unit\": \"short\"\n        },\n        {\n          \"alias\": \"Count\",\n          \"align\": \"auto\",\n          \"colorMode\": null,\n          \"colors\": [\n            \"rgba(245, 54, 54, 0.9)\",\n            \"rgba(237, 129, 40, 0.89)\",\n            \"rgba(50, 172, 45, 0.97)\"\n          ],\n          \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n          \"decimals\": 0,\n          \"mappingType\": 1,\n          \"pattern\": \"Value\",\n          \"thresholds\": [],\n          \"type\": \"number\",\n          \"unit\": \"locale\"\n        },\n        {\n          \"alias\": \"\",\n          \"align\": \"auto\",\n          \"colorMode\": null,\n          \"colors\": [\n            \"rgba(245, 54, 54, 0.9)\",\n            \"rgba(237, 129, 40, 0.89)\",\n            \"rgba(50, 172, 45, 0.97)\"\n          ],\n          \"decimals\": 2,\n          \"pattern\": \"/.*/\",\n          \"thresholds\": [],\n          \"type\": \"number\",\n          \"unit\": \"short\"\n        }\n      ],\n      \"targets\": [\n        {\n          \"expr\": \"sum(Ratio_success) by (label) \",\n          \"format\": \"table\",\n          \"instant\": true,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"\",\n          \"refId\": \"A\"\n        }\n      ],\n      \"title\": \"OK\",\n      \"transform\": \"table\",\n      \"type\": \"table\"\n    },\n    {\n      \"columns\": [],\n      \"datasource\": \"Prometheus\",\n      \"fontSize\": \"100%\",\n      \"gridPos\": {\n        \"h\": 8,\n        \"w\": 12,\n        \"x\": 12,\n        \"y\": 23\n      },\n      \"id\": 34,\n      \"links\": [],\n      \"options\": {},\n      \"pageSize\": 25,\n      \"scroll\": true,\n      \"showHeader\": true,\n      \"sort\": {\n        \"col\": 1,\n        \"desc\": false\n      },\n      \"styles\": [\n        {\n          \"alias\": \"Time\",\n          \"align\": \"auto\",\n          \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n          \"link\": false,\n          \"pattern\": \"Time\",\n          \"type\": \"hidden\"\n        },\n        {\n          \"alias\": \"Label\",\n          \"align\": \"auto\",\n          \"colorMode\": null,\n          \"colors\": [\n            \"rgba(245, 54, 54, 0.9)\",\n            \"rgba(237, 129, 40, 0.89)\",\n            \"rgba(50, 172, 45, 0.97)\"\n          ],\n          \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n          \"decimals\": 2,\n          \"mappingType\": 1,\n          \"pattern\": \"label\",\n          \"thresholds\": [],\n          \"type\": \"number\",\n          \"unit\": \"short\"\n        },\n        {\n          \"alias\": \"Count\",\n          \"align\": \"auto\",\n          \"colorMode\": null,\n          \"colors\": [\n            \"rgba(245, 54, 54, 0.9)\",\n            \"rgba(237, 129, 40, 0.89)\",\n            \"rgba(50, 172, 45, 0)\"\n          ],\n          \"dateFormat\": \"YYYY-MM-DD HH:mm:ss\",\n          \"decimals\": 0,\n          \"mappingType\": 1,\n          \"pattern\": \"Value\",\n          \"thresholds\": [\n            \"\"\n          ],\n          \"type\": \"number\",\n          \"unit\": \"none\"\n        },\n        {\n          \"alias\": \"\",\n          \"align\": \"auto\",\n          \"colorMode\": null,\n          \"colors\": [\n            \"rgba(245, 54, 54, 0.9)\",\n            \"rgba(237, 129, 40, 0.89)\",\n            \"rgba(50, 172, 45, 0.97)\"\n          ],\n          \"decimals\": 2,\n          \"pattern\": \"/.*/\",\n          \"thresholds\": [],\n          \"type\": \"number\",\n          \"unit\": \"short\"\n        }\n      ],\n      \"targets\": [\n        {\n          \"expr\": \"sum(Ratio_failure) by (label) \",\n          \"format\": \"table\",\n          \"instant\": true,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"\",\n          \"refId\": \"A\"\n        }\n      ],\n      \"title\": \"KO\",\n      \"transform\": \"table\",\n      \"type\": \"table\"\n    },\n    {\n      \"collapsed\": false,\n      \"datasource\": null,\n      \"gridPos\": {\n        \"h\": 1,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 31\n      },\n      \"id\": 18,\n      \"panels\": [],\n      \"repeat\": null,\n      \"title\": \"Response times\",\n      \"type\": \"row\"\n    },\n    {\n      \"aliasColors\": {\n        \"avg jmeter\": \"light-green\"\n      },\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"description\": \"\",\n      \"fill\": 0,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 11,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 32\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 35,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": true,\n        \"hideEmpty\": true,\n        \"hideZero\": true,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"sort\": \"avg\",\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 3,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": false,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \" avg (rate(ResponseTime_sum{code=~\\\"1.*|2.*|3.*|4.*|5.*\\\"}[$interval]) / (rate(ResponseTime_count{code=~\\\"1.*|2.*|3.*|4.*|5.*\\\"}[$interval])>0)) by (label)\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"instant\": false,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{label}} \",\n          \"refId\": \"A\",\n          \"step\": 1\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Avg. response time\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"ms\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {},\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"description\": \"\",\n      \"fill\": 1,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 43\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 13,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": true,\n        \"hideEmpty\": true,\n        \"hideZero\": true,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"sort\": \"avg\",\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 3,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": false,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"avg(ResponseTime{code=\\\"200\\\"}) by (quantile)\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{quantile}}\",\n          \"refId\": \"B\",\n          \"step\": 1\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Pct response times\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"ms\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"decimals\": null,\n          \"format\": \"short\",\n          \"label\": \"\",\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": false\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"collapsed\": false,\n      \"datasource\": null,\n      \"gridPos\": {\n        \"h\": 1,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 53\n      },\n      \"id\": 19,\n      \"panels\": [],\n      \"repeat\": null,\n      \"title\": \"Requests per second\",\n      \"type\": \"row\"\n    },\n    {\n      \"aliasColors\": {\n        \"KO\": \"semi-dark-red\",\n        \"OK\": \"semi-dark-green\",\n        \"failure\": \"#890F02\"\n      },\n      \"bars\": true,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 0,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 54\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 4,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"hideEmpty\": false,\n        \"hideZero\": false,\n        \"max\": true,\n        \"min\": true,\n        \"show\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": false,\n      \"linewidth\": 3,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"repeat\": null,\n      \"repeatDirection\": \"h\",\n      \"seriesOverrides\": [\n        {\n          \"alias\": \"KO\",\n          \"color\": \"#E02F44\"\n        },\n        {\n          \"alias\": \"OK\",\n          \"color\": \"#56A64B\"\n        }\n      ],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"sum(rate(Ratio_success[$interval]))\",\n          \"format\": \"time_series\",\n          \"interval\": \"\",\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"OK\",\n          \"metric\": \"\",\n          \"refId\": \"A\",\n          \"step\": 2\n        },\n        {\n          \"expr\": \"sum(rate(Ratio_failure[$interval]))\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"instant\": false,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"KO\",\n          \"refId\": \"B\",\n          \"step\": 2\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"Total\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {\n        \"requests\": \"#64B0C8\"\n      },\n      \"bars\": true,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 5,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 64\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 5,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"sort\": \"max\",\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": false,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"sum(rate(Ratio_total[$interval])) by (label)\",\n          \"format\": \"time_series\",\n          \"interval\": \"\",\n          \"intervalFactor\": 2,\n          \"legendFormat\": \"{{label}}\",\n          \"refId\": \"A\",\n          \"step\": 2\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"by label\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {\n        \"HTTP 404\": \"#BF1B00\"\n      },\n      \"bars\": true,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 5,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 10,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 74\n      },\n      \"height\": \"400\",\n      \"hiddenSeries\": false,\n      \"id\": 6,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": false,\n        \"max\": true,\n        \"min\": true,\n        \"rightSide\": false,\n        \"show\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": false,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 2,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"sum by (code) (rate(Ratio_total[$interval]))\",\n          \"format\": \"time_series\",\n          \"interval\": \"\",\n          \"intervalFactor\": 2,\n          \"legendFormat\": \"HTTP {{code}}\",\n          \"refId\": \"A\",\n          \"step\": 2\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"by HTTP code\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"collapsed\": true,\n      \"datasource\": null,\n      \"gridPos\": {\n        \"h\": 1,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 84\n      },\n      \"id\": 20,\n      \"panels\": [\n        {\n          \"aliasColors\": {\n            \"moviri.com/404 - HTTP 404\": \"#58140C\"\n          },\n          \"bars\": true,\n          \"dashLength\": 10,\n          \"dashes\": false,\n          \"datasource\": \"Prometheus\",\n          \"fill\": 5,\n          \"fillGradient\": 0,\n          \"gridPos\": {\n            \"h\": 10,\n            \"w\": 24,\n            \"x\": 0,\n            \"y\": 86\n          },\n          \"height\": \"400\",\n          \"hiddenSeries\": false,\n          \"id\": 15,\n          \"legend\": {\n            \"alignAsTable\": true,\n            \"avg\": true,\n            \"current\": false,\n            \"max\": true,\n            \"min\": true,\n            \"show\": true,\n            \"total\": false,\n            \"values\": true\n          },\n          \"lines\": false,\n          \"linewidth\": 1,\n          \"links\": [],\n          \"nullPointMode\": \"null\",\n          \"options\": {\n            \"dataLinks\": []\n          },\n          \"percentage\": false,\n          \"pointradius\": 5,\n          \"points\": false,\n          \"renderer\": \"flot\",\n          \"seriesOverrides\": [],\n          \"spaceLength\": 10,\n          \"stack\": true,\n          \"steppedLine\": false,\n          \"targets\": [\n            {\n              \"expr\": \"sum by(code) (rate(Ratio_failure[$interval]))\",\n              \"format\": \"time_series\",\n              \"hide\": false,\n              \"interval\": \"\",\n              \"intervalFactor\": 1,\n              \"legendFormat\": \"HTTP {{code}}\",\n              \"refId\": \"A\",\n              \"step\": 2\n            }\n          ],\n          \"thresholds\": [],\n          \"timeFrom\": null,\n          \"timeRegions\": [],\n          \"timeShift\": null,\n          \"title\": \"by HTTP code\",\n          \"tooltip\": {\n            \"shared\": true,\n            \"sort\": 0,\n            \"value_type\": \"individual\"\n          },\n          \"type\": \"graph\",\n          \"xaxis\": {\n            \"buckets\": null,\n            \"mode\": \"time\",\n            \"name\": null,\n            \"show\": true,\n            \"values\": []\n          },\n          \"yaxes\": [\n            {\n              \"format\": \"short\",\n              \"label\": null,\n              \"logBase\": 1,\n              \"max\": null,\n              \"min\": null,\n              \"show\": true\n            },\n            {\n              \"format\": \"short\",\n              \"label\": null,\n              \"logBase\": 1,\n              \"max\": null,\n              \"min\": null,\n              \"show\": true\n            }\n          ],\n          \"yaxis\": {\n            \"align\": false,\n            \"alignLevel\": null\n          }\n        },\n        {\n          \"aliasColors\": {\n            \"moviri.com/servicesKO - servicesKO\": \"#58140C\"\n          },\n          \"bars\": true,\n          \"dashLength\": 10,\n          \"dashes\": false,\n          \"datasource\": \"Prometheus\",\n          \"fill\": 5,\n          \"fillGradient\": 0,\n          \"gridPos\": {\n            \"h\": 10,\n            \"w\": 24,\n            \"x\": 0,\n            \"y\": 96\n          },\n          \"height\": \"400\",\n          \"hiddenSeries\": false,\n          \"id\": 16,\n          \"legend\": {\n            \"alignAsTable\": true,\n            \"avg\": true,\n            \"current\": false,\n            \"max\": true,\n            \"min\": true,\n            \"show\": true,\n            \"sort\": \"max\",\n            \"sortDesc\": true,\n            \"total\": false,\n            \"values\": true\n          },\n          \"lines\": false,\n          \"linewidth\": 1,\n          \"links\": [],\n          \"nullPointMode\": \"null\",\n          \"options\": {\n            \"dataLinks\": []\n          },\n          \"percentage\": false,\n          \"pointradius\": 5,\n          \"points\": false,\n          \"renderer\": \"flot\",\n          \"seriesOverrides\": [],\n          \"spaceLength\": 10,\n          \"stack\": true,\n          \"steppedLine\": false,\n          \"targets\": [\n            {\n              \"expr\": \"sum by (label) (rate(Ratio_failure[$interval]))\",\n              \"format\": \"time_series\",\n              \"interval\": \"\",\n              \"intervalFactor\": 1,\n              \"legendFormat\": \"{{label}}\",\n              \"refId\": \"B\",\n              \"step\": 2\n            }\n          ],\n          \"thresholds\": [],\n          \"timeFrom\": null,\n          \"timeRegions\": [],\n          \"timeShift\": null,\n          \"title\": \"by label\",\n          \"tooltip\": {\n            \"shared\": true,\n            \"sort\": 0,\n            \"value_type\": \"individual\"\n          },\n          \"type\": \"graph\",\n          \"xaxis\": {\n            \"buckets\": null,\n            \"mode\": \"time\",\n            \"name\": null,\n            \"show\": true,\n            \"values\": []\n          },\n          \"yaxes\": [\n            {\n              \"format\": \"short\",\n              \"label\": null,\n              \"logBase\": 1,\n              \"max\": null,\n              \"min\": null,\n              \"show\": true\n            },\n            {\n              \"format\": \"short\",\n              \"label\": null,\n              \"logBase\": 1,\n              \"max\": null,\n              \"min\": null,\n              \"show\": true\n            }\n          ],\n          \"yaxis\": {\n            \"align\": false,\n            \"alignLevel\": null\n          }\n        }\n      ],\n      \"repeat\": null,\n      \"title\": \"Errors\",\n      \"type\": \"row\"\n    },\n    {\n      \"collapsed\": false,\n      \"datasource\": null,\n      \"gridPos\": {\n        \"h\": 1,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 85\n      },\n      \"id\": 22,\n      \"panels\": [],\n      \"title\": \"Test farm\",\n      \"type\": \"row\"\n    },\n    {\n      \"aliasColors\": {},\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 1,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 8,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 86\n      },\n      \"hiddenSeries\": false,\n      \"id\": 24,\n      \"legend\": {\n        \"avg\": false,\n        \"current\": false,\n        \"max\": false,\n        \"min\": false,\n        \"show\": true,\n        \"total\": false,\n        \"values\": false\n      },\n      \"lines\": true,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [\n        {\n          \"alias\": \"jmeter - PS Scavenge v1\",\n          \"yaxis\": 1\n        }\n      ],\n      \"spaceLength\": 10,\n      \"stack\": false,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"rate(jvm_gc_collection_seconds_sum{job=~\\\"jmeter\\\"}[1m])\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{instance}} - {{gc}} \",\n          \"refId\": \"A\"\n        },\n        {\n          \"expr\": \"rate(jvm_gc_collection_seconds_sum{job=~\\\"jmeter\\\"}[$interval]) / ignoring(gc) group_left rate(process_cpu_seconds_total{job=~\\\"jmeter\\\"}[$interval])\",\n          \"format\": \"time_series\",\n          \"hide\": true,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{instance}} - {{gc}} v2\",\n          \"refId\": \"B\"\n        },\n        {\n          \"expr\": \"rate(jvm_gc_collection_seconds_sum{job=~\\\"jmeter\\\"}[1m]) /  rate(jvm_gc_collection_seconds_count{job=~\\\"jmeter\\\"}[1m])\",\n          \"hide\": true,\n          \"legendFormat\": \"{{instance}} - {{gc}} v3\",\n          \"refId\": \"C\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"GC % time\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"decimals\": null,\n          \"format\": \"percentunit\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": \"0\",\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {},\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 1,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 8,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 94\n      },\n      \"hiddenSeries\": false,\n      \"id\": 27,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": true,\n        \"max\": false,\n        \"min\": false,\n        \"rightSide\": true,\n        \"show\": true,\n        \"sort\": \"avg\",\n        \"sortDesc\": false,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"(jvm_memory_pool_bytes_committed {job=~\\\"jmeter\\\"}) - (jvm_memory_pool_bytes_used{job=~\\\"jmeter\\\"}) \",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{instance}} - {{pool}}\",\n          \"refId\": \"A\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"JVM heap  - free memory by pool\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"bytes\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": \"0\",\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {},\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 1,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 8,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 102\n      },\n      \"hiddenSeries\": false,\n      \"id\": 26,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": true,\n        \"max\": false,\n        \"min\": false,\n        \"rightSide\": true,\n        \"show\": true,\n        \"sort\": \"avg\",\n        \"sortDesc\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"(jvm_memory_pool_bytes_used{job=\\\"jmeter\\\"}) \",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{instance}} - {{pool}}\",\n          \"refId\": \"A\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"JVM heap -  used memory by pool\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"bytes\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": \"0\",\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    },\n    {\n      \"aliasColors\": {},\n      \"bars\": false,\n      \"dashLength\": 10,\n      \"dashes\": false,\n      \"datasource\": \"Prometheus\",\n      \"fill\": 1,\n      \"fillGradient\": 0,\n      \"gridPos\": {\n        \"h\": 8,\n        \"w\": 24,\n        \"x\": 0,\n        \"y\": 110\n      },\n      \"hiddenSeries\": false,\n      \"id\": 25,\n      \"legend\": {\n        \"alignAsTable\": true,\n        \"avg\": true,\n        \"current\": true,\n        \"max\": false,\n        \"min\": false,\n        \"rightSide\": true,\n        \"show\": true,\n        \"total\": false,\n        \"values\": true\n      },\n      \"lines\": true,\n      \"linewidth\": 1,\n      \"links\": [],\n      \"nullPointMode\": \"null\",\n      \"options\": {\n        \"dataLinks\": []\n      },\n      \"percentage\": false,\n      \"pointradius\": 5,\n      \"points\": false,\n      \"renderer\": \"flot\",\n      \"seriesOverrides\": [],\n      \"spaceLength\": 10,\n      \"stack\": true,\n      \"steppedLine\": false,\n      \"targets\": [\n        {\n          \"expr\": \"(jvm_memory_bytes_max {job=\\\"jmeter\\\"}) - (jvm_memory_bytes_used{job=\\\"jmeter\\\"})\",\n          \"format\": \"time_series\",\n          \"hide\": false,\n          \"intervalFactor\": 1,\n          \"legendFormat\": \"{{instance}} - {{area}}\",\n          \"refId\": \"A\"\n        }\n      ],\n      \"thresholds\": [],\n      \"timeFrom\": null,\n      \"timeRegions\": [],\n      \"timeShift\": null,\n      \"title\": \"JVM free memory\",\n      \"tooltip\": {\n        \"shared\": true,\n        \"sort\": 0,\n        \"value_type\": \"individual\"\n      },\n      \"type\": \"graph\",\n      \"xaxis\": {\n        \"buckets\": null,\n        \"mode\": \"time\",\n        \"name\": null,\n        \"show\": true,\n        \"values\": []\n      },\n      \"yaxes\": [\n        {\n          \"format\": \"bytes\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": \"0\",\n          \"show\": true\n        },\n        {\n          \"format\": \"short\",\n          \"label\": null,\n          \"logBase\": 1,\n          \"max\": null,\n          \"min\": null,\n          \"show\": true\n        }\n      ],\n      \"yaxis\": {\n        \"align\": false,\n        \"alignLevel\": null\n      }\n    }\n  ],\n  \"refresh\": \"30s\",\n  \"schemaVersion\": 22,\n  \"style\": \"dark\",\n  \"tags\": [\n    \"jmeter\"\n  ],\n  \"templating\": {\n    \"list\": [\n      {\n        \"allValue\": null,\n        \"current\": {\n          \"selected\": false,\n          \"text\": \"30s\",\n          \"value\": \"30s\"\n        },\n        \"hide\": 0,\n        \"includeAll\": false,\n        \"label\": \"interval\",\n        \"multi\": false,\n        \"name\": \"interval\",\n        \"options\": [\n          {\n            \"selected\": true,\n            \"text\": \"30s\",\n            \"value\": \"30s\"\n          },\n          {\n            \"selected\": false,\n            \"text\": \"1m\",\n            \"value\": \"1m\"\n          },\n          {\n            \"selected\": false,\n            \"text\": \"5m\",\n            \"value\": \"5m\"\n          },\n          {\n            \"selected\": false,\n            \"text\": \"10m\",\n            \"value\": \"10m\"\n          }\n        ],\n        \"query\": \"30s,1m,5m,10m\",\n        \"skipUrlSync\": false,\n        \"type\": \"custom\"\n      }\n    ]\n  },\n  \"time\": {\n    \"from\": \"now-15m\",\n    \"to\": \"now\"\n  },\n  \"timepicker\": {\n    \"refresh_intervals\": [\n      \"5s\",\n      \"10s\",\n      \"30s\",\n      \"1m\",\n      \"5m\",\n      \"15m\",\n      \"30m\",\n      \"1h\",\n      \"2h\",\n      \"1d\"\n    ],\n    \"time_options\": [\n      \"5m\",\n      \"15m\",\n      \"1h\",\n      \"6h\",\n      \"12h\",\n      \"24h\",\n      \"2d\",\n      \"7d\",\n      \"30d\"\n    ]\n  },\n  \"timezone\": \"browser\",\n  \"title\": \"JMeter\",\n  \"uid\": \"jbtLA0-Wk5\",\n  \"version\": 2\n}"
  },
  {
    "path": "docs/examples/simple_prometheus_example.jmx",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"4.0\" jmeter=\"4.0 r1823414\">\n  <hashTree>\n    <TestPlan guiclass=\"TestPlanGui\" testclass=\"TestPlan\" testname=\"Test Plan\" enabled=\"true\">\n      <stringProp name=\"TestPlan.comments\"></stringProp>\n      <boolProp name=\"TestPlan.functional_mode\">false</boolProp>\n      <boolProp name=\"TestPlan.tearDown_on_shutdown\">true</boolProp>\n      <boolProp name=\"TestPlan.serialize_threadgroups\">false</boolProp>\n      <elementProp name=\"TestPlan.user_defined_variables\" elementType=\"Arguments\" guiclass=\"ArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n        <collectionProp name=\"Arguments.arguments\"/>\n      </elementProp>\n      <stringProp name=\"TestPlan.user_define_classpath\"></stringProp>\n    </TestPlan>\n    <hashTree>\n      <ThreadGroup guiclass=\"ThreadGroupGui\" testclass=\"ThreadGroup\" testname=\"listener tg\" enabled=\"true\">\n        <stringProp name=\"ThreadGroup.on_sample_error\">continue</stringProp>\n        <elementProp name=\"ThreadGroup.main_controller\" elementType=\"LoopController\" guiclass=\"LoopControlPanel\" testclass=\"LoopController\" testname=\"Loop Controller\" enabled=\"true\">\n          <boolProp name=\"LoopController.continue_forever\">false</boolProp>\n          <intProp name=\"LoopController.loops\">-1</intProp>\n        </elementProp>\n        <stringProp name=\"ThreadGroup.num_threads\">1</stringProp>\n        <stringProp name=\"ThreadGroup.ramp_time\">1</stringProp>\n        <boolProp name=\"ThreadGroup.scheduler\">false</boolProp>\n        <stringProp name=\"ThreadGroup.duration\"></stringProp>\n        <stringProp name=\"ThreadGroup.delay\"></stringProp>\n      </ThreadGroup>\n      <hashTree>\n        <JSR223Sampler guiclass=\"TestBeanGUI\" testclass=\"JSR223Sampler\" testname=\"can_fail_sampler\" enabled=\"true\">\n          <stringProp name=\"TestPlan.comments\">This sampler just does something to generate data for the prometheus listener.\n\nNote that just by calling the function in the parameters section assigns the variable &apos;category&apos; which the PrometheusListener plugin will pick up on.</stringProp>\n          <stringProp name=\"cacheKey\">true</stringProp>\n          <stringProp name=\"filename\"></stringProp>\n          <stringProp name=\"parameters\">${__RandomString(1,ABC,category)}</stringProp>\n          <stringProp name=\"script\">import java.util.Random;\nimport org.apache.commons.lang3.RandomStringUtils;\n\nRandom rand = new Random();\nint maxWait = 3500;\n\n// emulate different &apos;categories&apos; being slower than others\nif(args[0].equals(&quot;A&quot;)) { \n\tmaxWait = 1000;\t\n} else if (args[0].equals(&quot;B&quot;)) {\n\tmaxWait = 1750;\t\n} else if (args[0].equals(&quot;C&quot;)) {\n\tmaxWait = 3000;\t\n}\n\nint wait = rand.nextInt(maxWait);\nint fail = rand.nextInt(10);\n\nlog.info(&quot;sleeping for {} ms&quot;, wait);\n\nThread.currentThread().sleep(wait);\n\nif(fail &gt;= 8 ){\n\tSampleResult.setSuccessful(false);\n\tSampleResult.setResponseCode(&quot;404&quot;);\n}else{\n\tSampleResult.setResponseCode(&quot;204&quot;);\n}\n\nSampleResult.setResponseData(new byte[wait]);\n</stringProp>\n          <stringProp name=\"scriptLanguage\">groovy</stringProp>\n        </JSR223Sampler>\n        <hashTree>\n          <SizeAssertion guiclass=\"SizeAssertionGui\" testclass=\"SizeAssertion\" testname=\"less than 2kB\" enabled=\"true\">\n            <stringProp name=\"Assertion.test_field\">SizeAssertion.response_network_size</stringProp>\n            <stringProp name=\"SizeAssertion.size\">2000</stringProp>\n            <intProp name=\"SizeAssertion.operator\">4</intProp>\n          </SizeAssertion>\n          <hashTree/>\n        </hashTree>\n        <com.github.johrstrom.listener.PrometheusListener guiclass=\"com.github.johrstrom.listener.gui.PrometheusListenerGui\" testclass=\"com.github.johrstrom.listener.PrometheusListener\" testname=\"Main prometheus listener\" enabled=\"true\">\n          <collectionProp name=\"prometheus.collector_definitions\">\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the response time for a jsr223 sampler</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_rt_as_hist</stringProp>\n              <stringProp name=\"collector.type\">HISTOGRAM</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"102727412\">label</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\">100,500,1000,3000</stringProp>\n              <stringProp name=\"listener.collector.listen_to\">samples</stringProp>\n              <stringProp name=\"listener.collector.measuring\">ResponseTime</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the response time for a jsr223 sampler</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_rt_as_summary</stringProp>\n              <stringProp name=\"collector.type\">SUMMARY</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"50511102\">category</stringProp>\n                <stringProp name=\"102727412\">label</stringProp>\n                <stringProp name=\"3059181\">code</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\">0.75,0.5|0.95,0.1|0.99,0.01</stringProp>\n              <stringProp name=\"listener.collector.measuring\">ResponseTime</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the total number of samplers</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_count_total</stringProp>\n              <stringProp name=\"collector.type\">COUNTER</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"102727412\">label</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\"></stringProp>\n              <stringProp name=\"listener.collector.measuring\">CountTotal</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the total number of successful samplers</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_success_total</stringProp>\n              <stringProp name=\"collector.type\">COUNTER</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"102727412\">label</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\"></stringProp>\n              <stringProp name=\"listener.collector.measuring\">SuccessTotal</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the response size for a jsr223 sampler</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_rsize_as_hist</stringProp>\n              <stringProp name=\"collector.type\">HISTOGRAM</stringProp>\n              <collectionProp name=\"collector.labels\"/>\n              <stringProp name=\"collector.quantiles_or_buckets\">100,500,1000,3000</stringProp>\n              <stringProp name=\"listener.collector.measuring\">ResponseSize</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">success ratio of the can_fail_sampler</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_can_fail</stringProp>\n              <stringProp name=\"collector.type\">SUCCESS_RATIO</stringProp>\n              <collectionProp name=\"collector.labels\"/>\n              <stringProp name=\"collector.quantiles_or_buckets\"></stringProp>\n              <stringProp name=\"listener.collector.measuring\">SuccessRatio</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the latency (ttfb) for a jsr223 sampler</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_latency_as_hist</stringProp>\n              <stringProp name=\"collector.type\">HISTOGRAM</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"102727412\">label</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\">100,500,1000,3000</stringProp>\n              <stringProp name=\"listener.collector.measuring\">Latency</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">the idle time for a jsr223 sampler</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_idle_time</stringProp>\n              <stringProp name=\"collector.type\">SUMMARY</stringProp>\n              <collectionProp name=\"collector.labels\"/>\n              <stringProp name=\"collector.quantiles_or_buckets\">0.75,0.5|0.95,0.1|0.99,0.01</stringProp>\n              <stringProp name=\"listener.collector.measuring\">IdleTime</stringProp>\n            </elementProp>\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n              <stringProp name=\"collector.help\">default help string</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_assertions</stringProp>\n              <stringProp name=\"collector.type\">SUCCESS_RATIO</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"102727412\">label</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\"></stringProp>\n              <stringProp name=\"listener.collector.measuring\">SuccessRatio</stringProp>\n              <stringProp name=\"listener.collector.listen_to\">assertions</stringProp>\n            </elementProp>\n          </collectionProp>\n          <stringProp name=\"TestPlan.comments\">This listener &quot;measures&quot; everything, sometimes in summaries, sometimes in histograms.</stringProp>\n        </com.github.johrstrom.listener.PrometheusListener>\n        <hashTree/>\n      </hashTree>\n      <ThreadGroup guiclass=\"ThreadGroupGui\" testclass=\"ThreadGroup\" testname=\"config tg\" enabled=\"true\">\n        <stringProp name=\"ThreadGroup.on_sample_error\">continue</stringProp>\n        <elementProp name=\"ThreadGroup.main_controller\" elementType=\"LoopController\" guiclass=\"LoopControlPanel\" testclass=\"LoopController\" testname=\"Loop Controller\" enabled=\"true\">\n          <boolProp name=\"LoopController.continue_forever\">false</boolProp>\n          <intProp name=\"LoopController.loops\">-1</intProp>\n        </elementProp>\n        <stringProp name=\"ThreadGroup.num_threads\">1</stringProp>\n        <stringProp name=\"ThreadGroup.ramp_time\">1</stringProp>\n        <boolProp name=\"ThreadGroup.scheduler\">false</boolProp>\n        <stringProp name=\"ThreadGroup.duration\"></stringProp>\n        <stringProp name=\"ThreadGroup.delay\"></stringProp>\n      </ThreadGroup>\n      <hashTree>\n        <com.github.johrstrom.config.PrometheusMetricsConfig guiclass=\"com.github.johrstrom.config.gui.PrometheusMetricsConfigGui\" testclass=\"com.github.johrstrom.config.PrometheusMetricsConfig\" testname=\"PrometheusMetricsConfig\" enabled=\"true\">\n          <collectionProp name=\"prometheus.collector_definitions\">\n            <elementProp name=\"\" elementType=\"com.github.johrstrom.collector.BaseCollectorConfig\">\n              <stringProp name=\"collector.help\">default help string</stringProp>\n              <stringProp name=\"collector.metric_name\">jsr223_animals_total</stringProp>\n              <stringProp name=\"collector.type\">COUNTER</stringProp>\n              <collectionProp name=\"collector.labels\">\n                <stringProp name=\"94842723\">color</stringProp>\n                <stringProp name=\"3530753\">size</stringProp>\n                <stringProp name=\"-1081453217\">mammal</stringProp>\n              </collectionProp>\n              <stringProp name=\"collector.quantiles_or_buckets\"></stringProp>\n            </elementProp>\n          </collectionProp>\n        </com.github.johrstrom.config.PrometheusMetricsConfig>\n        <hashTree/>\n        <JSR223Sampler guiclass=\"TestBeanGUI\" testclass=\"JSR223Sampler\" testname=\"JSR223 Sampler\" enabled=\"true\">\n          <stringProp name=\"scriptLanguage\">groovy</stringProp>\n          <stringProp name=\"parameters\">${__RandomString(1,RGB,color)} ${__RandomString(1,SML,size)} ${__RandomString(1,YN,mammal)}</stringProp>\n          <stringProp name=\"filename\"></stringProp>\n          <stringProp name=\"cacheKey\">true</stringProp>\n          <stringProp name=\"script\">import io.prometheus.client.*;\n\n\nString color = vars.get(&quot;color&quot;);\nString size = vars.get(&quot;size&quot;);\nString mammal = vars.get(&quot;mammal&quot;);\n\nCounter counter = (Counter) vars.getObject(&quot;jsr223_animals_total&quot;);\n\ncounter.labels(color,size,mammal).inc();\n</stringProp>\n        </JSR223Sampler>\n        <hashTree>\n          <ConstantTimer guiclass=\"ConstantTimerGui\" testclass=\"ConstantTimer\" testname=\"Constant Timer\" enabled=\"true\">\n            <stringProp name=\"ConstantTimer.delay\">3000</stringProp>\n          </ConstantTimer>\n          <hashTree/>\n        </hashTree>\n      </hashTree>\n      <ThreadGroup guiclass=\"ThreadGroupGui\" testclass=\"ThreadGroup\" testname=\"skip tg\" enabled=\"true\">\n        <stringProp name=\"ThreadGroup.on_sample_error\">continue</stringProp>\n        <elementProp name=\"ThreadGroup.main_controller\" elementType=\"LoopController\" guiclass=\"LoopControlPanel\" testclass=\"LoopController\" testname=\"Loop Controller\" enabled=\"true\">\n          <boolProp name=\"LoopController.continue_forever\">false</boolProp>\n          <intProp name=\"LoopController.loops\">-1</intProp>\n        </elementProp>\n        <stringProp name=\"ThreadGroup.num_threads\">1</stringProp>\n        <stringProp name=\"ThreadGroup.ramp_time\">1</stringProp>\n        <boolProp name=\"ThreadGroup.scheduler\">false</boolProp>\n        <stringProp name=\"ThreadGroup.duration\"></stringProp>\n        <stringProp name=\"ThreadGroup.delay\"></stringProp>\n      </ThreadGroup>\n      <hashTree>\n        <JSR223Sampler guiclass=\"TestBeanGUI\" testclass=\"JSR223Sampler\" testname=\"first_random_sampler\" enabled=\"true\">\n          <stringProp name=\"TestPlan.comments\">This sampler just does something to generate data for the prometheus listener.\n\nNote that just by calling the function in the parameters section assigns the variable &apos;category&apos; which the PrometheusListener plugin will pick up on.</stringProp>\n          <stringProp name=\"cacheKey\">true</stringProp>\n          <stringProp name=\"filename\"></stringProp>\n          <stringProp name=\"parameters\">${__RandomString(1,ABC,category)}</stringProp>\n          <stringProp name=\"script\">import java.util.Random;\n\nRandom rand = new Random();\nint wait = rand.nextInt(3500);\n\nlog.info(&quot;sleeping for {} ms&quot;, wait);\n\nThread.currentThread().sleep(wait);</stringProp>\n          <stringProp name=\"scriptLanguage\">groovy</stringProp>\n        </JSR223Sampler>\n        <hashTree>\n          <com.github.johrstrom.listener.PrometheusListener guiclass=\"com.github.johrstrom.listener.gui.PrometheusListenerGui\" testclass=\"com.github.johrstrom.listener.PrometheusListener\" testname=\"GetFirstSample\" enabled=\"true\">\n            <collectionProp name=\"prometheus.collector_definitions\">\n              <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n                <stringProp name=\"collector.help\">the response time for a jsr223 sampler</stringProp>\n                <stringProp name=\"collector.metric_name\">jsr223_rt_as_hist</stringProp>\n                <stringProp name=\"collector.type\">HISTOGRAM</stringProp>\n                <collectionProp name=\"collector.labels\">\n                  <stringProp name=\"102727412\">label</stringProp>\n                </collectionProp>\n                <stringProp name=\"collector.quantiles_or_buckets\">100,500,1000,3000</stringProp>\n                <stringProp name=\"listener.collector.listen_to\">samples</stringProp>\n                <stringProp name=\"listener.collector.measuring\">ResponseTime</stringProp>\n              </elementProp>\n            </collectionProp>\n          </com.github.johrstrom.listener.PrometheusListener>\n          <hashTree/>\n        </hashTree>\n        <JSR223Sampler guiclass=\"TestBeanGUI\" testclass=\"JSR223Sampler\" testname=\"want_to_skp\" enabled=\"true\">\n          <stringProp name=\"TestPlan.comments\">This sampler just does something to generate data for the prometheus listener.\n\nNote that just by calling the function in the parameters section assigns the variable &apos;category&apos; which the PrometheusListener plugin will pick up on.</stringProp>\n          <stringProp name=\"cacheKey\">true</stringProp>\n          <stringProp name=\"filename\"></stringProp>\n          <stringProp name=\"parameters\">${__RandomString(1,ABC,category)}</stringProp>\n          <stringProp name=\"script\">import java.util.Random;\n\nRandom rand = new Random();\nint wait = rand.nextInt(3500);\n\nlog.info(&quot;sleeping for {} ms&quot;, wait);\n\nThread.currentThread().sleep(wait);</stringProp>\n          <stringProp name=\"scriptLanguage\">groovy</stringProp>\n        </JSR223Sampler>\n        <hashTree/>\n        <JSR223Sampler guiclass=\"TestBeanGUI\" testclass=\"JSR223Sampler\" testname=\"second_random_sampler\" enabled=\"true\">\n          <stringProp name=\"TestPlan.comments\">This sampler just does something to generate data for the prometheus listener.\n\nNote that just by calling the function in the parameters section assigns the variable &apos;category&apos; which the PrometheusListener plugin will pick up on.</stringProp>\n          <stringProp name=\"cacheKey\">true</stringProp>\n          <stringProp name=\"filename\"></stringProp>\n          <stringProp name=\"parameters\">${__RandomString(1,ABC,category)}</stringProp>\n          <stringProp name=\"script\">import java.util.Random;\n\nRandom rand = new Random();\nint wait = rand.nextInt(3500);\n\nlog.info(&quot;sleeping for {} ms&quot;, wait);\n\nThread.currentThread().sleep(wait);</stringProp>\n          <stringProp name=\"scriptLanguage\">groovy</stringProp>\n        </JSR223Sampler>\n        <hashTree>\n          <com.github.johrstrom.listener.PrometheusListener guiclass=\"com.github.johrstrom.listener.gui.PrometheusListenerGui\" testclass=\"com.github.johrstrom.listener.PrometheusListener\" testname=\"GetSampleSample\" enabled=\"true\">\n            <collectionProp name=\"prometheus.collector_definitions\">\n              <elementProp name=\"\" elementType=\"com.github.johrstrom.listener.ListenerCollectorConfig\">\n                <stringProp name=\"collector.help\">the response time for a jsr223 sampler</stringProp>\n                <stringProp name=\"collector.metric_name\">jsr223_rt_as_hist</stringProp>\n                <stringProp name=\"collector.type\">HISTOGRAM</stringProp>\n                <collectionProp name=\"collector.labels\">\n                  <stringProp name=\"102727412\">label</stringProp>\n                </collectionProp>\n                <stringProp name=\"collector.quantiles_or_buckets\">100,500,1000,3000</stringProp>\n                <stringProp name=\"listener.collector.listen_to\">samples</stringProp>\n                <stringProp name=\"listener.collector.measuring\">ResponseTime</stringProp>\n              </elementProp>\n            </collectionProp>\n          </com.github.johrstrom.listener.PrometheusListener>\n          <hashTree/>\n        </hashTree>\n      </hashTree>\n      <ResultCollector guiclass=\"ViewResultsFullVisualizer\" testclass=\"ResultCollector\" testname=\"View Results Tree\" enabled=\"true\">\n        <boolProp name=\"ResultCollector.error_logging\">false</boolProp>\n        <objProp>\n          <name>saveConfig</name>\n          <value class=\"SampleSaveConfiguration\">\n            <time>true</time>\n            <latency>true</latency>\n            <timestamp>true</timestamp>\n            <success>true</success>\n            <label>true</label>\n            <code>true</code>\n            <message>true</message>\n            <threadName>true</threadName>\n            <dataType>true</dataType>\n            <encoding>false</encoding>\n            <assertions>true</assertions>\n            <subresults>true</subresults>\n            <responseData>false</responseData>\n            <samplerData>false</samplerData>\n            <xml>false</xml>\n            <fieldNames>true</fieldNames>\n            <responseHeaders>false</responseHeaders>\n            <requestHeaders>false</requestHeaders>\n            <responseDataOnError>false</responseDataOnError>\n            <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>\n            <assertionsResultsToSave>0</assertionsResultsToSave>\n            <bytes>true</bytes>\n            <sentBytes>true</sentBytes>\n            <threadCounts>true</threadCounts>\n            <idleTime>true</idleTime>\n            <connectTime>true</connectTime>\n          </value>\n        </objProp>\n        <stringProp name=\"filename\"></stringProp>\n      </ResultCollector>\n      <hashTree/>\n    </hashTree>\n  </hashTree>\n</jmeterTestPlan>\n"
  },
  {
    "path": "licenses/io.prometheus.LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n"
  },
  {
    "path": "licenses/io.prometheus.NOTICE",
    "content": "Prometheus instrumentation library for JVM applications\nCopyright 2012-2015 The Prometheus Authors\n\nThis product includes software developed at\nBoxever Ltd. (http://www.boxever.com/).\n\nThis product includes software developed at\nSoundCloud Ltd. (http://soundcloud.com/).\n\nThis product includes software developed as part of the\nOcelli project by Netflix Inc. (https://github.com/Netflix/ocelli/).\n"
  },
  {
    "path": "makefile",
    "content": ".DEFAULT_GOAL := help\nSHELL := /bin/bash\nTHIS_FILE := $(lastword $(MAKEFILE_LIST))\nTHIS_FOLDER := $(shell basename $(CURDIR))\n\n#Service version, tag and image name\nmaster_version := $$(git show master:pom.xml | xmllint --xpath \"//*[local-name()='project']/*[local-name()='version']/text()\" -)\nthis_version := $$(git show master:pom.xml | xmllint --xpath \"//*[local-name()='project']/*[local-name()='version']/text()\" -)\nthis_branch := $(shell git rev-parse --abbrev-ref HEAD)\nrepo_location := $(strip $(shell  git rev-parse --show-toplevel))\nclean ?= false\n\nifeq ($(strip $(clean)),true)\nCLEAN := clean\nendif\n\n.PHONY: help\nhelp:\t\t\t\t\t## Show this help\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[36m%-30s\\033[0m %s\\n\", $$1, $$2}'\n\t\n.PHONY: clean\nclean:              ## Cleans the repo\n\t@mvn clean\n\n.PHONY: build\nbuild:              ## Builds jar\n\t@mvn install -Dgpg.skip\n\n.PHONY: test\ntest:              ## run tests\n\t@mvn verify -Dgpg.skip\n\n\n.PHONY: info\ninfo:    \t\t\t\t\t## Print some info on the repo\n\t@echo \"this_version: $(this_version)\" && \\\n\techo \"this_branch: $(this_branch)\" && \\\n\techo \"repo_location: $(repo_location)\" &&\\\n\techo \"master_version: $(master_version)\"\n\t"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<groupId>com.github.johrstrom</groupId>\n\t<artifactId>jmeter-prometheus-plugin</artifactId>\n\t<version>0.7.2-SNAPSHOT</version>\n\t<name>Jmeter-Prometheus Listener Plugin</name>\n\t<description>A Jmeter plugin that creates a Prometheus endpoint of results.</description>\n\t<url>https://github.com/johrstrom/jmeter-prometheus-plugin</url>\n\n\t<properties>\n\t\t<prometheus.version>0.16.0</prometheus.version>\n\t\t<jmeter.version>5.5</jmeter.version>\n\t\t<maven.compiler.source>1.8</maven.compiler.source>\n\t\t<maven.compiler.target>1.8</maven.compiler.target>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>\n\t</properties>\n\n\t<issueManagement>\n\t\t<url>https://github.com/johrstrom/jmeter-prometheus-plugin/issues</url>\n\t\t<system>GitHub Issues</system>\n\t</issueManagement>\n\n\t<licenses>\n\t\t<license>\n\t\t\t<name>Apache License, Version 2.0</name>\n\t\t\t<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t\t<distribution>repo</distribution>\n\t\t\t<comments>A business-friendly OSS license</comments>\n\t\t</license>\n\t</licenses>\n\n\t<scm>\n\t\t<url>https://github.com/johrstrom/jmeter-prometheus-plugin</url>\n\t\t<connection>scm:git:git://github.com/johrstrom/jmeter-prometheus-plugin.git</connection>\n\t\t<developerConnection>scm:git:git@github.com:johrstrom/jmeter-prometheus-plugin.git</developerConnection>\n\t  <tag>HEAD</tag>\n  \t</scm>\n\n\t<distributionManagement>\n\t\t<snapshotRepository>\n\t\t\t<id>ossrh</id>\n\t\t\t<url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t\t</snapshotRepository>\n\n\t\t<repository>\n\t\t\t<id>ossrh</id>\n\t\t\t<url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>\n\t\t</repository>\n\t</distributionManagement>\n\n\t<developers>\n\t\t<developer>\n\t\t\t<email>johrstrom@hotmail.com</email>\n\t\t\t<name>Jeff Ohrstrom</name>\n\t\t\t<url>https://github.com/johrstrom</url>\n\t\t\t<id>kevinsawicki</id>\n\t\t</developer>\n\t\t<developer>\n\t\t\t<email>giovanni.gibilisco@akamas.io</email>\n\t\t\t<name>Giovanni Paolo Gibilisco</name>\n\t\t\t<url>https://github.com/GiovanniPaoloGibilisco</url>\n\t\t\t<id>GiovanniPaoloGibilisco</id>\n\t\t</developer>\n\t</developers>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.jmeter</groupId>\n\t\t\t<artifactId>ApacheJMeter_core</artifactId>\n\t\t\t<version>${jmeter.version}</version>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.jmeter</groupId>\n\t\t\t<artifactId>ApacheJMeter_java</artifactId>\n\t\t\t<version>${jmeter.version}</version>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t\t<!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_components -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.jmeter</groupId>\n\t\t\t<artifactId>ApacheJMeter_components</artifactId>\n\t\t\t<version>${jmeter.version}</version>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.prometheus</groupId>\n\t\t\t<artifactId>simpleclient</artifactId>\n\t\t\t<version>${prometheus.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.prometheus</groupId>\n\t\t\t<artifactId>simpleclient_servlet</artifactId>\n\t\t\t<version>${prometheus.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.prometheus</groupId>\n\t\t\t<artifactId>simpleclient_httpserver</artifactId>\n\t\t\t<version>${prometheus.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.prometheus</groupId>\n\t\t\t<artifactId>simpleclient_hotspot</artifactId>\n\t\t\t<version>${prometheus.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<version>4.13.1</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\t<build>\n\t\t<resources>\n\t\t\t<resource>\n\t\t\t\t<directory>licenses</directory>\n\t\t\t\t<includes>\n\t\t\t\t\t<include>*</include>\n\t\t\t\t</includes>\n\t\t\t</resource>\n\t\t\t<resource>\n\t\t\t\t<directory>.</directory>\n\t\t\t\t<includes>\n\t\t\t\t\t<include>LICENSE</include>\n\t\t\t\t\t<include>NOTICE</include>\n\t\t\t\t</includes>\n\t\t\t</resource>\n\t\t</resources>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>3.1.0</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<parallel>methods</parallel>\n\t\t\t\t\t<threadCount>4</threadCount>\n\t\t\t\t\t<trimStackTrace>false</trimStackTrace>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-shade-plugin</artifactId>\n\t\t\t\t<version>3.0.0</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<artifactSet>\n\t\t\t\t\t\t<excludes>\n\t\t\t\t\t\t\t<exclude>org.apache.jmeter:*</exclude>\n\t\t\t\t\t\t</excludes>\n\t\t\t\t\t</artifactSet>\n\t\t\t\t\t<!-- put your configurations here -->\n\t\t\t\t\t<filters>\n\t\t\t\t\t\t<filter>\n\t\t\t\t\t\t\t<artifact>*:*</artifact>\n\t\t\t\t\t\t\t<excludes>\n\t\t\t\t\t\t\t\t<exclude>META-INF/*.SF</exclude>\n\t\t\t\t\t\t\t\t<exclude>META-INF/*.DSA</exclude>\n\t\t\t\t\t\t\t\t<exclude>META-INF/*.RSA</exclude>\n\t\t\t\t\t\t\t</excludes>\n\t\t\t\t\t\t</filter>\n\t\t\t\t\t</filters>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>shade</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-resources-plugin</artifactId>\n\t\t\t\t<version>3.0.1</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>copy-resources</id>\n\t\t\t\t\t\t<!-- here the phase you need -->\n\t\t\t\t\t\t<phase>process-test-resources</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>copy-resources</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<outputDirectory>${basedir}/target/test-classes</outputDirectory>\n\t\t\t\t\t\t\t<resources>\n\t\t\t\t\t\t\t\t<resource>\n\t\t\t\t\t\t\t\t\t<directory>docs/examples</directory>\n\t\t\t\t\t\t\t\t\t<filtering>true</filtering>\n\t\t\t\t\t\t\t\t</resource>\n\t\t\t\t\t\t\t</resources>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-release-plugin</artifactId>\n\t\t\t\t<version>2.5.3</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<localCheckout>true</localCheckout>\n\t\t\t\t\t<pushChanges>false</pushChanges>\n\t\t\t\t\t<mavenExecutorId>forked-path</mavenExecutorId>\n\t\t\t\t\t<!-- <arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments> -->\n\t\t\t\t</configuration>\n\t\t\t\t<dependencies>\n\t\t\t\t\t<dependency>\n\t\t\t\t\t\t<groupId>org.apache.maven.scm</groupId>\n\t\t\t\t\t\t<artifactId>maven-scm-provider-gitexe</artifactId>\n\t\t\t\t\t\t<version>1.9.5</version>\n\t\t\t\t\t</dependency>\n\t\t\t\t</dependencies>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-gpg-plugin</artifactId>\n\t\t\t\t<version>${maven-gpg-plugin.version}</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>sign-artifacts</id>\n\t\t\t\t\t\t<phase>verify</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>sign</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-deploy-plugin</artifactId>\n\t\t\t\t<version>2.8.2</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>default-deploy</id>\n\t\t\t\t\t\t<phase>deploy</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>deploy</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.sonatype.plugins</groupId>\n\t\t\t\t<artifactId>nexus-staging-maven-plugin</artifactId>\n\t\t\t\t<version>1.6.7</version>\n\t\t\t\t<extensions>true</extensions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<serverId>ossrh</serverId>\n\t\t\t\t\t<nexusUrl>https://oss.sonatype.org/</nexusUrl>\n\t\t\t\t\t<autoReleaseAfterClose>true</autoReleaseAfterClose>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-source-plugin</artifactId>\n\t\t\t\t<version>3.0.1</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>attach-sources</id>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>jar</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t\n\t\t</plugins>\n\t</build>\n</project>\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/collector/BaseCollectorConfig.java",
    "content": "package com.github.johrstrom.collector;\n\nimport io.prometheus.client.*;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.apache.jmeter.testelement.AbstractTestElement;\nimport org.apache.jmeter.testelement.property.CollectionProperty;\nimport org.apache.jmeter.testelement.property.JMeterProperty;\nimport org.apache.jmeter.testelement.property.NullProperty;\nimport org.apache.jmeter.testelement.property.PropertyIterator;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * The base class for turning text/strings (from the JMeter GUI, or a .jmx file, etc.) into Prometheus Collector\n * objects.  Along with being a TestElement so that it can be serialized (for the .jmx file) it also handles all\n * the logic of parsing strings into double arrays.\n *\n * There are also static utility functions to actually instantiate a Collector of a given type from a configuration.\n *\n *\n * buckets are comma seperated list of decimals. An example:\n * \t\t100,200,300,400.3\n * quantiles are comma seperated pair of decimals seperated by |. The first decimal being the quantile and the second being the\n * error rating. An example:\n * \t\t0.999,0.1|0.99,0.2|0.75,0.3\n *\n * @author Jeff Ohrstrom\n *\n */\npublic class BaseCollectorConfig extends AbstractTestElement  {\n\n\tprivate static final long serialVersionUID = 1520731432941268549L;\n\n\tpublic static String HELP = \"collector.help\";\n\tpublic static String NAME = \"collector.metric_name\";\n\tpublic static String TYPE = \"collector.type\";\n\tpublic static String LABELS = \"collector.labels\";\n\tpublic static String QUANTILES_OR_BUCKETS = \"collector.quantiles_or_buckets\";\n\n\tpublic static double[] DEFAULT_BUCKET_SIZES = {100,500,1000,3000};\n\tpublic static String DEFAULT_BUCKET_SIZES_STRING = \"100,500,1000,3000\";\n\tpublic static long DEFAULT_QUANTILE_WINDOW_LENGHT = 60;\n\n\tpublic static QuantileDefinition[] DEFAULT_QUANTILES = QuantileDefinition.defaultQuantiles();\n\tpublic static String DEFAULT_QUANTILES_STRING = QuantileDefinition.arrayToString(DEFAULT_QUANTILES);\n\n\n\tpublic static String DEFAULT_HELP_STRING = \"default help string\";\n\tpublic static String METRIC_NAME_BASE = \"jmeter_autogenerated_metric_\";\n\n\tprivate static Logger log = LoggerFactory.getLogger(BaseCollectorConfig.class);\n\n\tpublic enum JMeterCollectorType {\n\t\tCOUNTER,\n\t\tGAUGE,\n\t\tHISTOGRAM,\n\t\tSUMMARY,\n\t\tSUCCESS_RATIO\n\t}\n\n\tpublic BaseCollectorConfig(){\n\t\tthis.setHelp(DEFAULT_HELP_STRING);\n\t\tthis.setMetricName(this.getRandomMetricName());\n\t\tthis.setType(JMeterCollectorType.COUNTER.toString());\n\t\tthis.setLabels(new String[0]);\n\t\tthis.setQuantileOrBucket(\"\");\n\t}\n\n\tpublic String getHelp() {\n\t\treturn this.getPropertyAsString(HELP, DEFAULT_HELP_STRING);\n\t}\n\n\tpublic void setHelp(String help) {\n\t\tif(help == null || help.isEmpty())\n\t\t\tthis.setProperty(HELP, DEFAULT_HELP_STRING);\n\t\telse\n\t\t\tthis.setProperty(HELP, help);\n\t}\n\n\tpublic String getType() {\n\t\treturn this.getPropertyAsString(TYPE, JMeterCollectorType.COUNTER.name());\n\t}\n\n\tpublic JMeterCollectorType getCollectorType() {\n\t\treturn JMeterCollectorType.valueOf(this.getType());\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.setProperty(TYPE, type);\n\n\t\tif(type != null && type.equals(JMeterCollectorType.HISTOGRAM.name()) && this.getQuantileOrBucket().isEmpty())\n\t\t\tthis.setQuantileOrBucket(DEFAULT_BUCKET_SIZES_STRING);\n\t\tif(type != null && type.equals(JMeterCollectorType.SUMMARY.name()) && this.getQuantileOrBucket().isEmpty())\n\t\t\tthis.setQuantileOrBucket(DEFAULT_QUANTILES_STRING);\n\t}\n\n\tpublic String getQuantileOrBucket() {\n\t\treturn this.getPropertyAsString(QUANTILES_OR_BUCKETS,\"\");\n\t}\n\n\tpublic void setQuantileOrBucket(String quantileOrBucket) {\n\t\tthis.setProperty(QUANTILES_OR_BUCKETS, quantileOrBucket);\n\t}\n\n\tpublic double[] getBuckets() {\n\t\tString buckets = getQuantileOrBucket();\n\n\t\tif(buckets == null || buckets.isEmpty()) {\n\t\t\treturn DEFAULT_BUCKET_SIZES;\n\t\t}else {\n\t\t\treturn this.parseBucketsFromString(buckets);\n\t\t}\n\t}\n\n\tpublic QuantileDefinition[] getQuantiles() {\n\t\tString quantiles = getQuantileOrBucket();\n\n\t\tif(quantiles == null || quantiles.isEmpty()) {\n\t\t\treturn DEFAULT_QUANTILES;\n\t\t}else {\n\t\t\treturn QuantileDefinition.parseQuantilesFromString(quantiles);\n\t\t}\n\t}\n\n\tpublic long getQuantileWindowLength() {\n\t\tString quantiles = getQuantileOrBucket();\n\n\t\tif (quantiles == null || quantiles.isEmpty()) {\n\t\t\treturn DEFAULT_QUANTILE_WINDOW_LENGHT;\n\t\t} else {\n\t\t\treturn QuantileDefinition.parseQuantilesWindowLengthFromString(quantiles);\n\t\t}\n\t}\n\n\tpublic String getMetricName() {\n\t\treturn this.getPropertyAsString(NAME, getRandomMetricName());\n\t}\n\n\tpublic void setMetricName(String name) {\n\t\tif(name == null || name.isEmpty())\n\t\t\tthis.setProperty(NAME, getRandomMetricName());\n\t\telse\n\t\t\tthis.setProperty(NAME, name);\n\t}\n\n\tpublic String getRandomMetricName() {\n\t\treturn METRIC_NAME_BASE + RandomStringUtils.randomAlphanumeric(8);\n\t}\n\n\tpublic void setLabels(String labels) {\n\t\tthis.setLabels(labels.split(\",\"));\n\t}\n\n\tpublic void setLabels(String[] labels) {\n\t\tList<String> list = new ArrayList<String>(Arrays.asList(labels));\n\n\t\tIterator<String> it = list.iterator();\n\t\twhile(it.hasNext()) { // can't have empty strings from Gui\n\t\t\tString item = it.next();\n\t\t\tif(item == null || item.isEmpty()) {\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\n\t\tthis.setProperty(new CollectionProperty(LABELS, list));\n\t}\n\n\tpublic String[] getLabels() {\n\t\tJMeterProperty prop = this.getProperty(LABELS);\n\t\tif(prop == null || prop instanceof NullProperty) {\n\t\t\treturn new String[0];\n\t\t}\n\n\t\tCollectionProperty colletion = (CollectionProperty) prop;\n\t\tString[] retArray = new String[colletion.size()];\n\t\tPropertyIterator it = colletion.iterator();\n\n\t\tint i=0;\n\t\twhile(it.hasNext()) {\n\t\t\tString next = it.next().getStringValue();\n\t\t\tretArray[i] = next;\n\t\t\ti++;\n\t\t}\n\n\t\treturn retArray;\n\t}\n\n\tpublic String getLabelsAsString() {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tString[] labels = this.getLabels();\n\t\tfor(int i = 0; i < labels.length; i++) {\n\t\t\tsb.append(labels[i]);\n\t\t\tif(i+1 < labels.length)\n\t\t\t\tsb.append(\",\");\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\tpublic static Counter newCounter(BaseCollectorConfig cfg) throws Exception {\n\t\tio.prometheus.client.Counter.Builder builder = new Counter.Builder()\n\t\t\t.help(cfg.getHelp())\n\t\t\t.name(cfg.getMetricName());\n\n\t\tString[] labels = cfg.getLabels();\n\t\tif(labels.length != 0) {\n\t\t\tbuilder.labelNames(labels);\n\t\t}\n\n\t\treturn builder.create();\n\t}\n\n\tpublic static Summary newSummary(BaseCollectorConfig cfg) throws Exception {\n\t\tio.prometheus.client.Summary.Builder builder = new Summary.Builder()\n\t\t\t\t.name(cfg.getMetricName())\n\t\t\t\t.help(cfg.getHelp())\n\t\t\t\t.maxAgeSeconds(cfg.getQuantileWindowLength());\n\n\t\tString[] labels = cfg.getLabels();\n\t\tif(labels.length != 0) {\n\t\t\tbuilder.labelNames(labels);\n\t\t}\n\n\t\tfor(QuantileDefinition def : cfg.getQuantiles()) {\n\t\t\tbuilder.quantile(def.quantile, def.error);\n\t\t}\n\n\t\treturn builder.create();\n\t}\n\n\tpublic static Histogram newHistogram(BaseCollectorConfig cfg) throws Exception {\n\t\tio.prometheus.client.Histogram.Builder builder = new Histogram.Builder()\n\t\t\t\t.name(cfg.getMetricName())\n\t\t\t\t.help(cfg.getHelp())\n\t\t\t\t.buckets(cfg.getBuckets());\n\n\t\tString[] labels = cfg.getLabels();\n\t\tif(labels.length != 0) {\n\t\t\tbuilder.labelNames(labels);\n\t\t}\n\n\t\treturn builder.create();\n\t}\n\n\tpublic static Gauge newGauge(BaseCollectorConfig cfg) throws Exception {\n\t\tio.prometheus.client.Gauge.Builder builder =  new Gauge.Builder()\n\t\t\t\t.name(cfg.getMetricName())\n\t\t\t\t.help(cfg.getHelp());\n\n\t\tString[] labels = cfg.getLabels();\n\t\tif(labels.length != 0) {\n\t\t\tbuilder.labelNames(labels);\n\t\t}\n\n\t\treturn builder.create();\n\t}\n\n\tpublic static Collector fromConfig(BaseCollectorConfig cfg) {\n\t\tJMeterCollectorType t = cfg.getCollectorType();\n\t\tCollector c = null;\n\n\t\ttry {\n\t\t\tif(t.equals(JMeterCollectorType.COUNTER)) {\n\t\t\t\tc = BaseCollectorConfig.newCounter(cfg);\n\n\t\t\t}else if(t.equals(JMeterCollectorType.SUMMARY)) {\n\t\t\t\tc = BaseCollectorConfig.newSummary(cfg);\n\n\t\t\t}else if(t.equals(JMeterCollectorType.HISTOGRAM)) {\n\t\t\t\tc = BaseCollectorConfig.newHistogram(cfg);\n\t\t\t}else if(t.equals(JMeterCollectorType.GAUGE)) {\n\t\t\t\tc = BaseCollectorConfig.newGauge(cfg);\n\t\t\t}else if(t.equals(JMeterCollectorType.SUCCESS_RATIO)) {\n\t\t\t\tc = new SuccessRatioCollector(cfg);\n\t\t\t}\n\n\t\t} catch(Exception e) {\n\t\t\tlog.error(String.format(\"Didn't create collector from definition %s because of an error\", cfg), e);\n\t\t}\n\n\t\treturn c;\n\t}\n\n\n\n\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif(o instanceof BaseCollectorConfig) {\n\t\t\tBaseCollectorConfig other = (BaseCollectorConfig) o;\n\t\t\tboolean sameName = this.getName().equals(other.getName());\n\t\t\tboolean sameHelp = this.getHelp().equals(other.getHelp());\n\t\t\tboolean sameType = this.getType().equals(other.getType());\n\t\t\tboolean sameQB = this.getQuantileOrBucket().equals(other.getQuantileOrBucket());\n\t\t\tboolean sameLabels = this.getLabelsAsString().equalsIgnoreCase(other.getLabelsAsString());\n\n\t\t\treturn sameName && sameHelp && sameType && sameQB & sameLabels;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\n\t    final int prime = 31;\n\t    int result = 1;\n\t    result = prime * result + this.getMetricName().hashCode();\n\t    result = prime * result + this.getHelp().hashCode();\n\t    result = prime * result + this.getType().hashCode();\n\t    result = prime * result + this.getQuantileOrBucket().hashCode();\n\t    result = prime * result + this.getLabelsAsString().hashCode();\n\n\t    return result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tPropertyIterator it = this.propertyIterator();\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"[\");\n\t\twhile(it.hasNext()) {\n\t\t\tJMeterProperty prop = it.next();\n\t\t\tsb.append(prop.getName()).append(\": \").append(prop.getStringValue()).append(\", \");\n\t\t}\n\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\tprotected double[] parseBucketsFromString(String fullBucketString) {\n\t\tString[] bucketStrings = fullBucketString.split(\",\");\n\t\tList<Double> buckets = new ArrayList<Double>();\n\n\t\tfor(String bucket : bucketStrings) {\n\t\t\ttry {\n\t\t\t\tdouble d = Double.parseDouble(bucket);\n\t\t\t\tbuckets.add(d);\n\t\t\t}catch(Exception e) {\n\t\t\t\tlog.warn(\"couldn't parse {} because of error {}:{}. It wont be included in buckets for the metric {}\",\n\t\t\t\t\t\tbucket, e.getClass().toString(), e.getMessage(), this.getMetricName());\n\t\t\t}\n\t\t}\n\n\t\tif(buckets.isEmpty()) {\n\t\t\tlog.warn(\"Did not parse any buckets for metric {}. Returning defaults\", this.getMetricName());\n\t\t\treturn DEFAULT_BUCKET_SIZES;\n\t\t}else {\n\t\t\treturn buckets.stream().mapToDouble(Double::doubleValue).toArray();\n\t\t}\n\t}\n\n\n\t/**\n\t * A very simple POJO for holding Quantiles and the error rating for them.\n\t *\n\t * @author Jeff ohrstrom\n\t *\n\t */\n\tpublic static class QuantileDefinition {\n\t\tpublic double quantile;\n\t\tpublic double error;\n\t\tpublic static final String QUANTILE_ERROR_SEPERATOR = \",\";\n\t\tpublic static final String QUANTILE_DEFINITION_SEPERATOR = \"|\";\n\t\tpublic static final String QUANTILE_DEFINITION_SEPERATOR_REGEX = \"\\\\|\";\n\t\tpublic static final String QUANTILE_LENGTH_SEPERATOR_REGEX = \"\\\\;\";\n\n\t\tQuantileDefinition(double quantile, double error) {\n\t\t\tthis.quantile = quantile;\n\t\t\tthis.error = error;\n\t\t}\n\n\t\tQuantileDefinition(String quantile, String error) throws NumberFormatException {\n\t\t\tthis(new String[]{quantile, error});\n\t\t}\n\n\t\tQuantileDefinition(String[] definition) throws NumberFormatException {\n\t\t\tif(definition.length != 2) {\n\t\t\t\tthrow new IllegalArgumentException(String.format(\"Quantiles need exactly 2 parameters. %d given.\", definition.length));\n\t\t\t}\n\t\t\tthis.quantile = Double.parseDouble(definition[0]);\n\t\t\tthis.error = Double.parseDouble(definition[1]);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn new StringBuilder()\n\t\t\t\t\t.append(this.quantile)\n\t\t\t\t\t.append(QUANTILE_ERROR_SEPERATOR)\n\t\t\t\t\t.append(this.error)\n\t\t\t\t\t.toString();\n\t\t}\n\n\t\tpublic static QuantileDefinition[] defaultQuantiles() {\n\t\t\tQuantileDefinition[] def = new QuantileDefinition[3];\n\n\t\t\tdef[0] = new QuantileDefinition(0.75,0.5);\n\t\t\tdef[1] = new QuantileDefinition(0.95,0.1);\n\t\t\tdef[2] = new QuantileDefinition(0.99,0.01);\n\n\t\t\treturn def;\n\t\t}\n\n\t\tpublic static String arrayToString(QuantileDefinition[] definitions) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\tfor(int i = 0; i < definitions.length; i++) {\n\t\t\t\tsb.append(definitions[i]);\n\t\t\t\tif(i+1 < definitions.length)\n\t\t\t\t\tsb.append(QUANTILE_DEFINITION_SEPERATOR);\n\t\t\t}\n\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tpublic static QuantileDefinition[] parseQuantilesFromString(String fullQuantileString) {\n\t\t\tString quantileOnlyString = fullQuantileString.split(QUANTILE_LENGTH_SEPERATOR_REGEX)[0];\n\t\t\tString[] quantileDefStrings = quantileOnlyString.split(QUANTILE_DEFINITION_SEPERATOR_REGEX);\n\t\t\tList<QuantileDefinition> quantiles = new ArrayList<QuantileDefinition>();\n\n\t\t\tfor(String quantile : quantileDefStrings) {\n\t\t\t\ttry {\n\t\t\t\t\tQuantileDefinition q = new QuantileDefinition(quantile.split(QUANTILE_ERROR_SEPERATOR));\n\t\t\t\t\tquantiles.add(q);\n\t\t\t\t}catch(Exception e) {\n\t\t\t\t\tlog.warn(\"couldn't parse {} because of error {}:{}. It wont be included in quantiles for the metric\",\n\t\t\t\t\t\t\tquantile, e.getClass().toString(), e.getMessage());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(quantiles.isEmpty()) {\n\t\t\t\tlog.warn(\"Did not parse any quantiles, returning defaults.\");\n\t\t\t\treturn DEFAULT_QUANTILES;\n\t\t\t}else {\n\t\t\t\treturn quantiles.toArray(new QuantileDefinition[quantiles.size()]);\n\t\t\t}\n\t\t}\n\n\n\t\tpublic static long parseQuantilesWindowLengthFromString(String fullQuantileString) {\n\t\t\tString[] quantileDefStrings = fullQuantileString.split(QUANTILE_LENGTH_SEPERATOR_REGEX);\n\t\t\tif (quantileDefStrings.length < 2) {\n\t\t\t\tlog.debug(\"Using default quantile window lenght of \" + DEFAULT_QUANTILE_WINDOW_LENGHT + \" seconds\");\n\t\t\t\treturn DEFAULT_QUANTILE_WINDOW_LENGHT;\n\t\t\t}\n\t\t\treturn Long.parseLong(quantileDefStrings[1]);\n\t\t}\n\n\n\t}\n\n\n}\n\n\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/collector/CollectorElement.java",
    "content": "package com.github.johrstrom.collector;\n\nimport java.util.*;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.apache.jmeter.testelement.AbstractTestElement;\nimport org.apache.jmeter.testelement.property.*;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.prometheus.client.Collector;\n\npublic abstract class CollectorElement<C extends BaseCollectorConfig> extends AbstractTestElement {\n\n\tpublic static final String COLLECTOR_DEF = \"prometheus.collector_definitions\";\n\n\tprotected Map<C, Collector> collectors = new HashMap<C, Collector>();\n\tprotected transient JMeterCollectorRegistry registry = JMeterCollectorRegistry.getInstance();\n\n\tprivate static Logger log = LoggerFactory.getLogger(CollectorElement.class);\n\tprivate static final long serialVersionUID = 963612021269632269L;\n\n\tpublic CollectorElement() {\n\t\tlog.debug(\"making a new config element: \" + this.toString());\n\t\tthis.setCollectorConfigs(new ArrayList<C>());\n\t}\n\n\tpublic CollectionProperty getCollectorConfigs() {\n\t\tJMeterProperty collectorDefinitions = this.getProperty(COLLECTOR_DEF);\n\n\t\tif (collectorDefinitions == null || collectorDefinitions instanceof NullProperty) {\n\t\t\tcollectorDefinitions = new CollectionProperty(COLLECTOR_DEF, new ArrayList<C>());\n\t\t\tcollectorDefinitions.setName(COLLECTOR_DEF);\n\t\t}\n\n\t\treturn (CollectionProperty) collectorDefinitions;\n\n\t}\n\n\tpublic void setCollectorConfigs(List<C> collectors) {\n\t\tlog.debug(\"setting new collectors. size is: \" + collectors.size());\n\t\tthis.setCollectorConfigs(new CollectionProperty(COLLECTOR_DEF, collectors));\n\t}\n\n\tpublic void setCollectorConfigs(CollectionProperty collectors) {\n\t\tthis.setProperty(collectors);\n\t}\n\n\tprotected void clearCollectors() {\n\t\tIterator<Entry<C, Collector>> iter = this.collectors.entrySet().iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tEntry<C, Collector> entry = iter.next();\n\t\t\tthis.registry.unregister(entry.getKey());\n\t\t\titer.remove();\n\t\t}\n\t}\n\n\tprotected void makeNewCollectors() {\n\t\tthis.clearCollectors();\n\n\t\tCollectionProperty collectorDefs = this.getCollectorConfigs();\n\t\tPropertyIterator iter = collectorDefs.iterator();\n\n\t\twhile (iter.hasNext()) {\n\n\t\t\ttry {\n\t\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\t\tC config = (C) iter.next().getObjectValue();\n\t\t\t\tCollector collector = registry.getOrCreateAndRegister(config);\n\n\t\t\t\tthis.collectors.put(config, collector);\n\t\t\t\tlog.debug(\"added \" + config.getMetricName() + \" to list of collectors\");\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.error(\"Didn't create new collector because of error, \", e);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/collector/JMeterCollectorRegistry.java",
    "content": "package com.github.johrstrom.collector;\n\nimport io.prometheus.client.Collector;\nimport io.prometheus.client.CollectorRegistry;\nimport io.prometheus.client.Gauge;\nimport io.prometheus.client.hotspot.*;\nimport org.apache.jmeter.threads.JMeterContextService;\nimport org.apache.jmeter.threads.JMeterContextService.ThreadCounts;\nimport org.apache.jmeter.util.JMeterUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class JMeterCollectorRegistry extends CollectorRegistry {\n\n\tprivate static JMeterCollectorRegistry instance = null;\n\tprivate static Logger log = LoggerFactory.getLogger(JMeterCollectorRegistry.class);\n\tprivate ConcurrentHashMap<BaseCollectorConfig,Collector> registered = new ConcurrentHashMap<>();\n\t\n\tprivate static final boolean saveThreads = \n\t\t\tJMeterUtils.getPropDefault(ThreadCollector.COLLECT_THREADS, ThreadCollector.COLLECT_THREADS_DEFAULT);\n\t\n\tpublic static final String COLLECT_JVM  = \"prometheus.save.jvm\";\n\tpublic static final boolean COLLECT_JVM_DEFAULT  = true;\n\tprivate static final boolean saveJVM = JMeterUtils.getPropDefault(COLLECT_JVM, COLLECT_JVM_DEFAULT);\n\t\n\t\t\t\n\tpublic synchronized static JMeterCollectorRegistry getInstance() {\n\t\tif (instance == null) {\n\t\t\tlog.debug(\"Creating prometheus collector registry\");\n\t\t\tinstance = new JMeterCollectorRegistry();\n\t\t}\n\t\treturn instance;\n\t}\n\t\n\tprivate JMeterCollectorRegistry() {\n\t\tsuper(true);\n\t\tthis.initDefaultExports();\n\t\tthis.createJMeterExports();\n\t}\n\t\n\tprivate void initDefaultExports() {\n\t\tif(saveJVM) {\n\t\t    new StandardExports().register(this);\n\t\t    new MemoryPoolsExports().register(this);\n\t\t    new MemoryAllocationExports().register(this);\n\t\t    new BufferPoolsExports().register(this);\n\t\t    new GarbageCollectorExports().register(this);\n\t\t    new ThreadExports().register(this);\n\t\t    new ClassLoadingExports().register(this);\n\t\t    new VersionInfoExports().register(this);\t\n\t\t}\n\t}\n\t\n\tprivate void createJMeterExports() {\n\t\tif(saveThreads) {\n\t\t\tThreadCollector tc = new ThreadCollector();\n\t\t\tthis.register(tc);\n\t\t\tthis.registered.put(ThreadCollector.getConfig(), tc);\n\t\t}\n\t}\n\n\t\n\tpublic synchronized void unregister(BaseCollectorConfig cfg) {\n\t\tlog.debug(\"unregistering {}\", cfg.getMetricName());\n\t\tif(registered.containsKey(cfg)) {\n\t\t\tCollector collector = registered.get(cfg);\n\t\t\t\n\t\t\ttry {\n\t\t\t\tthis.unregister(collector);\n\t\t\t\tthis.registered.remove(cfg);\n\t\t\t} catch(Exception e) {\n\t\t\t\tlog.error(\"can't unregister collector because error: \", e);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\tpublic synchronized Collector getOrCreateAndRegister(BaseCollectorConfig cfg) { \n\t\tif(registered.containsKey(cfg)) {\n\t\t\tlog.trace(\"{} found already registered.\", cfg.getMetricName());\n\t\t\treturn registered.get(cfg);\n\t\t}else {\n\t\t\tCollector c = BaseCollectorConfig.fromConfig(cfg);\n\t\t\tthis.register(c);\t//throws exception here if it fails to register\n\t\t\t\n\t\t\tthis.registered.put(cfg, c);\n\t\t\tlog.debug(\"created and registered {}\", cfg);\n\t\t\treturn c;\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic synchronized void clear() {\n\t\tsuper.clear();\n\t\tthis.registered.clear();\n\t}\n\t\n\tprivate static class ThreadCollector extends Collector {\n\t\t\n\t\tpublic static final String COLLECT_THREADS_NAME = \"prometheus.save.threads.name\";\n\t\tpublic static final String COLLECT_THREADS_NAME_DEFAULT = \"jmeter_threads\";\n\t\t\n\t\tpublic static final String COLLECT_THREADS = \"prometheus.save.threads\";\n\t\tpublic static final boolean COLLECT_THREADS_DEFAULT = true;\n\t\t\n\t\t\n\t\tprivate final Gauge innerCollector;\n\t \n\t\tprotected ThreadCollector() {\n\t\t\tBaseCollectorConfig cfg = getConfig();\n\t\t\t\n\t\t\tinnerCollector = Gauge.build()\n\t\t\t\t\t.name(cfg.getMetricName())\n\t\t\t\t\t.labelNames(cfg.getLabels())\n\t\t\t\t\t.help(cfg.getHelp())\n\t\t\t\t\t.create();\n\t\t}\n\t\t\n\t\tprotected static BaseCollectorConfig getConfig() {\n\t\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\n\t\t\tcfg.setHelp(\"Gauge for jmeter threads\");\n\t\t\tcfg.setMetricName(threadMetricName());\n\t\t\tcfg.setLabels(new String[] {\"state\"});\n\t\t\tcfg.setType(Type.GAUGE.name());\n\t\t\t\n\t\t\treturn cfg;\n\t\t}\n\t\t\n\t\t\n\t\tpublic static String threadMetricName() {\n\t\t\treturn JMeterUtils.getPropDefault(COLLECT_THREADS_NAME, COLLECT_THREADS_NAME_DEFAULT);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<MetricFamilySamples> collect() {\t\n\t\t\tThreadCounts tc = JMeterContextService.getThreadCounts();\n\t\t\t\n\t\t\tinnerCollector.labels(\"active\").set(tc.activeThreads);\n\t\t\tinnerCollector.labels(\"finished\").set(tc.finishedThreads);\n\t\t\tinnerCollector.labels(\"started\").set(tc.startedThreads);\n\t\t\t\n\t\t\treturn innerCollector.collect();\n\t\t}\n\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/collector/SuccessRatioCollector.java",
    "content": "package com.github.johrstrom.collector;\n\nimport io.prometheus.client.Collector;\nimport io.prometheus.client.Counter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SuccessRatioCollector extends Collector {\n\n\tprivate final Counter success, failure, total;\n\t\n\tpublic SuccessRatioCollector(BaseCollectorConfig config) {\n\t\tthis.success = new Counter.Builder()\n\t\t\t\t.help(config.getHelp())\n\t\t\t\t.name(extendedName(config.getMetricName(), \"success\"))\n\t\t\t\t.labelNames(config.getLabels())\n\t\t\t\t.create();\n\t\t\n\t\tthis.failure = new Counter.Builder()\n\t\t\t\t.help(config.getHelp())\n\t\t\t\t.name(extendedName(config.getMetricName(), \"failure\"))\n\t\t\t\t.labelNames(config.getLabels())\n\t\t\t\t.create();\n\t\t\n\t\tthis.total = new Counter.Builder()\n\t\t\t\t.help(config.getHelp())\n\t\t\t\t.name(extendedName(config.getMetricName(), \"total\"))\n\t\t\t\t.labelNames(config.getLabels())\n\t\t\t\t.create();\n\t\t\n\t}\n\t\n\tpublic void incrementSuccess(String[] labels) {\n\t\tthis.success.labels(labels).inc();\n\t\tthis.total.labels(labels).inc();\n\t\t\n\t\t// this ensures that we emit 0 when this set of labels has \n\t\t// never failed. i.e, total = success when failure = 0. \n\t\tif(this.getFailure(labels) < 1 ) {\n\t\t\tthis.failure.labels(labels).inc(0);\n\t\t}\n\t}\n\t\n\tpublic void incrementFailure(String[] labels) {\n\t\tthis.failure.labels(labels).inc();\n\t\tthis.total.labels(labels).inc();\n\t\t\n\t\t// this ensures that we emit 0 when this set of labels has \n\t\t// never succeeded. i.e, total = failures when success = 0.\n\t\tif(this.getSuccess(labels) < 1) {\n\t\t\tthis.success.labels(labels).inc(0);\n\t\t}\n\t}\n\t\n\tpublic double getSuccess(String[] labels) {\n\t\treturn this.success.labels(labels).get();\n\t}\n\t\n\tpublic double getFailure(String[] labels) {\n\t\treturn this.failure.labels(labels).get();\n\t}\n\t\n\tpublic double getTotal(String[] labels) {\n\t\treturn this.total.labels(labels).get();\n\t}\n\n\t@Override\n\tpublic List<MetricFamilySamples> collect() {\n\t\tArrayList<MetricFamilySamples> metrics = new ArrayList<MetricFamilySamples>();\n\n\t\tmetrics.addAll(this.success.collect());\n\t\tmetrics.addAll(this.failure.collect());\n\t\tmetrics.addAll(this.total.collect());\n\t\t\n\t\treturn metrics;\n\t}\n\t\n\tprivate static String extendedName(String orig, String append) {\n\t\tStringBuilder sb = new StringBuilder(32);\n\t\t\n\t\tsb.append(orig);\n\t\tif(!orig.endsWith(\"_\")) {\n\t\t\tsb.append(\"_\");\n\t\t}\n\t\tsb.append(append);\n\t\t\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/collector/gui/AbstractCollectorTable.java",
    "content": "package com.github.johrstrom.collector.gui;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.CollectorElement;\nimport org.apache.jmeter.gui.util.HorizontalPanel;\nimport org.apache.jmeter.gui.util.VerticalPanel;\nimport org.apache.jmeter.testelement.property.CollectionProperty;\nimport org.apache.jmeter.testelement.property.PropertyIterator;\nimport org.apache.jorphan.gui.ObjectTableModel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic abstract class AbstractCollectorTable<C extends BaseCollectorConfig> \n\textends JPanel implements ActionListener {\n\n\tpublic static final String ADD = \"Add\";\n\tpublic static final String DELETE = \"Delete\";\n\t\n\tprotected transient JTable table;\n\tprotected transient ObjectTableModel model;\n\tprotected JButton add,delete;\n\t\n\t\n\tprivate final Class<C> clazzType;\n\tprivate static final long serialVersionUID = 2027712606129940455L;\n\tprivate Logger log = LoggerFactory.getLogger(AbstractCollectorTable.class);\n\t\n\t\n\t\n\t/**\n\t * @return\n\t */\n\tpublic abstract Flatten getGuiHelper();\n\t\n\t\n\t/**\n\t * \n\t */\n\tpublic abstract void modifyColumns();\n\t\n\t\n\tpublic AbstractCollectorTable(Class<C> collectorType) {\n\t\tclazzType = collectorType;\n\t\tthis.init();\n\t\tthis.modifyColumns();\n\t}\n\t\n\tpublic List<C> getRowsAsCollectors(){\n\t\tArrayList<C> collectors = new ArrayList<>();\n\t\t\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tIterator<C> iter = (Iterator<C>) this.model.iterator();\n\t\t\n\t\twhile(iter.hasNext()) {\n\t\t\tC cfg = this.clazzType.cast(iter.next());\n\t\t\tcollectors.add(cfg);\n\t\t\tlog.debug(\"populated config: \" + cfg.toString() + \" from table.\");\n\t\t}\n\t\t\n\t\treturn collectors;\n\t}\n\t\n\tpublic void clearModelData() {\n\t\tthis.model.clearData();\n\t}\n\t\n//\tpublic void modifyTestElement(CollectorElement<C> ele) {\n//\t\t\n//\t\tif(!(ele instanceof CollectorElement)) {\n//\t\t\treturn;\n//\t\t}\n//\t\t\n//\t\tint rows = this.model.getRowCount();\n//\t\tArrayList<C> collectors = new ArrayList<>();\n//\t\t\n//\t\t@SuppressWarnings(\"unchecked\")\n//\t\tCollectorElement<C> config = (CollectorElement<C>) ele;\n//\n//\t\tlog.debug(\"modifying test element \" + ele.toString() + \". row count in model is \" + rows);\n//\t\t\n//\t\t@SuppressWarnings(\"unchecked\")\n//\t\tIterator<C> iter = (Iterator<C>) model.iterator();\n//\t\t\n//\t\twhile(iter.hasNext()) {\n//\t\t\tC cfg = this.clazzType.cast(iter.next());\n//\t\t\tcollectors.add(cfg);\n//\t\t\tlog.debug(\"populated config: \" + cfg.toString() + \" from table.\");\n//\t\t}\n//\t\t\n//\t\tconfig.setCollectorConfigs(collectors);\n//\t\tthis.setCollector(config);\n//\t}\n\t\n\t\n\tpublic void populateTable(CollectorElement<C> config) {\n\t\t\n\t\tCollectionProperty collectors = config.getCollectorConfigs();\n\t\tlog.debug(\"Configuring table with \" + collectors.size() + \" collectors.\");\n\t\t\n\t\tthis.model.clearData();\n\t\t\n\t\tPropertyIterator it = collectors.iterator();\n\t\twhile(it.hasNext()) {\n\t\t\tBaseCollectorConfig cfg = (BaseCollectorConfig) it.next().getObjectValue();\t\t\t \n\t\t\tthis.model.addRow(cfg);\n\t\t\tlog.debug(\"added row into table: \" + cfg.toString());\n\t\t}\n\t\t\n\t}\n\t\n\t\n\t/**\n\t * Private helper function to initialize all the Swing components.\n\t */\n\tprotected void init() {\n\t\tthis.setLayout(new BorderLayout(0, 5));\n\t\t\n\t\tVerticalPanel panel = new VerticalPanel();\n\t\tpanel.add(makeTablePanel());\n\t\tpanel.add(makeButtonPanel());\n\t\t\n\t\tthis.add(panel, BorderLayout.CENTER);\n\t}\n\t\n\t\n\tprotected Component makeTablePanel() {\n\t\tFlatten helper = this.getGuiHelper();\n\t\t\n\t\tthis.model = new ObjectTableModel(\n\t\t\t\thelper.getHeaders(),\n\t\t\t\tthis.clazzType,\n\t\t\t\thelper.getReadFunctors(),\n\t\t\t\thelper.getWriteFunctors(),\n\t\t\t\thelper.getEditorClasses()\n\t\t);\n\t\t\n\t\tthis.table = new JTable(this.model);\n\t\t\n\t\tJScrollPane scrollPane = new JScrollPane(this.table);\n\t\ttable.setFillsViewportHeight(true);\n\t\t\n\t\treturn scrollPane;\n\t}\n\n\tprotected JPanel makeButtonPanel() {\n\t\t\n\t\tadd = new JButton(ADD); \n\t\tadd.setActionCommand(ADD);\n\t\tadd.setEnabled(true);\n\t\tadd.addActionListener(this);\n\t\t\n\t\tdelete = new JButton(DELETE); \n\t\tdelete.setActionCommand(DELETE);\n\t\tdelete.setEnabled(true);\n\t\tdelete.addActionListener(this);\n\t\t\n\t\tHorizontalPanel panel = new HorizontalPanel();\n\t\tpanel.add(add);\n\t\tpanel.add(delete);\n\t\t\n\t\treturn panel;\n\t}\n\t\n\t@Override\n\tpublic void actionPerformed(ActionEvent event) {\n\t\tswitch (event.getActionCommand()) {\n\t\tcase ADD:\n\t\t\ttry {\n\t\t\t\tthis.model.addRow(this.clazzType.getDeclaredConstructor().newInstance());\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.error(\"Couldn't add to model. \", e);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase DELETE:\n\t\t\tdeleteSelectedRows();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tprotected void deleteSelectedRows() {\n\t\tint[] rows = table.getSelectedRows();\n\t\t\n\t\tfor(int i = 0; i < rows.length; i++) {\n\t\t\tthis.model.removeRow(rows[i]);\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/collector/gui/Flatten.java",
    "content": "package com.github.johrstrom.collector.gui;\n\nimport org.apache.jorphan.reflect.Functor;\n\npublic interface Flatten {\n\t\n\tpublic Functor[] getReadFunctors();\n\tpublic Functor[] getWriteFunctors();\n\tpublic String[] getHeaders();\n\tpublic Class<?>[] getEditorClasses();\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/config/PrometheusMetricsConfig.java",
    "content": "package com.github.johrstrom.config;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.CollectorElement;\nimport io.prometheus.client.Collector;\nimport org.apache.jmeter.engine.util.NoThreadClone;\nimport org.apache.jmeter.testelement.TestStateListener;\nimport org.apache.jmeter.testelement.property.CollectionProperty;\nimport org.apache.jmeter.testelement.property.JMeterProperty;\nimport org.apache.jmeter.threads.JMeterVariables;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Map.Entry;\n\npublic class PrometheusMetricsConfig extends CollectorElement<BaseCollectorConfig>\n\timplements NoThreadClone, TestStateListener {\n\n\tprivate static final long serialVersionUID = 7602510312126862226L;\n\n\tprivate Logger log = LoggerFactory.getLogger(PrometheusMetricsConfig.class);\n\t\n\t@Override\n\tpublic void testEnded() {\n\t\tthis.setRunningVersion(false);\n\t\tJMeterVariables variables = getThreadContext().getVariables();\n\t\t\n\t\tfor (Entry<BaseCollectorConfig, Collector> entry : this.collectors.entrySet()) {\n\t\t\tBaseCollectorConfig cfg = entry.getKey();\n\t\t\tvariables.remove(cfg.getMetricName());\n\t\t}\n\t\n\t\tthis.clearCollectors();\t\n\t}\n\n\t@Override\n\tpublic void testEnded(String arg0) {\n\t\tthis.testEnded();\n\t}\n\n\t@Override\n\tpublic void testStarted() {\n\t\tthis.setRunningVersion(true);\n\t\tthis.makeNewCollectors();\n\t\tJMeterVariables variables = getThreadContext().getVariables();\n\n\t\t\n\t\tlog.debug(\"Test started, adding {} collectors to variables\", this.collectors.size());\n\t\t\n\t\tfor (Entry<BaseCollectorConfig, Collector> entry : this.collectors.entrySet()) {\n\t\t\tBaseCollectorConfig cfg = entry.getKey(); \n\t\t\tvariables.putObject(cfg.getMetricName(), entry.getValue());\n\t\t\tlog.debug(\"Added ({},{}) to variables.\", entry.getKey(), entry.getValue().toString());\n\t\t}\n \t}\n\n\t@Override\n\tpublic void testStarted(String arg0) {\n\t\tthis.testStarted();\t\t\n\t}\n\t\n\t@Override\n\tpublic PrometheusMetricsConfig clone() {\n\t\tPrometheusMetricsConfig clone = new PrometheusMetricsConfig();\n\t\tclone.setCollectorConfigs(this.getCollectorConfigs());\n\t\t\t\t\n\t\treturn clone;\n\t}\n\t\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof PrometheusMetricsConfig) {\n\t\t\tPrometheusMetricsConfig other = (PrometheusMetricsConfig) o;\n\t\t\t\n\t\t\tCollectionProperty thisConfig = this.getCollectorConfigs();\n\t\t\tCollectionProperty otherConfig = other.getCollectorConfigs();\n\t\t\tboolean sameSize = thisConfig.size() == otherConfig.size();\n\t\t\t\n\t\t\tfor (int i = 0; i < thisConfig.size(); i++) {\n\t\t\t\tJMeterProperty left = thisConfig.get(i);\n\t\t\t\tJMeterProperty right = otherConfig.get(i);\n\t\t\t\t\n\t\t\t\tif(!left.equals(right)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn true && sameSize;\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/config/gui/ConfigCollectorTable.java",
    "content": "package com.github.johrstrom.config.gui;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.BaseCollectorConfig.JMeterCollectorType;\nimport com.github.johrstrom.collector.gui.AbstractCollectorTable;\nimport com.github.johrstrom.collector.gui.Flatten;\nimport org.apache.jorphan.reflect.Functor;\n\nimport javax.swing.*;\nimport javax.swing.table.TableColumn;\n\n\npublic class ConfigCollectorTable extends AbstractCollectorTable<BaseCollectorConfig>  \n\timplements Flatten {\n\n\tpublic static JComboBox<String> typeComboBox; \n\t\n\tpublic static int METRIC_NAME_INDEX = 0;\n\tpublic static int HELP_INDEX = 1;\n\tpublic static int LABEL_NAME_INDEX = 2;\n\tpublic static int TYPE_INDEX = 3;\n\tpublic static int QUANTILE_OR_BUCKET_INDEX = 4;\n\tpublic static int BASE_COLUMN_SIZE = 5;\n\t\n\tprivate static final long serialVersionUID = 8675797078488652676L;\n\t\n\tstatic {\n\t\ttypeComboBox = new JComboBox<>();\n\t\ttypeComboBox.addItem(JMeterCollectorType.COUNTER.toString());\n\t\ttypeComboBox.addItem(JMeterCollectorType.SUMMARY.toString());\n\t\ttypeComboBox.addItem(JMeterCollectorType.HISTOGRAM.toString());\n\t\ttypeComboBox.addItem(JMeterCollectorType.GAUGE.toString());\n\t\ttypeComboBox.addItem(JMeterCollectorType.SUCCESS_RATIO.toString());\n\t}\n\t\n\tpublic ConfigCollectorTable() {\n\t\tsuper(BaseCollectorConfig.class);\n\t}\n\n\t@Override\n\tpublic Flatten getGuiHelper() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void modifyColumns() {\n\t\tTableColumn column = this.table.getColumnModel().getColumn(TYPE_INDEX);\n\t\tcolumn.setCellEditor(new DefaultCellEditor(typeComboBox));\n\t}\n\t\t\n\t@Override\n\tpublic Functor[] getReadFunctors() {\n\t\tFunctor[] functors = new Functor[BASE_COLUMN_SIZE];\n\t\t\n\t\tfunctors[METRIC_NAME_INDEX] = new Functor(\"getMetricName\");\n\t\tfunctors[HELP_INDEX] = new Functor(\"getHelp\");\n\t\tfunctors[LABEL_NAME_INDEX] = new Functor(\"getLabelsAsString\");\n\t\tfunctors[TYPE_INDEX] = new Functor(\"getType\");\n\t\tfunctors[QUANTILE_OR_BUCKET_INDEX] = new Functor(\"getQuantileOrBucket\");\n\t\t\t\n\t\treturn functors;\n\t}\n\n\t@Override\n\tpublic Functor[] getWriteFunctors() {\n\t\tFunctor[] functors = new Functor[BASE_COLUMN_SIZE];\n\t\t\n\t\tfunctors[METRIC_NAME_INDEX] = new Functor(\"setMetricName\");\n\t\tfunctors[HELP_INDEX] = new Functor(\"setHelp\");\n\t\tfunctors[LABEL_NAME_INDEX] = new Functor(\"setLabels\");\n\t\tfunctors[TYPE_INDEX] = new Functor(\"setType\");\n\t\tfunctors[QUANTILE_OR_BUCKET_INDEX] = new Functor(\"setQuantileOrBucket\");\n\t\t\n\t\treturn functors;\n\t}\n\n\t@Override\n\tpublic String[] getHeaders() {\n\t\tString[] headers = new String[BASE_COLUMN_SIZE];\n\t\t\n\t\theaders[METRIC_NAME_INDEX] = \"Name\";\n\t\theaders[HELP_INDEX] = \"Help\";\n\t\theaders[LABEL_NAME_INDEX] = \"Labels\";\n\t\theaders[TYPE_INDEX] = \"Type\";\n\t\theaders[QUANTILE_OR_BUCKET_INDEX] = \"Buckets or Quantiles\";\n\t\t\n\t\treturn headers;\n\t}\n\n\t@Override\n\tpublic Class<?>[] getEditorClasses() {\n\t\tClass<?>[] clazzes = new Class<?>[BASE_COLUMN_SIZE];\n\t\t\n\t\tclazzes[METRIC_NAME_INDEX] = String.class;\n\t\tclazzes[HELP_INDEX] = String.class;\n\t\tclazzes[LABEL_NAME_INDEX] = String.class;\n\t\tclazzes[TYPE_INDEX] = ComboBoxEditor.class;\n\t\tclazzes[QUANTILE_OR_BUCKET_INDEX] = String.class;\n\t\t\n\t\treturn clazzes;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/config/gui/PrometheusMetricsConfigGui.java",
    "content": "package com.github.johrstrom.config.gui;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.CollectorElement;\nimport com.github.johrstrom.config.PrometheusMetricsConfig;\nimport org.apache.jmeter.config.gui.AbstractConfigGui;\nimport org.apache.jmeter.testelement.TestElement;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.awt.*;\nimport java.util.List;\n\n\npublic class PrometheusMetricsConfigGui<C> extends AbstractConfigGui {\n\t\n\tprivate static final long serialVersionUID = 6741986237897976082L;\n//\tprivate PrometheusMetricsConfig config;\n\tprivate ConfigCollectorTable table = new ConfigCollectorTable();\n\n\tprivate Logger log = LoggerFactory.getLogger(PrometheusMetricsConfigGui.class);\n\t\n\tpublic PrometheusMetricsConfigGui(){\n\t\tsuper();\n\t\tlog.debug(\"making a new config gui: {}\", this.toString());\n\t\tinit();\n\t}\n\t\n\t@Override\n\tpublic TestElement createTestElement() {\n\t\tPrometheusMetricsConfig cfg = new PrometheusMetricsConfig();\n\t\t\n\t\tcfg.setProperty(TestElement.GUI_CLASS, PrometheusMetricsConfigGui.class.getName());\n\t\tcfg.setProperty(TestElement.TEST_CLASS, PrometheusMetricsConfig.class.getName());\n\t\tthis.modifyTestElement(cfg);\n\t\t\n\t\treturn cfg;\n\t}\n\n\t@Override\n\tpublic String getLabelResource() {\n\t\treturn getClass().getCanonicalName();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see org.apache.jmeter.gui.AbstractJMeterGuiComponent#getStaticLabel()\n\t */\n\t@Override\n\tpublic String getStaticLabel() {\n\t\treturn \"Prometheus Metrics\";\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t * \n\t * @see org.apache.jmeter.gui.AbstractJMeterGuiComponent#getName()\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn super.getName() == null ? this.getStaticLabel() : super.getName();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void modifyTestElement(TestElement ele) {\n\t\t\n\t\tif(!(ele instanceof CollectorElement)) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tCollectorElement<BaseCollectorConfig> config = (CollectorElement<BaseCollectorConfig>) ele;\n\t\t\n\t\tList<BaseCollectorConfig> collectors = this.table.getRowsAsCollectors();\n\t\tconfig.setCollectorConfigs(collectors);\t\n\t\t\n\n\t\tconfig.setName(this.getName());\n\t\tconfig.setComment(this.getComment());\n\t}\n\n\tprivate void init() {\n\t\tthis.setLayout(new BorderLayout(0, 5));\n\t\t\n\t\tthis.add(makeTitlePanel(), BorderLayout.NORTH);\n\t\tthis.add(this.table, BorderLayout.CENTER);\n\t}\n\t\n\t\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void configure(TestElement ele) {\n\t\tsuper.configure(ele);\n\t\t\n\t\tif(ele instanceof CollectorElement<?>) {\n\t\t\ttry {\n\t\t\t\tthis.table.populateTable((CollectorElement<BaseCollectorConfig>) ele);\n\t\t\t} catch(Exception e) {\n\t\t\t\tlog.error(\"didn't modify test element because {}:{}\", e.getClass(), e.getMessage());\n\t\t\t}\n\t\t}\n\t\t\n\t\tthis.setName(ele.getName());\n\t\tthis.setComment(ele.getComment());\n\t}\n\n\t@Override\n\tpublic void clearGui() {\n\t\tsuper.clearGui();\n\t\tthis.table.clearModelData();\n\t}\n\t\n\t@Override\n\tprotected PrometheusMetricsConfigGui<C> clone() {\n\t\treturn new PrometheusMetricsConfigGui<C>();\n\t}\n\t\n\t\n\t\n}\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/ListenerCollectorConfig.java",
    "content": "package com.github.johrstrom.listener;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\n\npublic class ListenerCollectorConfig extends BaseCollectorConfig {\n\n\tprivate static final long serialVersionUID = -8968099072667146399L;\n\n\tpublic static final String LISTEN_TO = \"listener.collector.listen_to\";\n\tpublic static final String MEASURING = \"listener.collector.measuring\";\n\tpublic static final String SAMPLES = \"samples\";\n\tpublic static final String ASSERTIONS = \"assertions\";\n\t\n\tpublic ListenerCollectorConfig() {\n\t\tthis(new BaseCollectorConfig());\n\t}\n\t\n\tpublic ListenerCollectorConfig(BaseCollectorConfig base) {\n\t\tthis.setMetricName(base.getMetricName());\n\t\tthis.setType(base.getType());\n\t\tthis.setHelp(base.getHelp());\n\t\tthis.setLabels(base.getLabels());\n\t}\n\t\n\tpublic enum Measurable {\n\t\t// for aggregated type updater\n\t\tResponseTime,\n\t\tResponseSize,\n\t\tLatency,\n\t\tIdleTime,\n\t\tConnectTime,\n\t\t\n\t\t// for count type updater\n\t\tSuccessTotal,\n\t\tFailureTotal,\n\t\tCountTotal,\n\t\tSuccessRatio;\n\t}\n\t\n\tpublic void setListenTo(String listenTo) {\n\t\tif(listenTo.equalsIgnoreCase(SAMPLES)) {\n\t\t\tthis.setProperty(LISTEN_TO, SAMPLES);\n\t\t}else if(listenTo.equalsIgnoreCase(ASSERTIONS)) {\n\t\t\tthis.setProperty(LISTEN_TO, ASSERTIONS);\n\t\t} else {\n\t\t\tthis.setProperty(LISTEN_TO, SAMPLES);\n\t\t}\n\t}\n\t\n\tpublic String getListenTo() {\n\t\treturn this.getPropertyAsString(LISTEN_TO, SAMPLES);\n\t}\n\t\n\tpublic void setMeasuring(String measuring) {\n\t\tthis.setProperty(MEASURING, measuring);\n\t}\n\t\n\tpublic String getMeasuring() {\n\t\treturn this.getPropertyAsString(MEASURING, Measurable.ResponseTime.toString());\n\t}\n\t\n\tpublic Measurable getMeasuringAsEnum() {\t\t\n\t\treturn Measurable.valueOf(this.getMeasuring());\n\t}\n\t\n\tpublic boolean listenToSamples() {\n\t\treturn this.getListenTo().equalsIgnoreCase(SAMPLES);\n\t}\n\t\n\tpublic boolean listenToAssertions() {\n\t\treturn this.getListenTo().equalsIgnoreCase(ASSERTIONS);\n\t}\n\t\n//\t@Override\n//\tpublic boolean equals(Object o) {\n//\t\tif(o instanceof ListenerCollectorConfig) {\n//\t\t\tboolean base = super.equals(o);\n//\t\t\tListenerCollectorConfig other = (ListenerCollectorConfig) o;\n//\t\t\t\n//\t\t\tboolean measuring = this.getMeasuring().equals(other.getMeasuring());\n//\t\t\tboolean listenTo = this.getListenTo().equals(other.getListenTo());\n//\t\t\t\n//\t\t\treturn base && measuring && listenTo;\n//\t\t} \n//\t\t\n//\t\treturn false;\n//\t}\n//\n//\t@Override\n//\tpublic int hashCode() {\n//\t\t\n//\t    final int prime = 31;\n//\t    int result = 1;\n//\t    result = prime * result + this.getName().hashCode();\n//\t    result = prime * result + this.getHelp().hashCode();\n//\t    result = prime * result + this.getType().hashCode();\n//\t    result = prime * result + this.getQuantileOrBucket().hashCode();\n//\t    result = prime * result + this.getLabelsAsString().hashCode();\n//\t    result = prime * result + this.getListenTo().hashCode();\n//\t    result = prime * result + this.getMeasuring().hashCode();\n//\t   \n//\t    return result;\n//\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/PrometheusListener.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed  under the  License is distributed on an \"AS IS\" BASIS,\n * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or\n * implied.\n *\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.github.johrstrom.listener;\n\nimport com.github.johrstrom.collector.CollectorElement;\nimport com.github.johrstrom.collector.JMeterCollectorRegistry;\nimport com.github.johrstrom.listener.updater.AbstractUpdater;\nimport com.github.johrstrom.listener.updater.AggregatedTypeUpdater;\nimport com.github.johrstrom.listener.updater.CountTypeUpdater;\nimport io.prometheus.client.Collector;\nimport org.apache.jmeter.engine.util.NoThreadClone;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.samplers.SampleListener;\nimport org.apache.jmeter.testelement.TestStateListener;\nimport org.apache.jmeter.testelement.property.CollectionProperty;\nimport org.apache.jmeter.testelement.property.JMeterProperty;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * The main test element listener class of this library. Jmeter updates this\n * class through the SampleListener interface and it in turn updates the\n * CollectorRegistry. This class is also a TestStateListener to control when it\n * starts up or shuts down the server that ultimately serves Prometheus the\n * results through an http api.\n *\n * @author Jeff Ohrstrom\n */\npublic class PrometheusListener extends CollectorElement<ListenerCollectorConfig>\n\t\timplements SampleListener, Serializable, TestStateListener, NoThreadClone {\n\n\tprivate static final long serialVersionUID = -4833646252357876746L;\n\n\tprivate static final Logger log = LoggerFactory.getLogger(PrometheusListener.class);\n\n\tprivate transient PrometheusServer server = PrometheusServer.getInstance();\n\n\tprivate List<AbstractUpdater> updaters;\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.samplers.SampleListener#sampleOccurred(org.apache.\n\t * jmeter.samplers.SampleEvent)\n\t */\n\t@Override\n\tpublic void sampleOccurred(SampleEvent event) {\n\n\t\tfor (AbstractUpdater updater : this.updaters) {\n\t\t\tupdater.update(event);\n\t\t}\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see\n\t * org.apache.jmeter.samplers.SampleListener#sampleStarted(org.apache.jmeter\n\t * .samplers.SampleEvent)\n\t */\n\t@Override\n\tpublic void sampleStarted(SampleEvent arg0) {\n\t\t// do nothing\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see\n\t * org.apache.jmeter.samplers.SampleListener#sampleStopped(org.apache.jmeter\n\t * .samplers.SampleEvent)\n\t */\n\t@Override\n\tpublic void sampleStopped(SampleEvent arg0) {\n\t\t// do nothing\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.testelement.TestStateListener#testEnded()\n\t */\n\t@Override\n\tpublic void testEnded() {\n\t\ttry {\n\t\t\tthis.server.stop();\n\t\t} catch (Exception e) {\n\t\t\tlog.error(\"Couldn't stop http server\", e);\n\t\t}\n\n\t\tthis.clearCollectors();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.testelement.TestStateListener#testEnded(java.lang.\n\t * String)\n\t */\n\t@Override\n\tpublic void testEnded(String arg0) {\n\t\tthis.testEnded();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.testelement.TestStateListener#testStarted()\n\t */\n\t@Override\n\tpublic void testStarted() {\n\t\t// update the configuration\n\t\tthis.makeNewCollectors();\n\t\t// this.registerAllCollectors();\n\n\t\ttry {\n\t\t\tif (server == null) {\n\t\t\t\tlog.warn(\"Prometheus server has not yet been initialized, doing it now\");\n\t\t\t\tserver = PrometheusServer.getInstance();\n\t\t\t}\n\t\t\tserver.start();\n\t\t} catch (Exception e) {\n\t\t\tlog.error(\"Couldn't start http server\", e);\n\t\t}\n\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.testelement.TestStateListener#testStarted(java.lang.\n\t * String)\n\t */\n\t@Override\n\tpublic void testStarted(String arg0) {\n\t\tthis.testStarted();\n\t}\n\n\t@Override\n\tprotected void makeNewCollectors() {\n\t\t// this.clearCollectors();\n\t\tif (this.registry == null) {\n\t\t\tlog.warn(\"Collector registry has not yet been initialized, doing it now\");\n\t\t\tregistry = JMeterCollectorRegistry.getInstance();\n\t\t}\n\t\tthis.updaters = new ArrayList<AbstractUpdater>();\n\n\t\tCollectionProperty collectorDefs = this.getCollectorConfigs();\n\n\t\tfor (JMeterProperty collectorDef : collectorDefs) {\n\n\t\t\ttry {\n\t\t\t\tListenerCollectorConfig config = (ListenerCollectorConfig) collectorDef.getObjectValue();\n\t\t\t\tlog.debug(\"Creating collector from configuration: \" + config);\n\t\t\t\tCollector collector = this.registry.getOrCreateAndRegister(config);\n\t\t\t\tAbstractUpdater updater = null;\n\n\t\t\t\tswitch (config.getMeasuringAsEnum()) {\n\t\t\t\tcase CountTotal:\n\t\t\t\tcase FailureTotal:\n\t\t\t\tcase SuccessTotal:\n\t\t\t\tcase SuccessRatio:\n\t\t\t\t\tupdater = new CountTypeUpdater(config);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ResponseSize:\n\t\t\t\tcase ResponseTime:\n\t\t\t\tcase Latency:\n\t\t\t\tcase IdleTime:\n\t\t\t\tcase ConnectTime:\n\t\t\t\t\tupdater = new AggregatedTypeUpdater(config);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// hope our IDEs are telling us to use all possible enums!\n\t\t\t\t\tlog.error(config.getMeasuringAsEnum() + \" triggered default case, which means there's \"\n\t\t\t\t\t\t\t+ \"no functionality for this and is likely a bug\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tthis.collectors.put(config, collector);\n\t\t\t\tthis.updaters.add(updater);\n\t\t\t\tlog.debug(\"added \" + config.getMetricName() + \" to list of collectors\");\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.error(\"Didn't create new collector because of error, \", e);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/PrometheusServer.java",
    "content": "package com.github.johrstrom.listener;\n\nimport com.github.johrstrom.collector.JMeterCollectorRegistry;\nimport com.sun.net.httpserver.HttpExchange;\nimport com.sun.net.httpserver.HttpHandler;\nimport com.sun.net.httpserver.HttpServer;\nimport io.prometheus.client.CollectorRegistry;\nimport io.prometheus.client.exporter.common.TextFormat;\nimport org.apache.jmeter.util.JMeterUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.net.HttpURLConnection;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.URLDecoder;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.zip.GZIPOutputStream;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Expose Prometheus metrics using a plain Java HttpServer.\n * <p>\n * Example Usage:\n * <pre>\n * {@code\n * HTTPServer server = new HTTPServer(1234);\n * }\n * </pre>\n * */\npublic class PrometheusServer {\n\t\n\tpublic static final String PROMETHEUS_PORT = \"prometheus.port\";\n\tpublic static final int PROMETHEUS_PORT_DEFAULT = 9270;\n\t\n\tpublic static final String PROMETHEUS_DELAY = \"prometheus.delay\";\n\tpublic static final int PROMETHEUS_DELAY_DEFAULT = 0;\n\n\tpublic static final String PROMETHEUS_IP = \"prometheus.ip\";\n\tpublic static final String PROMETHEUS_IP_DEFAULT = \"127.0.0.1\";\n\n\tprivate static final Logger log = LoggerFactory.getLogger(PrometheusServer.class);\n\n\tprivate static class LocalByteArray extends ThreadLocal<ByteArrayOutputStream> {\n\t    protected ByteArrayOutputStream initialValue() {\n\t        return new ByteArrayOutputStream(1 << 20);\n\t    }\n\t}\n\n    static class HTTPMetricHandler implements HttpHandler {\n        private CollectorRegistry registry;\n        private final LocalByteArray response = new LocalByteArray();\n\n        HTTPMetricHandler(CollectorRegistry registry) {\n          this.registry = registry;\n        }\n\n\n        public void handle(HttpExchange t) throws IOException {\n            String query = t.getRequestURI().getRawQuery();\n\n            ByteArrayOutputStream response = this.response.get();\n            response.reset();\n            OutputStreamWriter osw = new OutputStreamWriter(response);\n            TextFormat.write004(osw,\n                    registry.filteredMetricFamilySamples(parseQuery(query)));\n            osw.flush();\n            osw.close();\n            response.flush();\n            response.close();\n\n            t.getResponseHeaders().set(\"Content-Type\",\n                    TextFormat.CONTENT_TYPE_004);\n            if (shouldUseCompression(t)) {\n                t.getResponseHeaders().set(\"Content-Encoding\", \"gzip\");\n                t.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0);\n                final GZIPOutputStream os = new GZIPOutputStream(t.getResponseBody());\n                response.writeTo(os);\n                os.close();\n            } else {\n                t.getResponseHeaders().set(\"Content-Length\",\n                        String.valueOf(response.size()));\n                t.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.size());\n                response.writeTo(t.getResponseBody());\n            }\n            t.close();\n        }\n\n    }\n\n    protected static boolean shouldUseCompression(HttpExchange exchange) {\n        List<String> encodingHeaders = exchange.getRequestHeaders().get(\"Accept-Encoding\");\n        if (encodingHeaders == null) return false;\n\n        for (String encodingHeader : encodingHeaders) {\n            String[] encodings = encodingHeader.split(\",\");\n            for (String encoding : encodings) {\n                if (encoding.trim().toLowerCase().equals(\"gzip\")) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    protected static Set<String> parseQuery(String query) throws IOException {\n        Set<String> names = new HashSet<String>();\n        if (query != null) {\n            String[] pairs = query.split(\"&\");\n            for (String pair : pairs) {\n                int idx = pair.indexOf(\"=\");\n                if (idx != -1 && URLDecoder.decode(pair.substring(0, idx), \"UTF-8\").equals(\"name[]\")) {\n                    names.add(URLDecoder.decode(pair.substring(idx + 1), \"UTF-8\"));\n                }\n            }\n        }\n        return names;\n    }\n\n    private HttpServer server;\n    private static PrometheusServer instance = null;\n    private int port = JMeterUtils.getPropDefault(PROMETHEUS_PORT, PROMETHEUS_PORT_DEFAULT);\n    private int delay = JMeterUtils.getPropDefault(PROMETHEUS_DELAY, PROMETHEUS_DELAY_DEFAULT);\n    private String ip = JMeterUtils.getPropDefault(PROMETHEUS_IP, PROMETHEUS_IP_DEFAULT);\n\n    protected static final HTTPMetricHandler metricHandler = new HTTPMetricHandler(JMeterCollectorRegistry.getInstance());\n\n    public synchronized static PrometheusServer getInstance() {\n    \tif(instance == null) {\n    \t\tlog.debug(\"Creating Prometheus Server\");\n    \t\tinstance = new PrometheusServer();\n    \t}\n    \t\n    \treturn instance;\n    }\n\n    private PrometheusServer() { }\n    \n    public synchronized void start() throws IOException {\n    \tif(server != null){\n    \t\tserver.stop(0);\n            ((ExecutorService) this.server.getExecutor()).shutdown();\n    \t}\n\n        server = HttpServer.create();\n        InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(ip), port);\n        \n        server.bind(addr, 3);\n\n        server.createContext(\"/\", metricHandler);\n        server.createContext(\"/metrics\", metricHandler);\n        \n\n        server.setExecutor(Executors.newSingleThreadExecutor());\n        server.start();      \n    }\n    \n    public synchronized void stop() throws InterruptedException {\n    \tTimeUnit.SECONDS.sleep(delay);\n    \tserver.stop(0);\n    \t((ExecutorService) this.server.getExecutor()).shutdown();\n    }\n\n}\n\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/gui/ListenerCollectorTable.java",
    "content": "package com.github.johrstrom.listener.gui;\n\nimport com.github.johrstrom.collector.gui.AbstractCollectorTable;\nimport com.github.johrstrom.collector.gui.Flatten;\nimport com.github.johrstrom.config.gui.ConfigCollectorTable;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.listener.ListenerCollectorConfig.Measurable;\nimport org.apache.jorphan.reflect.Functor;\n\nimport javax.swing.*;\nimport javax.swing.table.TableColumn;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class ListenerCollectorTable extends AbstractCollectorTable<ListenerCollectorConfig> \n\timplements Flatten {\n\n\n\n\tprivate static final long serialVersionUID = 4429063284832140575L;\n\t\n\tpublic static JComboBox<String> listenToComboBox, measuringComboBox; \n\tprivate static ConfigCollectorTable stealFrom = new ConfigCollectorTable();\n\t\n\tpublic static int LISTEN_TO_INDEX = ConfigCollectorTable.BASE_COLUMN_SIZE;\n\tpublic static int MEASURING_INDEX = LISTEN_TO_INDEX + 1;\n\t\n\tstatic {\n\t\tlistenToComboBox = new JComboBox<>();\n\t\tlistenToComboBox.addItem(ListenerCollectorConfig.SAMPLES);\n\t\tlistenToComboBox.addItem(ListenerCollectorConfig.ASSERTIONS);\n\t\t\n\t\tmeasuringComboBox = measuringBox();\n\t}\n\t\n\tpublic ListenerCollectorTable() {\n\t\tsuper(ListenerCollectorConfig.class);\n\t}\n\t\n\t@Override\n\tpublic Flatten getGuiHelper() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void modifyColumns() {\n\t\tTableColumn column = table.getColumnModel().getColumn(ConfigCollectorTable.TYPE_INDEX);\n\t\tcolumn.setCellEditor(new DefaultCellEditor(ConfigCollectorTable.typeComboBox));\n\t\t\n\t\tcolumn = table.getColumnModel().getColumn(ListenerCollectorTable.LISTEN_TO_INDEX);\n\t\tcolumn.setCellEditor(new DefaultCellEditor(listenToComboBox));\n\t\t\n\t\tcolumn = table.getColumnModel().getColumn(ListenerCollectorTable.MEASURING_INDEX);\n\t\tcolumn.setCellEditor(new DefaultCellEditor(measuringComboBox));\n\t}\n\t\n\t@Override\n\tpublic Functor[] getReadFunctors() {\n\t\n\t\tList<Functor> functors = new ArrayList<>(Arrays.asList(stealFrom.getReadFunctors()));\n\t\t\n\t\tfunctors.add(new Functor(\"getListenTo\"));\n\t\tfunctors.add(new Functor(\"getMeasuring\"));\n\t\t\n\t\treturn functors.toArray(new Functor[functors.size()]);\n\t}\n\n\t@Override\n\tpublic Functor[] getWriteFunctors() {\n\t\tList<Functor> functors = new ArrayList<>(Arrays.asList(stealFrom.getWriteFunctors()));\n\t\t\n\t\tfunctors.add(new Functor(\"setListenTo\"));\n\t\tfunctors.add(new Functor(\"setMeasuring\"));\n\t\t\n\t\treturn functors.toArray(new Functor[functors.size()]);\n\t}\n\n\t@Override\n\tpublic String[] getHeaders() {\n\t\tList<String> headers = new ArrayList<>(Arrays.asList(stealFrom.getHeaders()));\n\t\t\n\t\theaders.add(\"Listen to\");\n\t\theaders.add(\"Measuring\");\n\t\t\n\t\treturn headers.toArray(new String[headers.size()]);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getEditorClasses() {\n\t\tList<Class<?>> editors = new ArrayList<>(Arrays.asList(stealFrom.getEditorClasses()));\n\t\t\n\t\teditors.add(ComboBoxEditor.class);\n\t\teditors.add(ComboBoxEditor.class);\n\t\t\n\t\treturn editors.toArray(new Class<?>[editors.size()]);\n\t}\n\t\n\tpublic static JComboBox<String> measuringBox() {\n\t\tJComboBox<String> box = new JComboBox<String>();\n\t\tfor (Measurable value : Measurable.values()) {\n\t\t\tbox.addItem(value.toString());\n\t\t}\n\t\treturn box;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/gui/PrometheusListenerGui.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed  under the  License is distributed on an \"AS IS\" BASIS,\n * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or\n * implied.\n *\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.github.johrstrom.listener.gui;\n\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.CollectorElement;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.listener.PrometheusListener;\nimport org.apache.jmeter.testelement.TestElement;\nimport org.apache.jmeter.visualizers.gui.AbstractListenerGui;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.awt.*;\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n/**\n * The GUI class for the Prometheus Listener.\n * <p>\n * Currently, all configurations are done through properties files so this class\n * shows nothing visually other than comments.\n *\n * @author Jeff Ohrstrom\n */\npublic class PrometheusListenerGui extends AbstractListenerGui {\n\n\n\tprivate static final long serialVersionUID = 4984653136457108054L;\n\n\tprivate ListenerCollectorTable table = new ListenerCollectorTable();\n\tprivate Logger log = LoggerFactory.getLogger(PrometheusListenerGui.class);\n\n\tpublic PrometheusListenerGui() {\n\t\tsuper();\n\t\tlog.debug(\"making a new listener gui: {}\", this.toString());\n\t\tinit();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.gui.JMeterGUIComponent#getLabelResource()\n\t */\n\t@Override\n\tpublic String getLabelResource() {\n\t\treturn getClass().getCanonicalName();\n\t}\n\n\n\t@Override\n\tprotected PrometheusListenerGui clone() throws CloneNotSupportedException {\n\t\treturn new PrometheusListenerGui();\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.gui.AbstractJMeterGuiComponent#getStaticLabel()\n\t */\n\t@Override\n\tpublic String getStaticLabel() {\n\t\treturn \"Prometheus Listener\";\n\t}\n\n\t/*\n\t * (non-Javadoc)\n\t *\n\t * @see org.apache.jmeter.gui.AbstractJMeterGuiComponent#getName()\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn super.getName() == null ? this.getStaticLabel() : super.getName();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void configure(TestElement ele) {\n\t\tsuper.configure(ele);\n\t\tif (ele instanceof CollectorElement<?>) {\n\t\t\ttry {\n\t\t\t\tthis.table.populateTable((CollectorElement<ListenerCollectorConfig>) ele);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.error(\"didn't modify test element because {}. {}\", e.getClass(), e.getMessage());\n\t\t\t}\n\t\t}\n\n\t\t//ele.getName() == null ? this.setName(ele.getName()) : this.setName(getStaticLabel());;\n\t\tthis.setName(ele.getName() == null ? getStaticLabel() : ele.getName());\n\t\tthis.setComment(ele.getComment() == null ? \"\" : ele.getComment());\n\t}\n\n\t@Override\n\tpublic TestElement createTestElement() {\n\t\tPrometheusListener listener = new PrometheusListener();\n\n\t\tlistener.setProperty(TestElement.GUI_CLASS, PrometheusListenerGui.class.getName());\n\t\tlistener.setProperty(TestElement.TEST_CLASS, PrometheusListener.class.getName());\n\t\tthis.modifyTestElement(listener);\n\t\tlistener.setCollectorConfigs(defaultCollectors());\n\t\treturn listener;\n\t}\n\n\tprivate void init() {\n\t\tthis.setLayout(new BorderLayout(0, 5));\n\t\tthis.add(makeTitlePanel(), BorderLayout.NORTH);\n\t\tthis.add(this.table, BorderLayout.CENTER);\n\t}\n\n\t@Override\n\tpublic void modifyTestElement(TestElement ele) {\n\t\tif (!(ele instanceof CollectorElement)) {\n\t\t\treturn;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tCollectorElement<ListenerCollectorConfig> config = (CollectorElement<ListenerCollectorConfig>) ele;\n\t\tList<ListenerCollectorConfig> collectors = this.table.getRowsAsCollectors();\n\t\tconfig.setCollectorConfigs(collectors);\n\n\t\tconfig.setName(this.getName());\n\t\tconfig.setComment(this.getComment());\n\t}\n\n\t@Override\n\tpublic void clearGui() {\n\t\tsuper.clearGui();\n\t\tthis.table.clearModelData();\n\t}\n\n\n\tprivate List<ListenerCollectorConfig> defaultCollectors() {\n\t\tList<ListenerCollectorConfig> collectors = new ArrayList<>();\n\t\tcollectors.add(buildResponseTimeCollector());\n\t\tcollectors.add(buildSuccessRatioCollector());\n\t\treturn collectors;\n\t}\n\n\tprivate ListenerCollectorConfig buildSuccessRatioCollector() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"Ratio\");\n\t\tcfg.setHelp(\"Success and failure ratio\");\n\t\tcfg.setLabels(\"label,code\");\n\t\tcfg.setType(BaseCollectorConfig.JMeterCollectorType.SUCCESS_RATIO.toString());\n\t\tListenerCollectorConfig listenerCfg = new ListenerCollectorConfig(cfg);\n\t\tlistenerCfg.setListenTo(ListenerCollectorConfig.SAMPLES.toString());\n\t\tlistenerCfg.setMeasuring(ListenerCollectorConfig.Measurable.SuccessRatio.toString());\n\t\treturn listenerCfg;\n\t}\n\n\tprivate ListenerCollectorConfig buildResponseTimeCollector() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"ResponseTime\");\n\t\tcfg.setHelp(\"Sampler Response Time\");\n\t\tcfg.setLabels(\"label,code\");\n\t\tcfg.setType(BaseCollectorConfig.JMeterCollectorType.SUMMARY.toString());\n\t\tcfg.setQuantileOrBucket(\"0.75,0.5|0.95,0.1|0.99,0.01;60\");\n\t\tListenerCollectorConfig listenerCfg = new ListenerCollectorConfig(cfg);\n\t\tlistenerCfg.setListenTo(ListenerCollectorConfig.SAMPLES.toString());\n\t\tlistenerCfg.setMeasuring(ListenerCollectorConfig.Measurable.ResponseTime.toString());\n\t\treturn listenerCfg;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/updater/AbstractUpdater.java",
    "content": "package com.github.johrstrom.listener.updater;\n\nimport com.github.johrstrom.collector.JMeterCollectorRegistry;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport org.apache.jmeter.assertions.AssertionResult;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.threads.JMeterContextService;\nimport org.apache.jmeter.threads.JMeterVariables;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * The Updater family of classes are meant to update the actual Collectors given the configuration. The main problem\n * it tries to solve is tying a Prometheus Collector (with a type like 'Historgram', labels, etc.) to the JMeter data\n * that collector is measuring.\n * \n * Note: This class assumes that the Collector object passed into the constructor is valid. I.e., it is not null and \n * registered. Of course, being null has much more serious consequences. \n * \n * @author Jeff Ohrstrom\n *\n */\npublic abstract class AbstractUpdater {\n\t\n\tpublic static String NULL = \"null\";\n\n\tprotected ListenerCollectorConfig config;\n\tprotected static final JMeterCollectorRegistry registry = JMeterCollectorRegistry.getInstance();\n\t\n\t// helper lookup table for sample variables, so we don't loop over arrays every update.\n\tprivate Map<String,Integer> varIndexLookup;\n\n\t/**\n\t * All subclasses should have this and only this constructor signature. \n\t *\n\t * @param cfg the configuration of the collector\n\t */\n\tpublic AbstractUpdater(ListenerCollectorConfig cfg) {\n\t\t//this.collector = c;\n\t\tthis.config = cfg;\n\t\tthis.buildVarLookup();\n\t}\n\n\t\n\t/**\n\t * Updates the collector it was instantiated with with the given event e. \n\t * \n\t * @param e\n\t */\n\tpublic abstract void update(SampleEvent e);\n\t\n\tpublic static class AssertionContext {\n\t\tpublic AssertionResult assertion;\n\t\tpublic SampleEvent event;\n\t\t\n\t\tpublic AssertionContext(AssertionResult a, SampleEvent e) {\n\t\t\tthis.assertion = a;\n\t\t\tthis.event = e;\n\t\t}\n\t\t\n\t}\n\n\t/**\n\t * Helper function to extract the label values from the Sample Event. Values\n\t * depend on how the Updater was configured. \n\t * \n\t * @param event\n\t * @return the label values.\n\t */\n\tprotected String[] labelValues(SampleEvent event) {\n\t\tString[] labels = config.getLabels();\n\t\tString[] values =  new String[labels.length];\n\t\tJMeterVariables vars = JMeterContextService.getContext().getVariables();\n\t\t\n\t\tfor (int i = 0; i < labels.length; i++) {\n\t\t\tString name = labels[i];\n\t\t\tString value = null;\n\t\t\t\n\t\t\t// reserved keywords for the sampler's label name.\n\t\t\tif (name.equalsIgnoreCase(\"label\")) {\n\t\t\t\tvalue = event.getResult().getSampleLabel();\n\t\t\t} else if (name.equalsIgnoreCase(\"code\")) {\n\t\t\t\t// code also reserved\n\t\t\t\tvalue = event.getResult().getResponseCode();\n\t\t\t} else if (name.equalsIgnoreCase(\"thread_group\")) {\n\t\t\t\tvalue = event.getThreadGroup();\n\t\t\t\t\n\t\t\t// try to find it as a plain'ol variable.\n\t\t\t} else if (this.varIndexLookup.get(name) != null) {\n\t\t\t\tint idx = this.varIndexLookup.get(name);\n\t\t\t\tvalue = event.getVarValue(idx);\n\t\t\t\n\t\t\t// lastly look in sample_variables\n\t\t\t} else if (vars != null) {\n\t\t\t\tvalue = vars.get(name);\n\t\t\t}\n\t\t\t\n\t\t\tvalues[i] = (value == null || value.isEmpty()) ? NULL : value;\n\t\t}\n\t\t\n\t\treturn values;\n\t}\n\t\n\tprotected String[] labelValues(AssertionContext ctx) {\n\t\tString[] labels = config.getLabels();\n\t\tString[] values =  new String[labels.length];\n\t\tJMeterVariables vars = JMeterContextService.getContext().getVariables();\n\t\t\n\t\tfor(int i = 0; i < labels.length; i++) {\n\t\t\tString name = labels[i];\n\t\t\tString value = null;\n\t\t\t\n\t\t\tif(name.equalsIgnoreCase(\"label\")) {\n\t\t\t\tvalue = ctx.assertion.getName();\n\t\t\t\t\n\t\t\t// try to find it as a plain'ol variable.\n\t\t\t} else if (this.varIndexLookup.get(name) != null){\n\t\t\t\tint idx = this.varIndexLookup.get(name);\n\t\t\t\tvalue = ctx.event.getVarValue(idx);\n\t\t\t\t\n\t\t\t\n\t\t\t// lastly look in sample_variables\n\t\t\t}else if (vars != null){\n\t\t\t\tvalue = vars.get(name);\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\tvalues[i] = (value == null || value.isEmpty()) ? NULL : value;\n\t\t}\n\t\t\n\t\treturn values;\n\t}\n\t\n\t\n\tprivate void buildVarLookup() {\n\t\tthis.varIndexLookup = new HashMap<String,Integer>();\n\t\t\t\t\n\t\tfor(int i = 0; i < SampleEvent.getVarCount(); i++) {\n\t\t\tString name = SampleEvent.getVarName(i);\n\t\t\tif(inLabels(name)) {\n\t\t\t\tthis.varIndexLookup.put(name, i);\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\t\n\tprivate boolean inLabels(String searchFor) {\n\t\tString[] labels = config.getLabels();\n\t\tfor(int i = 0; i < labels.length; i++) {\n\t\t\tif(labels[i].equalsIgnoreCase(searchFor)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn false;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/updater/AggregatedTypeUpdater.java",
    "content": "package com.github.johrstrom.listener.updater;\n\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport io.prometheus.client.Collector;\nimport io.prometheus.client.Histogram;\nimport io.prometheus.client.Summary;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.samplers.SampleResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class AggregatedTypeUpdater extends AbstractUpdater {\n\t\n\tprivate static final Logger log = LoggerFactory.getLogger(AggregatedTypeUpdater.class);\n\n\tpublic AggregatedTypeUpdater(ListenerCollectorConfig cfg) {\n\t\tsuper(cfg);\n\t}\n\n\t@Override\n\tpublic void update(SampleEvent event) {\n\t\ttry {\n\t\t\tCollector collector = registry.getOrCreateAndRegister(this.config);\n\t\t\t\n\t\t\tString[] labels = this.labelValues(event);\n\t\t\tlong measurement = this.measure(event);\n\t\t\t\n\t\t\tif(collector instanceof Histogram) {\n\t\t\t\tHistogram hist = (Histogram) collector;\n\t\t\t\thist.labels(labels).observe(measurement);\n\t\t\t\t\n\t\t\t}else if(collector instanceof Summary) {\n\t\t\t\tSummary sum = (Summary) collector;\n\t\t\t\tsum.labels(labels).observe(measurement);\n\t\t\t}\n\t\t\t\n\t\t} catch(Exception e) {\n\t\t\tlog.error(\"Did not update {} because of error: {}\", this.config.getMetricName(), e.getMessage());\n\t\t\tlog.debug(e.getMessage(), e);\n\t\t}\n\n\t}\n\t\n\t\n\tprotected long measure(SampleEvent event) {\n\t\tSampleResult result = event.getResult();\n\t\tswitch(this.config.getMeasuringAsEnum()) {\n\t\tcase ResponseSize:\n\t\t\treturn result.getBodySizeAsLong();\n\t\tcase ResponseTime:\n\t\t\treturn result.getTime();\n\t\tcase Latency:\n\t\t\treturn result.getLatency();\n\t\tcase IdleTime:\n\t\t\treturn result.getIdleTime();\n\t\tcase ConnectTime:\n\t\t\treturn result.getConnectTime();\n\t\tdefault:\n\t\t\treturn 0;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/github/johrstrom/listener/updater/CountTypeUpdater.java",
    "content": "package com.github.johrstrom.listener.updater;\n\nimport com.github.johrstrom.collector.SuccessRatioCollector;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport io.prometheus.client.Collector;\nimport io.prometheus.client.Counter;\nimport org.apache.jmeter.assertions.AssertionResult;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This is the AbstractUpdater sub-type that can handle updating any kind of Counter metrics \n * along with {@link SuccessRatioCollector} type.\n * \n * @author Jeff ohrstrom\n *\n */\npublic class CountTypeUpdater extends AbstractUpdater {\n\t\n\tprivate static final Logger log = LoggerFactory.getLogger(CountTypeUpdater.class);\n\n\tpublic CountTypeUpdater(ListenerCollectorConfig cfg) {\n\t\tsuper(cfg);\n\t}\n\t\n\t@Override\n\tpublic void update(SampleEvent event) {\n\t\tif(this.config.listenToSamples()) {\n\t\t\tboolean successful = event.getResult().isSuccessful();\n\t\t\tthis.inc(this.labelValues(event), successful);\n\t\t\t\n\t\t} else if(this.config.listenToAssertions()) {\n\t\t\tfor(AssertionResult assertion : event.getResult().getAssertionResults()) {\n\t\t\t\tupdateAssertions(new AssertionContext(assertion, event));\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\n\t\n\tprotected void inc(String[] labels, boolean successful) {\n\t\ttry {\n\t\t\tCollector collector = registry.getOrCreateAndRegister(this.config);\n\t\t\t\t\t\t\n\t\t\tif(collector instanceof Counter) {\n\t\t\t\tCounter c = (Counter) collector;\n\t\t\t\t\n\t\t\t\tswitch (config.getMeasuringAsEnum()) {\n\t\t\t\tcase CountTotal: \n\t\t\t\t\tc.labels(labels).inc();\n\t\t\t\t\tbreak;\n\t\t\t\tcase FailureTotal:\n\t\t\t\t\tif(!successful) {\n\t\t\t\t\t\tc.labels(labels).inc();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SuccessTotal:\n\t\t\t\t\tif(successful) {\n\t\t\t\t\t\tc.labels(labels).inc();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else if(collector instanceof SuccessRatioCollector) {\n\t\t\t\tSuccessRatioCollector c = (SuccessRatioCollector) collector;\n\t\t\t\tif(successful) {\n\t\t\t\t\tc.incrementSuccess(labels);\n\t\t\t\t} else {\n\t\t\t\t\tc.incrementFailure(labels);\n\t\t\t\t}\n\t\t\t}\t\t\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tlog.error(\"Did not update {} because of error: {}\", this.config.getMetricName(), e.getMessage());\n\t\t\tlog.debug(e.getMessage(), e);\n\t\t}\n\t}\n\t\n\tprotected void updateAssertions(AssertionContext ctx) {\n\t\tString[] labels = this.labelValues(ctx);\n\t\tboolean successful = !ctx.assertion.isFailure();\n\t\t\n\t\tthis.inc(labels, successful);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/collector/BaseCollectorConfigTest.java",
    "content": "package com.github.johrstrom.collector;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig.JMeterCollectorType;\nimport com.github.johrstrom.collector.BaseCollectorConfig.QuantileDefinition;\nimport com.github.johrstrom.test.TestUtilities;\nimport io.prometheus.client.*;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.regex.Pattern;\n\n//import io.prometheus.client.Collector.Type;\n\n\npublic class BaseCollectorConfigTest {\n\t\n\t@Test\n\tpublic void emptyLabelsOK() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleCounterCfg();\n\t\t\n\t\t// first try with a brand new String array \n\t\tcfg.setLabels(new String[]{});  \n\t\tCollector collector = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertNotNull(collector);\n\t\t\n\t\t\n\t\t// Now try with a String array with an empty string in it\n\t\tcfg.setLabels(new String[]{\"\"});\n\t\tcollector = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertNotNull(collector);\n\t\t\n\t\t// Now just for kicks, try with several empty strings in it\n\t\tcfg.setLabels(new String[]{\"a\", \"\", \"b\", \"\"});\n\t\tcollector = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertNotNull(collector);\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void parseSingleQuantilesCorrectly() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleSummaryCfg();\n\t\tcfg.setQuantileOrBucket(\"0.95,0.1\");\n\t\t\n\t\t\n\t\tQuantileDefinition[] quantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(1, quantiles.length);\n\t\tAssert.assertEquals(0.95, quantiles[0].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[0].error,0.001);\n\t}\n\t\n\t@Test\n\tpublic void parseMultipleQuantilesCorrectly() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleSummaryCfg();\n\t\tcfg.setQuantileOrBucket(\"0.95,0.1|0.99,0.1|0.999,0.1\");\n\t\t\n\t\t\n\t\tQuantileDefinition[] quantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(3, quantiles.length);\n\t\tAssert.assertEquals(0.95, quantiles[0].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[0].error,0.001);\n\t\tAssert.assertEquals(0.99, quantiles[1].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[1].error,0.001);\n\t\tAssert.assertEquals(0.999, quantiles[2].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[2].error,0.001);\n\t}\n\n  @Test\n\tpublic void parseMultipleQuantilesWithWindowCorrectly() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleSummaryCfg();\n\t\tcfg.setQuantileOrBucket(\"0.95,0.1|0.99,0.1|0.999,0.1;60\");\n\n\n\t\tQuantileDefinition[] quantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(3, quantiles.length);\n\t\tAssert.assertEquals(0.95, quantiles[0].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[0].error,0.001);\n\t\tAssert.assertEquals(0.99, quantiles[1].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[1].error,0.001);\n\t\tAssert.assertEquals(0.999, quantiles[2].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[2].error,0.001);\n\n    Assert.assertEquals(60, cfg.getQuantileWindowLength());\n\t}\n\t\n\t@Test\n\tpublic void parseQauntileFailsAndGivesDEFAULTs() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleSummaryCfg();\n\t\tcfg.setQuantileOrBucket(\"skdn fsdfu|,nsf\");\n\t\t\n\t\tQuantileDefinition[] quantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(3, quantiles.length);\n\t\tAssert.assertArrayEquals(BaseCollectorConfig.DEFAULT_QUANTILES, quantiles);\n\t\t\n\t\tcfg.setQuantileOrBucket(\";sdfg|other_str\ting|asl dfuy\");\n\t\tquantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(3, quantiles.length);\n\t\tAssert.assertArrayEquals(BaseCollectorConfig.DEFAULT_QUANTILES, quantiles);\n\t}\n\t\n\t@Test\n\tpublic void parseReturnsPartialForQuantiles() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleSummaryCfg();\n\t\tcfg.setQuantileOrBucket(\"skdnfs dfuns\tf|0.5|0.75,0.1\"); //only 1 good at the end\n\t\t\n\t\tQuantileDefinition[] quantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(1, quantiles.length);\n\t\tAssert.assertEquals(0.75, quantiles[0].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[0].error,0.001);\n\t\t\n\t\tcfg.setQuantileOrBucket(\"skdnfsdfunsf|0.5,0.1|0.75,0.1|0.99\"); //2 in middle are good\n\t\tquantiles = cfg.getQuantiles();\n\t\tAssert.assertEquals(2, quantiles.length);\n\t\tAssert.assertEquals(0.5, quantiles[0].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[0].error,0.001);\n\t\tAssert.assertEquals(0.75, quantiles[1].quantile,0.001);\n\t\tAssert.assertEquals(0.1, quantiles[1].error,0.001);\n\t}\n\t\n\t@Test\n\tpublic void parseBucketsCorrectly() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleHistogramCfg();\n\t\tcfg.setQuantileOrBucket(\"100,500,1000,2500,5000\");\n\t\tdouble[] expected = new double[] {100, 500, 1000, 2500, 5000};\n\t\t\n\t\tdouble[] buckets = cfg.getBuckets();\n\t\tAssert.assertEquals(5, buckets.length);\n\t\tAssert.assertArrayEquals(expected, buckets,0.01);\n\t}\n\t\n\t@Test\n\tpublic void parseBucketFailureReturnsDefaults() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleHistogramCfg();\n\t\tcfg.setQuantileOrBucket(\"akldjand| sfpoa\tsdnf\");\n\t\t\n\t\tdouble[] buckets = cfg.getBuckets();\n\t\tAssert.assertEquals(4, buckets.length);\n\t\tAssert.assertArrayEquals(BaseCollectorConfig.DEFAULT_BUCKET_SIZES, buckets,0.01);\n\t\t\n\t\tcfg.setQuantileOrBucket(\"fail,otherFi al,123cantPa\trse,not123 ThisEither,a3\");\n\t\tbuckets = cfg.getBuckets();\n\t\tAssert.assertEquals(4, buckets.length);\n\t\tAssert.assertArrayEquals(BaseCollectorConfig.DEFAULT_BUCKET_SIZES, buckets,0.01);\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void parseBucketsWithPartialSuccess() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleHistogramCfg();\n\t\tcfg.setQuantileOrBucket(\"akldjand,17,lakj\tasdf,123no,48\");\n\t\t\n\t\tdouble[] buckets = cfg.getBuckets();\n\t\tAssert.assertEquals(2, buckets.length);\n\t\tAssert.assertEquals(17, buckets[0], 0.001);\n\t\tAssert.assertEquals(48, buckets[1], 0.001);\n\t}\n\t\n\t\n\t@Test\n\tpublic void initCorrectly() {\n\t\tBaseCollectorConfig init = new BaseCollectorConfig();\n\t\t\n\t\tAssert.assertEquals(init.getHelp(), BaseCollectorConfig.DEFAULT_HELP_STRING);\n\t\tAssert.assertArrayEquals(new String[0], init.getLabels());\n\t\tAssert.assertTrue(init.getLabelsAsString().isEmpty());\n\t\tAssert.assertEquals(init.getType(), JMeterCollectorType.COUNTER.toString());\n\t\t\n\t\tAssert.assertTrue(\n\t\t\tinit.getMetricName() + \" does not match the expected pattern.\",\n\t\t\tPattern.matches(BaseCollectorConfig.METRIC_NAME_BASE + \"\\\\p{Alnum}{8}\", \n\t\t\tinit.getMetricName()));\n\t\t\n\t}\n\n\n\t@Test\n\tpublic void createCorrectType() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleCounterCfg();\n\t\tCollector c = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertTrue(c instanceof Counter);\n\t\t\n\t\tcfg = TestUtilities.simpleGaugeCfg();\n\t\tc = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertTrue(c instanceof Gauge);\n\t\t\n\t\tcfg = TestUtilities.simpleHistogramCfg();\n\t\tc = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertTrue(c instanceof Histogram);\n\t\t\n\t\tcfg = TestUtilities.simpleSummaryCfg();\n\t\tc = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertTrue(c instanceof Summary);\n\t\t\n\n\t\tcfg = TestUtilities.simpleSuccessRatioCfg();\n\t\tc = BaseCollectorConfig.fromConfig(cfg);\n\t\tAssert.assertTrue(c instanceof SuccessRatioCollector);\n\t}\n\t\n\t@Test\n\tpublic void setOfElementsTest() {\n\t\tBaseCollectorConfig left = TestUtilities.simpleCounterCfg();\n\t\tBaseCollectorConfig right = TestUtilities.simpleCounterCfg();\n\n\t\tAssert.assertNotSame(left, right);\n\t\tAssert.assertEquals(left, right);\n\t\t\n\t\tint leftHash = left.hashCode();\n\t\tint rightHash = right.hashCode();\n\n\t\tAssert.assertEquals(leftHash, rightHash);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/collector/JMeterCollectorRegistryTest.java",
    "content": "package com.github.johrstrom.collector;\n\nimport com.github.johrstrom.test.TestUtilities;\nimport io.prometheus.client.Collector;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class JMeterCollectorRegistryTest {\n\t\n\tprivate JMeterCollectorRegistry registry = JMeterCollectorRegistry.getInstance();\n\n\t@Test\n\tpublic void safelyGetOrCreate() {\n\t\tBaseCollectorConfig cfg = TestUtilities.simpleHistogramCfg();\n\t\tcfg.setMetricName(\"register_tester\");\n\t\t\n\t\tCollector c1 = registry.getOrCreateAndRegister(cfg);\n\t\tCollector c2 = registry.getOrCreateAndRegister(cfg);\n\n\t\tAssert.assertSame(c1, c2);\n\t\tAssert.assertEquals(c1, c2);\n\t\t\n\t\tregistry.unregister(cfg);\n\t\tregistry.unregister(cfg);\n\t\tregistry.unregister(cfg);\n\t\t\n\t\tCollector c3 = registry.getOrCreateAndRegister(cfg);\n\t\t\n\t\tAssert.assertTrue(c3 != c1 && c3 != c2);\n\t\tAssert.assertTrue(!c3.equals(c1) && !c3.equals(c2));\n\t\t\n\t\tregistry.unregister(cfg);\n\t\tregistry.unregister(cfg);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/collector/SuccessRatioCollectorTest.java",
    "content": "package com.github.johrstrom.collector;\n\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.test.TestUtilities;\nimport io.prometheus.client.Collector.MetricFamilySamples;\nimport io.prometheus.client.Collector.MetricFamilySamples.Sample;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.List;\n\npublic class SuccessRatioCollectorTest {\n\t\n\tprivate static final JMeterCollectorRegistry reg = JMeterCollectorRegistry.getInstance();\n\t\n\tprivate final String[] labelNames = new String[] {\"foo_label\",\"label\"};\n\tprivate final String[] labelValues = new String[] {\"bar_value\", \"myLabelz\"};\n\t\n\t@Test\n\tpublic void testSuccess() {\n\n\t\tBaseCollectorConfig base = TestUtilities.simpleSuccessRatioCfg();\n\t\t\n\t\tbase.setLabels(labelNames);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tString baseName = \"something_ratio\";\n\t\tcfg.setMetricName(baseName);\n\t\t\n\t\tSuccessRatioCollector c = (SuccessRatioCollector) reg.getOrCreateAndRegister(cfg);\n\t\t\n\t\t\n\t\tc.incrementSuccess(labelValues);\n\t\t\n\t\tList<MetricFamilySamples> families = c.collect();\n\t\tboolean foundSuccess, foundFailure, foundTotal;\n\t\tfoundSuccess = foundFailure = foundTotal = false;\n\t\t\n\t\tfor(MetricFamilySamples family : families) {\n\t\t\tswitch (family.name) {\n\t\t\t\tcase \"something_ratio_success\":\n\t\t\t\t\tassertOnSingleFamily(family, 1);\n\t\t\t\t\tfoundSuccess = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"something_ratio_failure\":\n\t\t\t\t\tassertOnSingleFamily(family, 0);\n\t\t\t\t\tfoundFailure = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"something_ratio\":\n\t\t\t\t\tassertOnSingleFamily(family, 1);\n\t\t\t\t\tfoundTotal = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tAssert.fail(family.name + \" is not an expected metric family name\");\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tAssert.assertTrue(foundSuccess && foundFailure && foundTotal);\n\t}\n\t\n\t@Test\n\tpublic void testFailure() {\n\n\t\tBaseCollectorConfig base = TestUtilities.simpleSuccessRatioCfg();\n\t\t\n\t\tbase.setLabels(labelNames);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tString baseName = \"otherthing_ratio\";\n\t\tcfg.setMetricName(baseName);\n\t\t\n\t\tSuccessRatioCollector c = (SuccessRatioCollector) reg.getOrCreateAndRegister(cfg);\n\t\t\n\t\t\n\t\tc.incrementFailure(labelValues);\t//only real diff with test above\n\t\t\n\t\tList<MetricFamilySamples> families = c.collect();\n\t\tboolean foundSuccess, foundFailure, foundTotal;\n\t\tfoundSuccess = foundFailure = foundTotal = false;\n\t\t\n\t\tfor(MetricFamilySamples family : families) {\n\t\t\tswitch (family.name) {\n\t\t\t\tcase \"otherthing_ratio_success\":\n\t\t\t\t\tassertOnSingleFamily(family, 0);\n\t\t\t\t\tfoundSuccess = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"otherthing_ratio_failure\":\n\t\t\t\t\tassertOnSingleFamily(family, 1);\n\t\t\t\t\tfoundFailure = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"otherthing_ratio\":\n\t\t\t\t\tassertOnSingleFamily(family, 1);\n\t\t\t\t\tfoundTotal = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tAssert.fail(family.name + \" is not an expected metric family name\");\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t\n\t\tAssert.assertTrue(foundSuccess && foundFailure && foundTotal);\n\t}\n\t\n\tprivate void assertOnSingleFamily(MetricFamilySamples family, double expectedValue) {\n\t\tAssert.assertEquals(2, family.samples.size());\n\t\tSample sample = family.samples.get(0);\n\t\t\n\t\tAssert.assertArrayEquals(labelValues, sample.labelValues.toArray());\n\t\tAssert.assertArrayEquals(labelNames, sample.labelNames.toArray());\n\t\tAssert.assertEquals(expectedValue, sample.value, 0.1);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/config/PrometheusMetricsConfigTest.java",
    "content": "package com.github.johrstrom.config;\n\nimport com.github.johrstrom.test.TestUtilities;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\n\n\npublic class PrometheusMetricsConfigTest {\n\t\n\t@Test\n\tpublic void listenerIsSerializable() throws IOException {\n\t\tByteArrayOutputStream objectBuffer = new ByteArrayOutputStream();\n\t\tObjectOutputStream out = new ObjectOutputStream(objectBuffer);\n\t\t\n\t\tPrometheusMetricsConfig cfg = new PrometheusMetricsConfig();\n\t\tcfg.setCollectorConfigs(TestUtilities.simpleListConfig());\n\t\t\n\t\tout.writeObject(cfg);\n\n\t\tAssert.assertNotNull(cfg);\n\t\tAssert.assertTrue(objectBuffer.size() > 0);\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/config/gui/ConfigGuiTest.java",
    "content": "package com.github.johrstrom.config.gui;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport org.apache.jmeter.util.JMeterUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Locale;\n\n\n\npublic class ConfigGuiTest {\n\n\t@Test\n\tpublic void simpleTest() {\n\t\tJMeterUtils.setLocale(Locale.ENGLISH);\n\t\t\n\t\tPrometheusMetricsConfigGui<BaseCollectorConfig> gui = new PrometheusMetricsConfigGui<>();\n\t\t\n\t\tString comment = \"this should be the comment\";\n\t\tString name = \"simple cfg name\";\n\t\t\n\t\tgui.setName(name);\n\t\tgui.setComment(comment);\n\t\t\n\t\tAssert.assertEquals(name, gui.getName());\n\t\tAssert.assertEquals(comment, gui.getComment());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/ListenerCollectorConfigTest.java",
    "content": "package com.github.johrstrom.listener;\n\nimport com.github.johrstrom.test.TestUtilities;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class ListenerCollectorConfigTest {\n\n\t@Test\n\tpublic void setOfElementsTest() {\n\t\tListenerCollectorConfig left = new ListenerCollectorConfig(TestUtilities.simpleCounterCfg());\n\t\tListenerCollectorConfig right = new ListenerCollectorConfig(TestUtilities.simpleCounterCfg());\n\n\t\tAssert.assertNotSame(left, right);\n\t\tAssert.assertEquals(left, right);\n\t\t\n\t\tint leftHash = left.hashCode();\n\t\tint rightHash = right.hashCode();\n\n\t\tAssert.assertEquals(leftHash, rightHash);\n\t}\n\t\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/PrometheusListenerTest.java",
    "content": "package com.github.johrstrom.listener;\n\nimport com.github.johrstrom.collector.JMeterCollectorRegistry;\nimport com.github.johrstrom.collector.SuccessRatioCollector;\nimport com.github.johrstrom.test.TestUtilities;\nimport com.github.johrstrom.test.TestUtilities.ResultAndVariables;\nimport io.prometheus.client.Collector;\nimport io.prometheus.client.Collector.MetricFamilySamples;\nimport io.prometheus.client.Collector.MetricFamilySamples.Sample;\nimport io.prometheus.client.Counter;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.save.SaveService;\nimport org.apache.jorphan.collections.HashTree;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.lang.reflect.Field;\nimport java.util.List;\n\npublic class PrometheusListenerTest {\n\n\tprivate static final JMeterCollectorRegistry reg = JMeterCollectorRegistry.getInstance();\n\n\tstatic {\n\t\tTestUtilities.createJmeterEnv();\n\t}\n\n\t@Test\n\tpublic void listenerIsSerializable() throws IOException {\n\t\tByteArrayOutputStream objectBuffer = new ByteArrayOutputStream();\n\t\tObjectOutputStream out = new ObjectOutputStream(objectBuffer);\n\n\t\tPrometheusListener listener = new PrometheusListener();\n\t\tout.writeObject(listener);\n\n\t\tAssert.assertNotNull(listener);\n\t\tAssert.assertTrue(objectBuffer.size() > 0);\n\n\t}\n\n\t@Test\n\tpublic void canHaveDuplicateMetrics() {\n\t\tPrometheusListener first = new PrometheusListener();\n\t\tPrometheusListener second = new PrometheusListener();\n\n\t\tfirst.setCollectorConfigs(TestUtilities.simpleListListener());\n\t\tsecond.setCollectorConfigs(TestUtilities.simpleListListener());\n\n\t\tfirst = (PrometheusListener) first.clone();\n\t\tsecond = (PrometheusListener) second.clone();\n\n\t\tfirst.testStarted();\n\t\tsecond.testStarted();\n\n\t\tfirst.testEnded();\n\t\tsecond.testEnded();\n\n\t}\n\n\t@Test\n\tpublic void lazyInizializationPrometheusServer() throws NoSuchFieldException, IllegalAccessException {\n\n\t\tPrometheusListener listener = new PrometheusListener();\n\t\t// Remove the server to test lazy initialization on test start\n\t\tField prometheusServerField = listener.getClass().getDeclaredField(\"server\");\n\t\tprometheusServerField.setAccessible(true);\n\t\tprometheusServerField.set(listener, null);\n\t\tlistener.setCollectorConfigs(TestUtilities.simpleListListener());\n\t\tlistener.testStarted();\n\t\tAssert.assertNotNull(prometheusServerField.get(listener));\n\t}\n\n\t@Test\n\tpublic void lazyInizializationRegistry() throws NoSuchFieldException, IllegalAccessException {\n\n\t\tPrometheusListener listener = new PrometheusListener();\n\t\t// Remove the server to test lazy initialization on test start\n\t\tField registryField = listener.getClass().getSuperclass().getDeclaredField(\"registry\");\n\t\tregistryField.setAccessible(true);\n\t\tregistryField.set(listener, null);\n\t\tlistener.setCollectorConfigs(TestUtilities.simpleListListener());\n\t\tlistener.testStarted();\n\t\tAssert.assertNotNull(registryField.get(listener));\n\t}\n\n\t@Test\n\tpublic void canReadJMX() throws IOException {\n\t\tFile jmx = new File(\"target/test-classes/simple_prometheus_example.jmx\");\n\t\tHashTree tree = SaveService.loadTree(jmx);\n\n\t\tAssert.assertNotNull(tree);\n\t}\n\n\t@Test\n\tpublic void updateAllTypes() {\n\t\tPrometheusListener listener = new PrometheusListener();\n\t\tList<ListenerCollectorConfig> configs = TestUtilities.fullListListener();\n\n\t\tfor (ListenerCollectorConfig cfg : configs) {\n\t\t\tcfg.setLabels(TestUtilities.TEST_LABELS);\n\t\t}\n\n\t\tlistener.setCollectorConfigs(configs);\n\t\tlistener.testStarted();\n\n\t\tlong connectTime = 3123;\n\t\tlong idleTime = 1233;\n\t\tlong elapsedTime = 2213;\n\t\tlong latency = 1532;\n\t\tint responseSize = 1342;\n\t\tint samplesOccurred = 0;\n\t\tdouble expectedCreated = System.currentTimeMillis() / 1000.0;\n\n\t\tResultAndVariables res = TestUtilities.resultWithLabels();\n\t\tres.result.setConnectTime(connectTime);\n\t\tres.result.setResponseData(new byte[responseSize]);\n\t\tres.result.setLatency(latency);\n\t\tres.result.setIdleTime(idleTime);\n\t\tres.result.setStampAndTime(System.currentTimeMillis(), elapsedTime);\n\t\tres.result.setSuccessful(true);\n\t\tSampleEvent event = new SampleEvent(res.result, \"tg1\", res.vars);\n\t\tlistener.sampleOccurred(event); // 1st event, successful\n\t\tsamplesOccurred++;\n\n\t\tres.result.setSuccessful(false); // 2nd event, failure\n\t\tevent = new SampleEvent(res.result, \"tg1\", res.vars);\n\t\tlistener.sampleOccurred(event);\n\t\tsamplesOccurred++;\n\n\t\tfor (ListenerCollectorConfig cfg : configs) {\n\t\t\tString name = cfg.getMetricName();\n\t\t\tswitch (name) {\n\n\t\t\tcase \"test_count_total\":\n\t\t\t\tCounter counter = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\t\t\tdouble shouldBeTwo = counter.labels(TestUtilities.EXPECTED_LABELS).get();\n\t\t\t\tAssert.assertEquals(2.0, shouldBeTwo, 0.1);\n\t\t\t\tbreak;\n\t\t\tcase \"test_failure_total\":\n\t\t\t\tcounter = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\t\t\tdouble shouldBeOne = counter.labels(TestUtilities.EXPECTED_LABELS).get();\n\t\t\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\t\tbreak;\n\t\t\tcase \"test_success_total\":\n\t\t\t\tcounter = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\t\t\tshouldBeOne = counter.labels(TestUtilities.EXPECTED_LABELS).get();\n\t\t\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\t\tbreak;\n\t\t\tcase \"test_ratio\":\n\t\t\t\tassertOnRatio(cfg);\n\t\t\t\tbreak;\n\n\t\t\t// histograms\n\t\t\tcase \"test_hist_rtime\":\n\t\t\t\tassertOnHistogram(reg.getOrCreateAndRegister(cfg), elapsedTime * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\telapsedTime);\n\t\t\t\tbreak;\n\t\t\tcase \"test_hist_rsize\":\n\t\t\t\tassertOnHistogram(reg.getOrCreateAndRegister(cfg), responseSize * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\tresponseSize);\n\t\t\t\tbreak;\n\t\t\tcase \"test_hist_latency\":\n\t\t\t\tassertOnHistogram(reg.getOrCreateAndRegister(cfg), latency * samplesOccurred, samplesOccurred, expectedCreated, latency);\n\t\t\t\tbreak;\n\t\t\tcase \"test_hist_idle_time\":\n\t\t\t\tassertOnHistogram(reg.getOrCreateAndRegister(cfg), idleTime * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\tidleTime);\n\t\t\t\tbreak;\n\t\t\tcase \"test_hist_connect_time\":\n\t\t\t\tassertOnHistogram(reg.getOrCreateAndRegister(cfg), connectTime * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\tconnectTime);\n\t\t\t\tbreak;\n\n\t\t\t// summaries\n\t\t\tcase \"test_summary_rtime\":\n\t\t\t\tassertOnSummary(reg.getOrCreateAndRegister(cfg), elapsedTime * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\telapsedTime);\n\t\t\t\tbreak;\n\t\t\tcase \"test_summary_rsize\":\n\t\t\t\tassertOnSummary(reg.getOrCreateAndRegister(cfg), responseSize * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\tresponseSize);\n\t\t\t\tbreak;\n\t\t\tcase \"test_summary_latency\":\n\t\t\t\tassertOnSummary(reg.getOrCreateAndRegister(cfg), latency * samplesOccurred, samplesOccurred, expectedCreated, latency);\n\t\t\t\tbreak;\n\t\t\tcase \"test_summary_idle_time\":\n\t\t\t\tassertOnSummary(reg.getOrCreateAndRegister(cfg), idleTime * samplesOccurred, samplesOccurred, expectedCreated, idleTime);\n\t\t\t\tbreak;\n\t\t\tcase \"test_summary_connect_time\":\n\t\t\t\tassertOnSummary(reg.getOrCreateAndRegister(cfg), connectTime * samplesOccurred, samplesOccurred, expectedCreated,\n\t\t\t\t\t\tconnectTime);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tAssert.fail(name + \" triggered untested switch case\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlistener.testEnded();\n\t}\n\n\tprivate void assertOnRatio(ListenerCollectorConfig cfg) {\n\t\tSuccessRatioCollector ratio = (SuccessRatioCollector) reg.getOrCreateAndRegister(cfg);\n\t\tdouble shouldBeOne = ratio.getSuccess(TestUtilities.EXPECTED_LABELS);\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\n\t\tshouldBeOne = ratio.getFailure(TestUtilities.EXPECTED_LABELS);\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\n\t\tdouble shouldBeTwo = ratio.getTotal(TestUtilities.EXPECTED_LABELS);\n\t\tAssert.assertEquals(2.0, shouldBeTwo, 0.1);\n\t}\n\n\tprotected static void assertOnHistogram(Collector collector, double expectedSum, double expectedCount, double expectedCreated,\n\t\t\tdouble boundary) {\n\t\tList<MetricFamilySamples> metrics = collector.collect();\n\t\tAssert.assertEquals(1, metrics.size());\n\t\tMetricFamilySamples family = metrics.get(0);\n\n\t\t// labels + Inf + count + sum\n\t\tAssert.assertEquals(TestUtilities.EXPECTED_LABELS.length + 5, family.samples.size());\n\n\t\tfor (Sample sample : family.samples) {\n\t\t\tList<String> values = sample.labelValues;\n\t\t\tList<String> names = sample.labelNames;\n\n\t\t\t// correct labels without 'le' (bin size)\n\t\t\tfor (int i = 0; i < TestUtilities.TEST_LABELS.length; i++) {\n\t\t\t\tAssert.assertEquals(TestUtilities.TEST_LABELS[i], names.get(i));\n\t\t\t\tAssert.assertEquals(TestUtilities.EXPECTED_LABELS[i], values.get(i));\n\t\t\t}\n\n\t\t\tint labelSize = TestUtilities.EXPECTED_LABELS.length;\n\n\t\t\t// _sum and _count don't have an 'le' label\n\t\t\tif (sample.name.endsWith(\"count\")) {\n\t\t\t\tAssert.assertTrue(values.size() == labelSize && names.size() == labelSize);\n\t\t\t\tAssert.assertEquals(expectedCount, sample.value, 0.1);\n\n\t\t\t} else if (sample.name.endsWith(\"sum\")) {\n\t\t\t\tAssert.assertTrue(values.size() == labelSize && names.size() == labelSize);\n\t\t\t\tAssert.assertEquals(expectedSum, sample.value, 0.1);\n\n\t\t\t} else if (sample.name.endsWith(\"created\")) {\n\t\t\t\tAssert.assertTrue(values.size() == labelSize && names.size() == labelSize);\n\t\t\t\tAssert.assertEquals(expectedCreated, sample.value, 0.1);\n\n\t\t\t} else {\n\t\t\t\tAssert.assertEquals(values.size(), labelSize + 1);\n\t\t\t\tAssert.assertEquals(names.size(), labelSize + 1);\n\n\t\t\t\tString leString = values.get(labelSize);\n\n\t\t\t\tdouble le = (!leString.isEmpty() && !leString.equals(\"+Inf\")) ? Double.parseDouble(leString)\n\t\t\t\t\t\t: Double.MAX_VALUE;\n\n\t\t\t\tif (le == Double.MAX_VALUE) {\n\t\t\t\t\tAssert.assertEquals(expectedCount, sample.value, 0.1);\n\t\t\t\t} else if (le < boundary) {\n\t\t\t\t\tAssert.assertEquals(0, sample.value, 0.1);\n\t\t\t\t} else if (le > boundary) {\n\t\t\t\t\tAssert.assertEquals(expectedCount, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void assertOnSummary(Collector collector, double expectedSum, double expectedCount, double expectedCreated,\n\t\t\tdouble expectedValue) {\n\n\t\tList<MetricFamilySamples> metrics = collector.collect();\n\t\tAssert.assertEquals(1, metrics.size());\n\t\tMetricFamilySamples family = metrics.get(0);\n\t\tAssert.assertEquals(6, family.samples.size()); // 3 quantiles + count + sum\n\n\t\tfor (Sample sample : family.samples) {\n\t\t\tList<String> values = sample.labelValues;\n\t\t\tList<String> names = sample.labelNames;\n\n\t\t\tfor (int i = 0; i < TestUtilities.TEST_LABELS.length; i++) {\n\t\t\t\tAssert.assertEquals(TestUtilities.TEST_LABELS[i], names.get(i));\n\t\t\t\tAssert.assertEquals(TestUtilities.EXPECTED_LABELS[i], values.get(i));\n\t\t\t}\n\n\t\t\t// _sum and _count don't have an 'le' label\n\t\t\tif (sample.name.endsWith(\"count\")) {\n\t\t\t\tAssert.assertEquals(values.size(), TestUtilities.EXPECTED_LABELS.length);\n\t\t\t\tAssert.assertEquals(names.size(), TestUtilities.TEST_LABELS.length);\n\n\t\t\t\tAssert.assertEquals(expectedCount, sample.value, 0.1);\n\n\t\t\t} else if (sample.name.endsWith(\"sum\")) {\n\t\t\t\tAssert.assertEquals(expectedSum, sample.value, 0.1);\n\t\t\t} else if (sample.name.endsWith(\"created\")) {\n\t\t\t\tAssert.assertEquals(expectedCreated, sample.value, 0.1);\n\t\t\t} else {\n\t\t\t\tAssert.assertEquals(values.size(), TestUtilities.EXPECTED_LABELS.length + 1);\n\t\t\t\tAssert.assertEquals(values.size(), TestUtilities.EXPECTED_LABELS.length + 1);\n\n\t\t\t\tAssert.assertEquals(expectedValue, sample.value, 0.1);\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/PrometheusServerTest.java",
    "content": "package com.github.johrstrom.listener;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.URL;\nimport java.net.URLConnection;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n\npublic class PrometheusServerTest {\n\t\n\t@Test\n\tpublic void ensureCleanStartStop() throws Exception {\n\t\tPrometheusServer server = PrometheusServer.getInstance();\n\t\tAssert.assertNotNull(server);\n\t\t\n\t\tserver.start();\n\t\tThread.currentThread();\n\t\tThread.sleep(1000);\n\n\t\tpingAPI();  // also be sure that you can hit the api\n\t\tserver.stop();\n\n\t\tserver.start();\n\t\tThread.currentThread();\n\t\tThread.sleep(1000);\n\n\t\tpingAPI();\t// you can still hit it after stopping\n\t\tserver.stop();\n\t}\n\n\tprivate String pingAPI() throws IOException {\n\t\tURL url = new URL(\"http://localhost:9270/metrics\");\n\t\tURLConnection conn = url.openConnection();\n\t\tconn.setReadTimeout(3000);\n\t\tBufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));\n\n\t\tStringBuilder sb = new StringBuilder();\n\t\tString line = \"\";\n\n\t\twhile ((line = in.readLine()) != null) {\n\t\t\tsb.append(line);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/gui/ListenerGuiTest.java",
    "content": "package com.github.johrstrom.listener.gui;\n\nimport org.apache.jmeter.util.JMeterUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Locale;\n\n\npublic class ListenerGuiTest {\n\n\t@Test\n\tpublic void simpleTest() {\n\t\tJMeterUtils.setLocale(Locale.ENGLISH);\n\t\t\n\t\tPrometheusListenerGui gui = new PrometheusListenerGui();\n\t\t\n\t\tString comment = \"this should be the comment\";\n\t\tString name = \"simple listener name\";\n\t\t\n\t\tgui.setName(name);\n\t\tgui.setComment(comment);\n\t\t\n\t\tAssert.assertEquals(name, gui.getName());\n\t\tAssert.assertEquals(comment, gui.getComment());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/updater/AbstractUpdaterTest.java",
    "content": "package com.github.johrstrom.listener.updater;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.test.TestUtilities;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.samplers.SampleResult;\nimport org.apache.jmeter.threads.JMeterContextService;\nimport org.apache.jmeter.threads.JMeterVariables;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class AbstractUpdaterTest {\n\n\tpublic static class TestUpdater extends  AbstractUpdater {\n\t\t\n\n\t\tpublic TestUpdater(ListenerCollectorConfig cfg) {\n\t\t\tsuper(cfg);\n\t\t}\n\n\t\t@Override\n\t\tpublic void update(SampleEvent e) {\n\t\t\t// do nothing\n\t\t}\n\t}\n\n\t\n\t@Test\n\tpublic void testKeywords() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(new String[] {\"label\",\"code\", \"thread_group\"});\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\t\n\t\tTestUpdater u = new TestUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(\"test_label\");\n\t\tres.setResponseCode(\"204\");\n\t\tSampleEvent event = new SampleEvent(res,\"test_tg\", new JMeterVariables());\n\t\t\n\t\tString[] labels = u.labelValues(event);\n\t\t\n\n\t\tAssert.assertTrue(labels.length == 3);\n\t\tAssert.assertArrayEquals(new String[] {\"test_label\", \"204\", \"test_tg\"}, labels);\n\t}\n\t\n\t@Test\n\tpublic void testVariables() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(new String[] {\"foo\", \"bar\"});\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\t\n\t\tTestUpdater u = new TestUpdater(cfg);\n\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo\", \"funny\");\n\t\tvars.put(\"bar\", \"banal\");\t\t\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\t\n\t\tSampleEvent event = new SampleEvent(new SampleResult(),\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] labels = u.labelValues(event);\n\t\t\n\n\t\tAssert.assertTrue(labels.length == 2);\n\t\tAssert.assertArrayEquals(new String[] {\"funny\", \"banal\"}, labels);\n\t}\n\t\n\t\n\t@Test\n\tpublic void testCombo() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(new String[] {\"foo\", \"code\", \"bar\", \"label\"});\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\t\n\t\tTestUpdater u = new TestUpdater(cfg);\n\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo\", \"funnier\");\n\t\tvars.put(\"bar\", \"more banal\");\t\t\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(\"one after the\");\n\t\tres.setResponseCode(\"909\");\n\t\tSampleEvent event = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\tString[] labels = u.labelValues(event);\n\n\t\tAssert.assertTrue(labels.length == 4);\n\t\tAssert.assertArrayEquals(new String[] {\"funnier\", \"909\", \"more banal\", \"one after the\"}, labels);\n\t}\n\t\n\t@Test\n\tpublic void testNulls() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(new String[] {\"be_null_one\", \"be_null_two\", \"code\"});\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\t\n\t\tTestUpdater u = new TestUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setResponseCode(\"304\");\n\t\tSampleEvent event = new SampleEvent(res ,\"tg1\", new JMeterVariables());\n\t\t\n\t\tString[] labels = u.labelValues(event);\n\t\t\n\t\tAssert.assertTrue(labels.length == 3);\n\t\tAssert.assertArrayEquals(new String[] {\"null\", \"null\", \"304\"}, labels);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/updater/AggregatedTypeUpdaterTest.java",
    "content": "package com.github.johrstrom.listener.updater;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.JMeterCollectorRegistry;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.listener.ListenerCollectorConfig.Measurable;\nimport com.github.johrstrom.test.TestUtilities;\nimport io.prometheus.client.Collector.MetricFamilySamples;\nimport io.prometheus.client.Collector.MetricFamilySamples.Sample;\nimport io.prometheus.client.Histogram;\nimport io.prometheus.client.Summary;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.samplers.SampleResult;\nimport org.apache.jmeter.threads.JMeterContextService;\nimport org.apache.jmeter.threads.JMeterVariables;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class AggregatedTypeUpdaterTest {\n\n\tprivate static final JMeterCollectorRegistry reg = JMeterCollectorRegistry.getInstance();\n\t\n\tprivate static final String[] labels = new String[] {\"foo_label\",\"label\",\"code\"};\n\tprivate static final String name = \"super_cool_sampler\";\n\tprivate static final String code = \"super_cool_sampler\";\n\tprivate static final String var_value = \"bar_value\";\n\tprivate static final String[] expectedLabels = new String[] {var_value,name,code};\n\n\t@Test\n\tpublic void testHistogramResponseTime() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleHistogramCfg();\n\t\tbase.setLabels(labels);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMeasuring(Measurable.ResponseTime.toString());\n\t\tcfg.setMetricName(\"ct_updater_test_hist_rt\");\n\n\t\tHistogram collector = (Histogram) reg.getOrCreateAndRegister(cfg);\n\t\tAggregatedTypeUpdater u = new AggregatedTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(name);\n\t\tint responseTime = 650;\n\t\tres.setStampAndTime(System.currentTimeMillis(), 650);\n\t\tres.setResponseCode(code);\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", var_value);\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] actualLabels = u.labelValues(e);\n\t\tAssert.assertArrayEquals(expectedLabels, actualLabels);\n\t\t\n\t\tu.update(e);\n\n\t\tList<MetricFamilySamples> metrics = collector.collect();\n\t\tassertEquals(1, metrics.size());\n\t\tMetricFamilySamples family = metrics.get(0);\n\t\tassertEquals(8, family.samples.size());     // 4 buckets + Inf + count + sum\n\n\n\t\tfor(Sample sample : family.samples) {\n\t\t\tList<String> values = sample.labelValues;\n\t\t\tList<String> names = sample.labelNames;\n\t\t\t\n\t\t\t//correct labels without 'le' (bin size)\n\t\t\tboolean correctLabels = names.get(0).equals(labels[0]) &&\n\t\t\t\t\tnames.get(1).equals(labels[1]) &&\n\t\t\t\t\tnames.get(2).equals(labels[2]) &&\n\t\t\t\t\tvalues.get(0).equals(expectedLabels[0]) && \n\t\t\t\t\tvalues.get(1).equals(expectedLabels[1]) &&\n\t\t\t\t\tvalues.get(2).equals(expectedLabels[2]);\n\t\t\t\n\t\t\tassertTrue(correctLabels);\n\t\t\t\n\t\t\t// _sum and _count don't have an 'le' label\n\t\t\tif(sample.name.endsWith(\"count\") || sample.name.endsWith(\"sum\") || sample.name.endsWith(\"created\")) {\n\t\t\t\tassertTrue(values.size() == 3 && names.size() == 3);\n\t\t\t\t\n\t\t\t\tif(sample.name.endsWith(\"count\")) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t} else if (sample.name.endsWith(\"created\")) {\n\t\t\t\t\tAssert.assertEquals(System.currentTimeMillis() / 1000.0, sample.value, 0.1);\n\t\t\t\t} else {\n\t\t\t\t\tAssert.assertEquals(responseTime, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}else {\n\t\t\t\tassertTrue(values.size() == 4 && names.size() == 4);\n\t\t\t\t\n\t\t\t\tString leString = values.get(3);\n\t\t\t\t\n\t\t\t\tdouble le = (!leString.isEmpty() && !leString.equals(\"+Inf\")) ? Double.parseDouble(leString) : Double.MAX_VALUE;\n\t\t\t\t\n\t\t\t\tif(le == Double.MAX_VALUE) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t} else if(le < responseTime) {\n\t\t\t\t\tAssert.assertEquals(0, sample.value, 0.1);\n\t\t\t\t}else if(le > responseTime) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t\t\n\t\t\n\t}\n\n\t\n\t@Test\n\tpublic void testSummaryResponseTime() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleSummaryCfg();\n\t\tbase.setLabels(labels);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMeasuring(Measurable.ResponseTime.toString());\n\t\tcfg.setMetricName(\"ct_updater_test_summary_rt\");\n\n\t\tSummary collector = (Summary) reg.getOrCreateAndRegister(cfg);\n\t\tAggregatedTypeUpdater u = new AggregatedTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(name);\n\t\tint responseTime = 650;\n\t\tres.setStampAndTime(System.currentTimeMillis(), 650);\n\t\tres.setResponseCode(code);\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", var_value);\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] actualLabels = u.labelValues(e);\n\t\t\n\t\tAssert.assertArrayEquals(expectedLabels, actualLabels);\n\t\t\n\t\tu.update(e);\n\t\t\n\t\tList<MetricFamilySamples> metrics = collector.collect();\n\t\tassertEquals(1, metrics.size());\n\t\tMetricFamilySamples family = metrics.get(0);\n\t\tassertEquals(6, family.samples.size());     // 3 quantiles + count + sum\n\n\t\t\n\t\tfor(Sample sample : family.samples) {\n\t\t\tList<String> values = sample.labelValues;\n\t\t\tList<String> names = sample.labelNames;\n\t\t\t\n\t\t\t//correct labels without quantile \n\t\t\tboolean correctLabels = names.get(0).equals(labels[0]) && \n\t\t\t\t\tnames.get(1).equals(labels[1]) &&\n\t\t\t\t\tnames.get(2).equals(labels[2]) &&\n\t\t\t\t\tvalues.get(0).equals(expectedLabels[0]) && \n\t\t\t\t\tvalues.get(1).equals(expectedLabels[1]) &&\n\t\t\t\t\tvalues.get(2).equals(expectedLabels[2]);\n\t\t\t\n\t\t\tassertTrue(correctLabels);\n\t\t\t\n\t\t\t// _sum and _count don't have an 'le' label\n\t\t\tif(sample.name.endsWith(\"count\") || sample.name.endsWith(\"sum\") || sample.name.endsWith(\"created\")) {\n\t\t\t\tassertTrue(values.size() == 3 && names.size() == 3);\n\t\t\t\t\n\t\t\t\tif(sample.name.endsWith(\"count\")) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t}else if (sample.name.endsWith(\"created\")) {\n\t\t\t\t\tAssert.assertEquals(System.currentTimeMillis() / 1000.0, sample.value, 0.1);\n\t\t\t\t} else {\n\t\t\t\t\tAssert.assertEquals(responseTime, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}else {\n\t\t\t\tassertTrue(values.size() == 4 && names.size() == 4);\n\t\t\t\tAssert.assertEquals(responseTime, sample.value, 0.1);\n\t\t\t}\n\t\t}\n\t\t\t\n\t}\n\n\t\n\t@Test\n\tpublic void testHistogramResponseSize() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleHistogramCfg();\n\t\tbase.setLabels(labels);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMetricName(\"ct_updater_test_histogram_rsize\");\n\t\tcfg.setMeasuring(Measurable.ResponseSize.toString());\n\n\t\tHistogram collector = (Histogram) reg.getOrCreateAndRegister(cfg);\n\t\tAggregatedTypeUpdater u = new AggregatedTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(name);\n\t\tint responseSize = 650;\n\t\tres.setResponseData(new byte[responseSize]);\n\t\tres.setResponseCode(code);\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", var_value);\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] actualLabels = u.labelValues(e);\n\t\tAssert.assertArrayEquals(expectedLabels, actualLabels);\n\t\t\n\t\t\n\t\tu.update(e);\n\t\t\n\t\t\n\t\tList<MetricFamilySamples> metrics = collector.collect();\n\t\tAssert.assertEquals(1, metrics.size());\n\t\tMetricFamilySamples family = metrics.get(0);\n\t\tAssert.assertEquals(8, family.samples.size());     // 4 buckets + Inf + count + sum\n\n\n\t\tfor(Sample sample : family.samples) {\n\t\t\tList<String> values = sample.labelValues;\n\t\t\tList<String> names = sample.labelNames;\n\t\t\t\n\t\t\tthis.correctLabels(names, values);\n\t\t\t\n\t\t\t// _sum and _count don't have an 'le' label\n\t\t\tif(sample.name.endsWith(\"count\") || sample.name.endsWith(\"sum\") || sample.name.endsWith(\"created\")) {\n\t\t\t\tassertTrue(values.size() == 3 && names.size() == 3);\n\t\t\t\t\n\t\t\t\tif(sample.name.endsWith(\"count\")) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t} else if (sample.name.endsWith(\"created\")) {\n\t\t\t\t\tAssert.assertEquals(System.currentTimeMillis() / 1000.0, sample.value, 0.1);\n\t\t\t\t} else {\n\t\t\t\t\tAssert.assertEquals(responseSize, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}else {\n\t\t\t\tassertTrue(values.size() == 4 && names.size() == 4);\n\t\t\t\t\n\t\t\t\tString leString = values.get(3);\n\t\t\t\t\n\t\t\t\tdouble le = (!leString.isEmpty() && !leString.equals(\"+Inf\")) ? Double.parseDouble(leString) : Double.MAX_VALUE;\n\t\t\t\t\n\t\t\t\tif(le == Double.MAX_VALUE) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t} else if(le < responseSize) {\n\t\t\t\t\tAssert.assertEquals(0, sample.value, 0.1);\n\t\t\t\t}else if(le > responseSize) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\t\t\n\t\t\t\n\t\t}\n\t\t\n\t}\n\t\n\t@Test\n\tpublic void testSummaryResponseSize() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleSummaryCfg();\n\t\tbase.setLabels(labels);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMetricName(\"ct_updater_test_summary_rsize\");\n\t\tcfg.setMeasuring(Measurable.ResponseSize.toString());\n\n\t\tSummary collector = (Summary) reg.getOrCreateAndRegister(cfg);\n\t\tAggregatedTypeUpdater u = new AggregatedTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(name);\n\t\tint responseSize = 650;\n\t\tres.setResponseData(new byte[responseSize]);\n\t\tres.setResponseCode(code);\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", var_value);\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] actualLabels = u.labelValues(e);\n\t\tAssert.assertArrayEquals(expectedLabels, actualLabels);\n\t\t\n\t\tu.update(e);\n\t\t\n\t\tList<MetricFamilySamples> metrics = collector.collect();\n\t\tAssert.assertEquals(1, metrics.size());\n\t\tMetricFamilySamples family = metrics.get(0);\n\t\tAssert.assertEquals(6, family.samples.size());     // 3 quantiles + count + sum\n\n\n\t\tfor(Sample sample : family.samples) {\n\t\t\tList<String> values = sample.labelValues;\n\t\t\tList<String> names = sample.labelNames;\n\t\t\t\n\t\t\tthis.correctLabels(names, values);\n\t\t\t\n\t\t\t// _sum and _count don't have an 'le' label\n\t\t\tif(sample.name.endsWith(\"count\") || sample.name.endsWith(\"sum\") || sample.name.endsWith(\"created\")) {\n\t\t\t\tassertTrue(values.size() == 3 && names.size() == 3);\n\t\t\t\t\n\t\t\t\tif(sample.name.endsWith(\"count\")) {\n\t\t\t\t\tAssert.assertEquals(1, sample.value, 0.1);\n\t\t\t\t} else if (sample.name.endsWith(\"created\")) {\n\t\t\t\t\tAssert.assertEquals(System.currentTimeMillis() / 1000.0, sample.value, 0.1);\n\t\t\t\t} else {\n\t\t\t\t\tAssert.assertEquals(responseSize, sample.value, 0.1);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}else {\n\t\t\t\tassertTrue(values.size() == 4 && names.size() == 4);\n\t\t\t\tAssert.assertEquals(responseSize, sample.value, 0.1);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void correctLabels(List<String> names, List<String> values) {\n\t\tboolean correctLabels = names.get(0).equals(labels[0]) && \n\t\t\t\tnames.get(1).equals(labels[1]) &&\n\t\t\t\tnames.get(2).equals(labels[2]) &&\n\t\t\t\tvalues.get(0).equals(expectedLabels[0]) && \n\t\t\t\tvalues.get(1).equals(expectedLabels[1]) &&\n\t\t\t\tvalues.get(2).equals(expectedLabels[2]);\n\t\t\n\t\tassertTrue(correctLabels);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/listener/updater/CountTypeUpdaterTest.java",
    "content": "package com.github.johrstrom.listener.updater;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.JMeterCollectorRegistry;\nimport com.github.johrstrom.collector.SuccessRatioCollector;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.listener.ListenerCollectorConfig.Measurable;\nimport com.github.johrstrom.test.TestUtilities;\nimport io.prometheus.client.Counter;\nimport org.apache.jmeter.assertions.AssertionResult;\nimport org.apache.jmeter.samplers.SampleEvent;\nimport org.apache.jmeter.samplers.SampleResult;\nimport org.apache.jmeter.threads.JMeterContextService;\nimport org.apache.jmeter.threads.JMeterVariables;\nimport org.junit.Assert;\nimport org.junit.Test;\n\n\npublic class CountTypeUpdaterTest {\n\n\tprivate static final JMeterCollectorRegistry reg = JMeterCollectorRegistry.getInstance();\n\t\n\tprivate final String[] labelNames = new String[] {\"foo_label\",\"label\", \"code\"};\n\tprivate final String[] expectedLabels = new String[] {\"bar_value\", \"myLabelz\", \"909\"};\n\t\n\t@Test\n\tpublic void successCountOnSamplesTest() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(labelNames);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMeasuring(Measurable.SuccessTotal.toString());\n\t\tcfg.setMetricName(\"ct_updater_test_success_only\");\n\t\t\n\t\tCounter c = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(\"myLabelz\");\n\t\tres.setStampAndTime(System.currentTimeMillis(), 10000);\n\t\tres.setSuccessful(true);\t// #1\n\t\tres.setResponseCode(\"909\");\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", \"bar_value\");\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] labels = u.labelValues(e);\n\t\tu.update(e);\n\t\t\n\t\tAssert.assertArrayEquals(expectedLabels, labels);\n\t\t\n\t\tdouble shouldBeOne = c.labels(expectedLabels).get();\n\t\tAssert.assertEquals(1, shouldBeOne, 0.1);\n\t\t\n\t\tu.update(e);\t// #2\n\t\t\n\t\tres.setSuccessful(false);\n\t\te = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\tu.update(e);\t// could be #3, but shouldn't update\n\t\t\n\t\tdouble shouldBeTwo = c.labels(expectedLabels).get();\n\t\t\n\t\tAssert.assertEquals(2, shouldBeTwo, 0.1);\n\t}\n\t\n\t@Test\n\tpublic void failureCountOnSamplesTest() {\t\t\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(labelNames);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMetricName(\"ct_updater_test_failure_only\");\n\t\tcfg.setMeasuring(Measurable.FailureTotal.toString());\n\t\t\n\t\tCounter c = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(\"myLabelz\");\n\t\tres.setStampAndTime(System.currentTimeMillis(), 10000);\n\t\tres.setSuccessful(false); //\t#1\n\t\tres.setResponseCode(\"909\");\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", \"bar_value\");\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] labels = u.labelValues(e);\n\t\tu.update(e);\n\t\t\n\t\tAssert.assertArrayEquals(expectedLabels, labels);\n\t\t\n\t\tdouble shouldBeOne = c.labels(expectedLabels).get();\t\t\n\t\t\n\t\tAssert.assertEquals(1, shouldBeOne, 0.1);\n\t\t\n\t\tu.update(e);\t// #2\n\t\t\n\t\tres.setSuccessful(true);\n\t\te = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\tu.update(e);\t// could be #3, but shouldn't update\n\t\t\n\t\tdouble shouldBeTwo = c.labels(expectedLabels).get();\n\t\t\n\t\tAssert.assertEquals(2, shouldBeTwo, 0.1);\n\t}\n\n\t@Test\n\tpublic void countSamplesTotalTest() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(this.labelNames);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMetricName(\"count_sample_total_test\");\n\t\tcfg.setMeasuring(Measurable.CountTotal.toString());\n\t\t\n\t\tCounter c = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(\"myLabelz\");\n\t\tres.setStampAndTime(System.currentTimeMillis(), 10000);\n\t\tres.setResponseCode(\"909\");\n\t\t\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", \"bar_value\");\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] labels = u.labelValues(e);\n\t\tu.update(e); // #1\n\t\t\n\t\t\n\t\tAssert.assertArrayEquals(this.expectedLabels, labels);\n\t\tdouble shouldBeOne = c.labels(this.expectedLabels).get();\n\t\t\n\t\tAssert.assertEquals(1, shouldBeOne, 0.1);\n\t\t\n\t\tu.update(e);  // #2\n\t\t\n\t\tres.setSuccessful(false); \t\t\t\t// should be 3 no matter what, even if last\n\t\te = new SampleEvent(res,\"tg1\", vars);\t// 2 where success\n\t\tu.update(e);\n\t\t\n\t\tdouble shouldBeThree = c.labels(this.expectedLabels).get();\n\t\tAssert.assertEquals(3, shouldBeThree, 0.1);\n\t}\n\t\n\t@Test\n\tpublic void testSROnSamples() {\n\t\tBaseCollectorConfig base = TestUtilities.simpleSuccessRatioCfg();\n\t\tbase.setLabels(labelNames);\n\t\tString baseName = \"ct_updater_test_success_ratio_samples\";\n\t\tbase.setMetricName(baseName);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\t\n\t\tSuccessRatioCollector c = (SuccessRatioCollector) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(\"myLabelz\");\n\t\tres.setStampAndTime(System.currentTimeMillis(), 10000);\n\t\tres.setSuccessful(true);\n\t\tres.setResponseCode(\"909\");\n\t\t\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(\"foo_label\", \"bar_value\");\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\tSampleEvent e = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\t\n\t\tString[] actualLabels = u.labelValues(e);\n\t\tu.update(e);\t// first success\n\t\t\n\t\tAssert.assertArrayEquals(expectedLabels, actualLabels);\n\t\t\n\t\tdouble successShouldBeOne = c.getSuccess(expectedLabels);\n\t\tdouble failureShouldBeZero = c.getFailure(expectedLabels);\n\t\tdouble totalShouldBeOne = c.getTotal(expectedLabels);\n\t\t\n\t\t\n\t\tAssert.assertEquals(1, successShouldBeOne, 0.1);\n\t\tAssert.assertEquals(0, failureShouldBeZero, 0.1);\n\t\tAssert.assertEquals(1, totalShouldBeOne, 0.1);\n\t\t\n\t\tu.update(e);\t// the 2nd success\n\t\tres.setSuccessful(false);\n\t\te = new SampleEvent(res,\"tg1\", vars);\n\t\t\n\t\tu.update(e);\t// now failure = 1, success = 2 and total = 3\n\t\t\n\t\tdouble successShouldBeTwo = c.getSuccess(expectedLabels);\n\t\tdouble failureShouldBeOne = c.getFailure(expectedLabels);\n\t\tdouble totalShouldBeThree = c.getTotal(expectedLabels);\n\t\t\n\t\t\n\t\tAssert.assertEquals(2, successShouldBeTwo, 0.1);\n\t\tAssert.assertEquals(1, failureShouldBeOne, 0.1);\n\t\tAssert.assertEquals(3, totalShouldBeThree, 0.1);\n\t}\n\t\n\t@Test\n\tpublic void testRatioOnAssertions() {\n\t\tListenerCollectorConfig cfg = TestUtilities.listenerSuccessRatioCfg(\n\t\t\t\t\"ratio_on_assertions\",\n\t\t\t\tListenerCollectorConfig.ASSERTIONS);\n\t\t\n\t\tSuccessRatioCollector ratio = (SuccessRatioCollector) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult result = newSampleResultWithAssertion(true);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #1 success\n\t\t\n\t\tdouble actualSuccess = ratio.getSuccess(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tdouble actualFailure = ratio.getFailure(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tdouble actualTotal = ratio.getTotal(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tAssert.assertEquals(1.0, actualSuccess, 0.1);\n\t\tAssert.assertEquals(0.0, actualFailure, 0.1);\n\t\tAssert.assertEquals(1.0, actualTotal, 0.1);\n\t\t\n\t\t\n\t\tresult = newSampleResultWithAssertion(false);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #1 failure\n\t\t\n\t\tactualSuccess = ratio.getSuccess(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tactualFailure = ratio.getFailure(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tactualTotal = ratio.getTotal(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tAssert.assertEquals(1.0, actualSuccess, 0.1);\n\t\tAssert.assertEquals(1.0, actualFailure, 0.1);\n\t\tAssert.assertEquals(2.0, actualTotal, 0.1);\n\t\t\n\t\tresult = newSampleResultWithAssertion(true);\n\t\tresult.addAssertionResult(altAssertion(true));\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #now update alt as well\n\t\t\n\t\tactualSuccess = ratio.getSuccess(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tactualFailure = ratio.getFailure(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tactualTotal = ratio.getTotal(TestUtilities.EXPECTED_ASSERTION_LABELS);\n\t\tAssert.assertEquals(2.0, actualSuccess, 0.1);\n\t\tAssert.assertEquals(1.0, actualFailure, 0.1);\n\t\tAssert.assertEquals(3.0, actualTotal, 0.1);\n\t\t\n\t\tactualSuccess = ratio.getSuccess(TestUtilities.EXPECTED_ASSERTION_LABELS_ALT);\n\t\tactualFailure = ratio.getFailure(TestUtilities.EXPECTED_ASSERTION_LABELS_ALT);\n\t\tactualTotal = ratio.getTotal(TestUtilities.EXPECTED_ASSERTION_LABELS_ALT);\n\t\tAssert.assertEquals(1.0, actualSuccess, 0.1);\n\t\tAssert.assertEquals(0.0, actualFailure, 0.1);\n\t\tAssert.assertEquals(1.0, actualTotal, 0.1);\n\t}\n\n\t\n\t@Test\n\tpublic void testSuccessAssertions() {\n\t\tListenerCollectorConfig cfg = TestUtilities.listenerCounterCfg(\n\t\t\t\t\"count_assertion_success_test\",\n\t\t\t\tMeasurable.SuccessTotal,\n\t\t\t\tListenerCollectorConfig.ASSERTIONS);\n\t\t\n\t\tCounter c = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult result = newSampleResultWithAssertion(true);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #1\n\t\tdouble shouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\n\t\tresult = newSampleResultWithAssertion(false);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #could be 2, but should be 1\n\t\tshouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\n\t\t// now update 2 assertions\n\t\tresult = newSampleResultWithAssertion(true);\n\t\tresult.addAssertionResult(altAssertion(true));\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #now should be 2\n\t\tdouble shouldBeTwo = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(2.0, shouldBeTwo, 0.1);\n\t\tshouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS_ALT).get();\t//but alt is just 1\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t}\n\t\n\t@Test\n\tpublic void testFailureAssertions() {\n\t\tListenerCollectorConfig cfg = TestUtilities.listenerCounterCfg(\n\t\t\t\t\"count_assertion_failure_test\",\n\t\t\t\tMeasurable.FailureTotal,\n\t\t\t\tListenerCollectorConfig.ASSERTIONS);\n\t\t\n\t\tCounter c = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult result = newSampleResultWithAssertion(false);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #1\n\t\tdouble shouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\n\t\t\n\t\tresult = newSampleResultWithAssertion(true);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #could be 2, but should be 1\n\t\tshouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\n\t\t// now update 2 assertions\n\t\tresult = newSampleResultWithAssertion(false);\n\t\tresult.addAssertionResult(altAssertion(false));\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #now should be 2\n\t\tdouble shouldBeTwo = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(2.0, shouldBeTwo, 0.1);\n\t\tshouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS_ALT).get();\t//but alt is just 1\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\t\t\n\t}\n\t\n\t@Test\n\tpublic void testTotalAssertions() {\n\t\tListenerCollectorConfig cfg = TestUtilities.listenerCounterCfg(\n\t\t\t\t\"count_assertion_total_test\",\n\t\t\t\tMeasurable.CountTotal,\n\t\t\t\tListenerCollectorConfig.ASSERTIONS);\n\t\t\n\t\tCounter c = (Counter) reg.getOrCreateAndRegister(cfg);\n\t\tCountTypeUpdater u = new CountTypeUpdater(cfg);\n\t\t\n\t\tSampleResult result = newSampleResultWithAssertion(false);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #1\n\t\tdouble shouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t\t\n\t\t\n\t\tresult = newSampleResultWithAssertion(true);\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #2\n\t\tdouble shouldBeTwo = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(2.0, shouldBeTwo, 0.1);\n\t\t\n\t\t// now update 2 assertions\n\t\tresult = newSampleResultWithAssertion(false);\n\t\tresult.addAssertionResult(altAssertion(false));\n\t\tu.update(new SampleEvent(result,\"tg1\", vars()));\t// #3\n\t\tdouble shouldBeThree = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS).get();\n\t\tAssert.assertEquals(3.0, shouldBeThree, 0.1);\n\t\tshouldBeOne = c.labels(TestUtilities.EXPECTED_ASSERTION_LABELS_ALT).get();\t//but alt is just 1\n\t\tAssert.assertEquals(1.0, shouldBeOne, 0.1);\n\t}\n\t\n\tpublic static SampleResult newSampleResult(boolean success) {\n\t\tSampleResult res = new SampleResult();\n\t\tres.setSampleLabel(TestUtilities.TEST_SAMPLER_NAME);\n\t\tres.setSuccessful(success);\n\t\t\n\t\treturn res;\n\t}\n\t\n\tpublic static SampleResult newSampleResultWithAssertion(boolean success) {\n\t\tSampleResult res = newSampleResult(success);\n\t\t\n\t\tAssertionResult assertion = new AssertionResult(TestUtilities.TEST_ASSERTION_NAME);\n\t\tassertion.setFailure(!success);\n\t\t\n\t\tres.addAssertionResult(assertion);\n\t\treturn res;\n\t}\n\t\n\tpublic static AssertionResult altAssertion(boolean success) {\n\t\tAssertionResult assertion = new AssertionResult(TestUtilities.TEST_ASSERTION_NAME_ALT);\n\t\tassertion.setFailure(!success);\n\t\treturn assertion;\n\t}\n\n\tpublic static JMeterVariables vars() {\n\t\tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(TestUtilities.TEST_VAR_NAME, TestUtilities.TEST_VAR_VALUE);\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\treturn vars;\n\t}\n\n\t\n\n}\n"
  },
  {
    "path": "src/test/java/com/github/johrstrom/test/NOOPThreadMonitor.java",
    "content": "package com.github.johrstrom.test;\n\nimport org.apache.jmeter.threads.JMeterThread;\nimport org.apache.jmeter.threads.JMeterThreadMonitor;\n\npublic class NOOPThreadMonitor implements JMeterThreadMonitor {\n\n   @Override\n   public void threadFinished(JMeterThread jmt) {\n     // NOOP\n   }\n\n}"
  },
  {
    "path": "src/test/java/com/github/johrstrom/test/TestUtilities.java",
    "content": "package com.github.johrstrom.test;\n\nimport com.github.johrstrom.collector.BaseCollectorConfig;\nimport com.github.johrstrom.collector.BaseCollectorConfig.JMeterCollectorType;\nimport com.github.johrstrom.listener.ListenerCollectorConfig;\nimport com.github.johrstrom.listener.ListenerCollectorConfig.Measurable;\nimport org.apache.jmeter.control.LoopController;\nimport org.apache.jmeter.engine.StandardJMeterEngine;\nimport org.apache.jmeter.gui.tree.JMeterTreeListener;\nimport org.apache.jmeter.gui.tree.JMeterTreeModel;\nimport org.apache.jmeter.samplers.SampleResult;\nimport org.apache.jmeter.save.SaveService;\nimport org.apache.jmeter.threads.ThreadGroup;\nimport org.apache.jmeter.threads.*;\nimport org.apache.jmeter.util.JMeterUtils;\nimport org.apache.jorphan.collections.HashTree;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\npublic class TestUtilities {\n\t\n\tpublic static final String TEST_VAR_NAME = \"arbitrary_var\";\n\tpublic static final String TEST_SAMPLER_NAME = \"super_cool_sampler\";\n\tpublic static final String TEST_ASSERTION_NAME = \"super_cool_assertion\";\n\tpublic static final String TEST_ASSERTION_NAME_ALT = \"other_super_cool_assertion\";\n\tpublic static final String TEST_SAMPLER_CODE = \"909\";\n\tpublic static final String TEST_VAR_VALUE = \"bar_value\";\n\t\n\tpublic static final String[] TEST_LABELS = new String[] {TEST_VAR_NAME,\"label\",\"code\"};\n\tpublic static final String[] EXPECTED_LABELS = new String[] {TEST_VAR_VALUE, TEST_SAMPLER_NAME, TEST_SAMPLER_CODE};\n\t\n\tpublic static final String[] TEST_ASSERTION_LABELS = new String[] {TEST_VAR_NAME,\"label\"};\n\tpublic static final String[] EXPECTED_ASSERTION_LABELS = new String[] {TEST_VAR_VALUE, TEST_ASSERTION_NAME};\n\tpublic static final String[] EXPECTED_ASSERTION_LABELS_ALT = new String[] {TEST_VAR_VALUE, TEST_ASSERTION_NAME_ALT};\n\t\n\tpublic static BaseCollectorConfig simpleCounterCfg() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"simple_counter\");\n\t\tcfg.setType(JMeterCollectorType.COUNTER.toString());\n\t\tcfg.setHelp(\"some helpe message\");\n\t\t\n\t\treturn cfg;\n\t}\n\t\n\tpublic static ListenerCollectorConfig listenerCounterCfg(String name, Measurable measurable, String listenTo) {\n\t\tBaseCollectorConfig base = TestUtilities.simpleCounterCfg();\n\t\tbase.setLabels(TestUtilities.TEST_ASSERTION_LABELS);\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig(base);\n\t\tcfg.setMetricName(name);\n\t\tcfg.setMeasuring(measurable.toString());\n\t\tcfg.setListenTo(listenTo);\n\t\t\n\t\treturn cfg;\n\t}\n\t\n\tpublic static ListenerCollectorConfig listenerSuccessRatioCfg(String name, String listenTo) {\n\t\tListenerCollectorConfig cfg = new ListenerCollectorConfig();\n\t\tcfg.setLabels(TestUtilities.TEST_ASSERTION_LABELS);\n\t\tcfg.setMetricName(name);\n\t\tcfg.setType(JMeterCollectorType.SUCCESS_RATIO.toString());\n\t\tcfg.setHelp(\"some helpe message\");\n\t\tcfg.setListenTo(listenTo);\n\t\t\n\t\treturn cfg;\n\t}\n\t\n\tpublic static BaseCollectorConfig simpleSummaryCfg() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"simple_summary\");\n\t\tcfg.setType(JMeterCollectorType.SUMMARY.toString());\n\t\tcfg.setHelp(\"some helpe message\");\n\t\t\n\t\treturn cfg;\n\t}\n\t\n\tpublic static BaseCollectorConfig simpleHistogramCfg() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"simple_histogram\");\n\t\tcfg.setType(JMeterCollectorType.HISTOGRAM.toString());\n\t\tcfg.setHelp(\"some helpe message\");\n\t\t\n\t\treturn cfg;\n\t}\n\t\n\tpublic static BaseCollectorConfig simpleGaugeCfg() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"simple_gauge\");\n\t\tcfg.setType(JMeterCollectorType.GAUGE.toString());\n\t\tcfg.setHelp(\"some helpe message\");\n\t\t\n\t\treturn cfg;\n\t}\n\t\n\tpublic static BaseCollectorConfig simpleSuccessRatioCfg() {\n\t\tBaseCollectorConfig cfg = new BaseCollectorConfig();\n\t\tcfg.setMetricName(\"simple_ratio\");\n\t\tcfg.setType(JMeterCollectorType.SUCCESS_RATIO.toString());\n\t\tcfg.setHelp(\"some helpe message\");\n\t\t\n\t\treturn cfg;\n\t}\n\n    public static void createJmeterEnv() {\n    \t\n        JMeterUtils.setJMeterHome(\"src/test/resources\");\n        JMeterUtils.setLocale(Locale.ENGLISH);\n        JMeterUtils.loadJMeterProperties(\"src/test/resources/bin/jmeter.properties\");\n        \n        try {\n\t\t\tSaveService.loadProperties();\n\t\t} catch (IOException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n        JMeterTreeModel jMeterTreeModel = new JMeterTreeModel();\n        JMeterTreeListener jMeterTreeListener = new JMeterTreeListener();\n        jMeterTreeListener.setModel(jMeterTreeModel);\n        \n        JMeterContextService.getContext().setVariables(new JMeterVariables());\n        StandardJMeterEngine engine = new StandardJMeterEngine();\n        JMeterContextService.getContext().setEngine(engine);\n        \n        JMeterThreadMonitor monitor = new NOOPThreadMonitor();\n        \n        \n        HashTree hashtree = new HashTree();\n        hashtree.add(new LoopController());\n        \n        JMeterThread thread = new JMeterThread(hashtree, monitor, null);\n        thread.setThreadName(\"test thread\");\n        JMeterContextService.getContext().setThread(thread);\n        \n        \n        ThreadGroup tg1 = new ThreadGroup();\n        tg1.setName(\"tg1\");\n        JMeterContextService.getContext().setThreadGroup(tg1);\n        \n    }\n    \n    public static List<BaseCollectorConfig> simpleListConfig() {\n    \tList<BaseCollectorConfig> list = new ArrayList<BaseCollectorConfig>(); \n    \t\n    \tlist.add(simpleGaugeCfg());\n    \tlist.add(simpleHistogramCfg());\n    \tlist.add(simpleSummaryCfg());\n    \tlist.add(simpleCounterCfg());\n    \tlist.add(simpleSuccessRatioCfg());\n    \t\n    \treturn list;\n    }\n    \n    public static List<ListenerCollectorConfig> simpleListListener() {\n    \tList<ListenerCollectorConfig> list = new ArrayList<ListenerCollectorConfig>(); \n    \t\n    \tlist.add(new ListenerCollectorConfig(simpleGaugeCfg()));\n    \tlist.add(new ListenerCollectorConfig(simpleHistogramCfg()));\n    \tlist.add(new ListenerCollectorConfig(simpleSummaryCfg()));\n    \tlist.add(new ListenerCollectorConfig(simpleCounterCfg()));\n    \tlist.add(new ListenerCollectorConfig(simpleSuccessRatioCfg()));\n    \t\n    \treturn list;\n    }\n    \n    public static List<ListenerCollectorConfig> fullListListener(){\n    \tList<ListenerCollectorConfig> list = new ArrayList<ListenerCollectorConfig>();\n    \t\n    \t// ---------- counters and success ratio\n    \tListenerCollectorConfig cfg = new ListenerCollectorConfig(simpleCounterCfg());\n    \tcfg = redoNameAndMeasuring(cfg, \"test_count_total\", Measurable.CountTotal);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_failure_total\", Measurable.FailureTotal);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_success_total\", Measurable.SuccessTotal);\n    \tlist.add(cfg);\n    \t\n    \tcfg = new ListenerCollectorConfig(simpleSuccessRatioCfg());\n    \tcfg = redoNameAndMeasuring(cfg, \"test_ratio\", Measurable.SuccessRatio);\n    \tlist.add(cfg);\n    \t\n    \t// ------- histograms\n    \tcfg = new ListenerCollectorConfig(simpleHistogramCfg());\n    \tcfg = redoNameAndMeasuring(cfg, \"test_hist_rtime\", Measurable.ResponseTime);\n    \tlist.add(cfg);\n    \n    \tcfg = redoNameAndMeasuring(cfg, \"test_hist_rsize\", Measurable.ResponseSize);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_hist_latency\", Measurable.Latency);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_hist_idle_time\", Measurable.IdleTime);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_hist_connect_time\", Measurable.ConnectTime);\n    \tlist.add(cfg);\n    \n    \t// -------- summaries\n    \tcfg = new ListenerCollectorConfig(simpleSummaryCfg());\n    \tcfg = redoNameAndMeasuring(cfg, \"test_summary_rtime\", Measurable.ResponseTime);\n    \tlist.add(cfg);\n    \n    \tcfg = redoNameAndMeasuring(cfg, \"test_summary_rsize\", Measurable.ResponseSize);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_summary_latency\", Measurable.Latency);\n    \tlist.add(cfg);    \t\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_summary_idle_time\", Measurable.IdleTime);\n    \tlist.add(cfg);\n    \t\n    \tcfg = redoNameAndMeasuring(cfg, \"test_summary_connect_time\", Measurable.ConnectTime);\n    \tlist.add(cfg);\n    \t\n    \treturn list;\n    }\n    \n    public static ListenerCollectorConfig redoNameAndMeasuring(ListenerCollectorConfig cfg, String name, Measurable m) {\n    \tListenerCollectorConfig clone = (ListenerCollectorConfig) cfg.clone();\n    \t\n    \tclone.setMetricName(name);\n    \tclone.setMeasuring(m.toString());\n    \t   \t\n    \treturn clone;\n    }\n    \n    public static ResultAndVariables resultWithLabels() {\n    \tSampleResult result = new SampleResult();\n    \t\n    \tresult.setSampleLabel(TEST_SAMPLER_NAME);\n    \tresult.setResponseCode(TEST_SAMPLER_CODE);\n    \t\n    \tJMeterVariables vars = new JMeterVariables();\n\t\tvars.put(TEST_VAR_NAME, TEST_VAR_VALUE);\n\t\tJMeterContextService.getContext().setVariables(vars);\n\t\t\n\t\treturn new ResultAndVariables(result, vars);\n    }\n    \n    public static class ResultAndVariables {\n    \tpublic SampleResult result;\n    \tpublic JMeterVariables vars;\n    \t\n    \tpublic ResultAndVariables(SampleResult result, JMeterVariables vars) {\n    \t\tthis.result = result;\n    \t\tthis.vars = vars;\n    \t}\n    }\n    \n    \n}\n"
  },
  {
    "path": "src/test/resources/bin/jmeter.properties",
    "content": "################################################################################\n# Apache JMeter Property file\n################################################################################\n\n##   Licensed to the Apache Software Foundation (ASF) under one or more\n##   contributor license agreements.  See the NOTICE file distributed with\n##   this work for additional information regarding copyright ownership.\n##   The ASF licenses this file to You under the Apache License, Version 2.0\n##   (the \"License\"); you may not use this file except in compliance with\n##   the License.  You may obtain a copy of the License at\n##\n##       http://www.apache.org/licenses/LICENSE-2.0\n##\n##   Unless required by applicable law or agreed to in writing, software\n##   distributed under the License is distributed on an \"AS IS\" BASIS,\n##   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n##   See the License for the specific language governing permissions and\n##   limitations under the License.\n\n################################################################################\n#\n#                      THIS FILE SHOULD NOT BE MODIFIED\n#\n# This avoids having to re-apply the modifications when upgrading JMeter\n# Instead only user.properties should be modified:\n# 1/ copy the property you want to modify to user.properties from jmeter.properties\n# 2/ Change its value there\n#\n################################################################################\n\n# JMeter properties are described in the file\n# http://jmeter.apache.org/usermanual/properties_reference.html\n# A local copy can be found in\n# printable_docs/usermanual/properties_reference.html\n\n#Preferred GUI language. Comment out to use the JVM default locale's language.\n#language=en\n\n\n# Additional locale(s) to add to the displayed list.\n# The current default list is: en, fr, de, no, es, tr, ja, zh_CN, zh_TW, pl, pt_BR\n# [see JMeterMenuBar#makeLanguageMenu()]\n# The entries are a comma-separated list of language names\n#locales.add=zu\n\n\n#---------------------------------------------------------------------------\n# XML Parser\n#---------------------------------------------------------------------------\n\n# Path to a Properties file containing Namespace mapping in the form\n# prefix=Namespace\n# Example:\n# ns=http://biz.aol.com/schema/2006-12-18\n#xpath.namespace.config=\n\n#---------------------------------------------------------------------------\n# SSL configuration\n#---------------------------------------------------------------------------\n\n## SSL System properties are now in system.properties\n\n# JMeter no longer converts javax.xxx property entries in this file into System properties.\n# These must now be defined in the system.properties file or on the command-line.\n# The system.properties file gives more flexibility.\n\n# By default, SSL session contexts are now created per-thread, rather than being shared.\n# The original behaviour can be enabled by setting the JMeter property to true\n#https.sessioncontext.shared=false\n\n# Be aware that https default protocol may vary depending on the version of JVM\n# See https://blogs.oracle.com/java-platform-group/entry/diagnosing_tls_ssl_and_https\n# See https://bz.apache.org/bugzilla/show_bug.cgi?id=58236\n# Default HTTPS protocol level:\n#https.default.protocol=TLS\n# This may need to be changed here (or in user.properties) to:\n#https.default.protocol=SSLv3\n\n# List of protocols to enable. You may have to select only a subset if you find issues with target server.\n# This is needed when server does not support Socket version negotiation, this can lead to:\n# javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated\n# java.net.SocketException: Connection reset\n# see https://bz.apache.org/bugzilla/show_bug.cgi?id=54759\n#https.socket.protocols=SSLv2Hello SSLv3 TLSv1\n\n# Control if we allow reuse of cached SSL context between iterations\n# set the value to 'false' to reset the SSL context each iteration\n#https.use.cached.ssl.context=true\n\n# Start and end index to be used with keystores with many entries\n# The default is to use entry 0, i.e. the first\n#https.keyStoreStartIndex=0\n#https.keyStoreEndIndex=0\n\n#---------------------------------------------------------------------------\n# Look and Feel configuration\n#---------------------------------------------------------------------------\n\n#Classname of the Swing default UI\n#\n# The LAF classnames that are available are now displayed as ToolTip text\n# when hovering over the Options/Look and Feel selection list.\n#\n# You can either use a full class name, as shown below,\n# or one of the strings \"System\" or \"CrossPlatform\" which means\n#  JMeter will use the corresponding string returned by UIManager.get<name>LookAndFeelClassName()\n\n# LAF can be overridden by os.name (lowercased, spaces replaced by '_')\n# Sample os.name LAF:\n#jmeter.laf.windows_xp=javax.swing.plaf.metal.MetalLookAndFeel\n\n# Failing that, the OS family = os.name, but only up to first space:\n# Sample OS family LAF:\n#jmeter.laf.windows=com.sun.java.swing.plaf.windows.WindowsLookAndFeel\n\n# Custom settings for Mac using System LAF if you don't want to use Darcula\n#jmeter.laf.mac=System\n\n# Failing that, the JMeter default laf can be defined:\n#jmeter.laf=System\n\n# If none of the above jmeter.laf properties are defined, JMeter uses the CrossPlatform LAF.\n# This is because the CrossPlatform LAF generally looks better than the System LAF.\n# See https://bz.apache.org/bugzilla/show_bug.cgi?id=52026 for details\n# N.B. the laf can be defined in user.properties.\n\n# LoggerPanel display\n# default to false\n#jmeter.loggerpanel.display=false\n\n# Enable LogViewer Panel to receive log event even if closed\n# Enabled since 2.12\n# Note this has some impact on performances, but as GUI mode must\n# not be used for Load Test it is acceptable\n#jmeter.loggerpanel.enable_when_closed=true\n\n# Max lines kept in LoggerPanel, default to 1000 chars\n# 0 means no limit\n#jmeter.loggerpanel.maxlength=1000\n\n# Interval period in ms to process the queue of events of the listeners\n#jmeter.gui.refresh_period=500\n\n# HiDPI mode (default: false)\n# Activate a 'pseudo'-hidpi mode. Allows to increase size of some UI elements\n# which are not correctly managed by JVM with high resolution screens in Linux or Windows\n#jmeter.hidpi.mode=false\n# To enable pseudo-hidpi mode change to true\n#jmeter.hidpi.mode=true\n# HiDPI scale factor\n#jmeter.hidpi.scale.factor=1.0\n# Suggested value for HiDPI\n#jmeter.hidpi.scale.factor=2.0\n\n# Toolbar display\n# Toolbar icon definitions\n#jmeter.toolbar.icons=org/apache/jmeter/images/toolbar/icons-toolbar.properties\n# Toolbar list\n#jmeter.toolbar=new,open,close,save,save_as_testplan,|,cut,copy,paste,|,expand,collapse,toggle,|,test_start,test_stop,test_shutdown,|,test_start_remote_all,test_stop_remote_all,test_shutdown_remote_all,|,test_clear,test_clear_all,|,search,search_reset,|,function_helper,help\n# Toolbar icons default size: 22x22. Available sizes are: 22x22, 32x32, 48x48\n#jmeter.toolbar.icons.size=22x22\n# Suggested value for HiDPI\n#jmeter.toolbar.icons.size=48x48\n\n# Icon definitions\n# default:\n#jmeter.icons=org/apache/jmeter/images/icon.properties\n# alternate:\n#jmeter.icons=org/apache/jmeter/images/icon_1.properties\n# Historical icon set (deprecated)\n#jmeter.icons=org/apache/jmeter/images/icon_old.properties\n\n# Tree icons default size: 19x19. Available sizes are: 19x19, 24x24, 32x32, 48x48\n# Useful for HiDPI display (see below)\n#jmeter.tree.icons.size=19x19\n# Suggested value for HiDPI screen like 3200x1800:\n#jmeter.tree.icons.size=32x32\n\n#Components to not display in JMeter GUI (GUI class name or static label)\n# These elements are deprecated and will be removed in next version:\n# MongoDB Script, MongoDB Source Config, Monitor Results\n# BSF Elements\nnot_in_menu=org.apache.jmeter.protocol.mongodb.sampler.MongoScriptSampler,org.apache.jmeter.protocol.mongodb.config.MongoSourceElement,\\\n    org.apache.jmeter.timers.BSFTimer,org.apache.jmeter.modifiers.BSFPreProcessor,org.apache.jmeter.extractor.BSFPostProcessor,org.apache.jmeter.assertions.BSFAssertion,\\\n    org.apache.jmeter.visualizers.BSFListener,org.apache.jmeter.protocol.java.sampler.BSFSampler,\\\n    org.apache.jmeter.protocol.http.control.gui.SoapSamplerGui\n\n# Number of items in undo history\n# Feature is disabled by default (0) due to known and not fixed bugs:\n# https://bz.apache.org/bugzilla/show_bug.cgi?id=57043\n# https://bz.apache.org/bugzilla/show_bug.cgi?id=57039\n# https://bz.apache.org/bugzilla/show_bug.cgi?id=57040\n# Set it to a number > 0 (25 can be a good default)\n# The bigger it is, the more it consumes memory\n#undo.history.size=0\n\n# Hotkeys to add JMeter components, will add elements when you press Ctrl+0 .. Ctrl+9 (Command+0 .. Command+9 on Mac)\ngui.quick_0=ThreadGroupGui\ngui.quick_1=HttpTestSampleGui\ngui.quick_2=RegexExtractorGui\ngui.quick_3=AssertionGui\ngui.quick_4=ConstantTimerGui\ngui.quick_5=TestActionGui\ngui.quick_6=JSR223PostProcessor\ngui.quick_7=JSR223PreProcessor\ngui.quick_8=DebugSampler\ngui.quick_9=ViewResultsFullVisualizer\n\n\n#---------------------------------------------------------------------------\n# JMX Backup configuration\n#---------------------------------------------------------------------------\n#Enable auto backups of the .jmx file when a test plan is saved.\n#When enabled, before the .jmx is saved, it will be backed up to the directory pointed\n#by the jmeter.gui.action.save.backup_directory property (see below). Backup file names are built\n#after the jmx file being saved. For example, saving test-plan.jmx will create a test-plan-000012.jmx\n#in the backup directory provided that the last created backup file is test-plan-000011.jmx.\n#Default value is true indicating that auto backups are enabled\n#jmeter.gui.action.save.backup_on_save=true\n\n#Set the backup directory path where JMX backups will be created upon save in the GUI.\n#If not set (what it defaults to) then backup files will be created in\n#a sub-directory of the JMeter base installation. The default directory is ${JMETER_HOME}/backups\n#If set and the directory does not exist, it will be created.\n#jmeter.gui.action.save.backup_directory=\n\n#Set the maximum time (in hours) that backup files should be preserved since the save time.\n#By default no expiration time is set which means we keep backups for ever.\n#jmeter.gui.action.save.keep_backup_max_hours=0\n\n#Set the maximum number of backup files that should be preserved. By default 10 backups will be preserved.\n#Setting this to zero will cause the backups to not being deleted (unless keep_backup_max_hours is set to a non zero value)\n#jmeter.gui.action.save.keep_backup_max_count=10\n\n#Enable auto saving of the .jmx file before start run a test plan\n#When enabled, before the run, the .jmx will be saved and also backed up to the directory pointed\n#save_automatically_before_run=true\n\n#---------------------------------------------------------------------------\n# Remote hosts and RMI configuration\n#---------------------------------------------------------------------------\n\n# Remote Hosts - comma delimited\nremote_hosts=127.0.0.1\n#remote_hosts=localhost:1099,localhost:2010\n\n# RMI port to be used by the server (must start rmiregistry with same port)\n#server_port=1099\n\n# To change the port to (say) 1234:\n# On the server(s)\n# - set server_port=1234\n# - start rmiregistry with port 1234\n# On Windows this can be done by:\n# SET SERVER_PORT=1234\n# JMETER-SERVER\n#\n# On Unix:\n# SERVER_PORT=1234 jmeter-server\n#\n# On the client:\n# - set remote_hosts=server:1234\n\n# Parameter that controls the RMI port used by RemoteSampleListenerImpl (The Controller)\n# Default value is 0 which means port is randomly assigned\n# You may need to open Firewall port on the Controller machine\n#client.rmi.localport=0\n\n# When distributed test is starting, there may be several attempts to initialize\n# remote engines. By default, only single try is made. Increase following property\n# to make it retry for additional times\n#client.tries=1\n\n# If there is initialization retries, following property sets delay between attempts\n#client.retries_delay=5000\n\n# When all initialization tries was made, test will fail if some remote engines are failed\n# Set following property to true to ignore failed nodes and proceed with test\n#client.continue_on_fail=false\n\n# To change the default port (1099) used to access the server:\n#server.rmi.port=1234\n\n# To use a specific port for the JMeter server engine, define\n# the following property before starting the server:\n#server.rmi.localport=4000\n\n# The jmeter server creates by default the RMI registry as part of the server process.\n# To stop the server creating the RMI registry:\n#server.rmi.create=false\n\n# Define the following property to cause JMeter to exit after the first test\n#server.exitaftertest=true\n\n#\n# Configuration of Secure RMI connection\n#\n# Type of keystore : JKS\n#server.rmi.ssl.keystore.type=JKS\n#\n# Keystore file that contains private key\n#server.rmi.ssl.keystore.file=rmi_keystore.jks\n#\n# Password of Keystore\n#server.rmi.ssl.keystore.password=changeit\n#\n# Key alias\n#server.rmi.ssl.keystore.alias=rmi\n#\n# Type of truststore : JKS\n#server.rmi.ssl.truststore.type=JKS\n#\n# Keystore file that contains certificate\n#server.rmi.ssl.truststore.file=rmi_keystore.jks\n#\n# Password of Trust store\n#server.rmi.ssl.truststore.password=changeit\n#\n# Set this if you don't want to use SSL for RMI\n#server.rmi.ssl.disable=false\n#---------------------------------------------------------------------------\n#         Include Controller\n#---------------------------------------------------------------------------\n\n# Prefix used by IncludeController when building file name\n#includecontroller.prefix=\n\n#---------------------------------------------------------------------------\n# HTTP Java configuration\n#---------------------------------------------------------------------------\n\n# Number of connection retries performed by HTTP Java sampler before giving up\n# 0 means no retry since version 3.0\n#http.java.sampler.retries=0\n\n#---------------------------------------------------------------------------\n# Following properties apply to Apache HttpClient\n#---------------------------------------------------------------------------\n\n# set the socket timeout (or use the parameter http.socket.timeout)\n# for AJP Sampler implementation.\n# Value is in milliseconds\n#httpclient.timeout=0\n# 0 == no timeout\n\n# Set the http version (defaults to 1.1)\n#httpclient.version=1.1 (or use the parameter http.protocol.version)\n\n# Define characters per second > 0 to emulate slow connections\n#httpclient.socket.http.cps=0\n#httpclient.socket.https.cps=0\n\n#Enable loopback protocol\n#httpclient.loopback=true\n\n# Define the local host address to be used for multi-homed hosts\n#httpclient.localaddress=1.2.3.4\n\n#---------------------------------------------------------------------------\n# AuthManager Kerberos configuration\n#---------------------------------------------------------------------------\n\n# AuthManager Kerberos configuration\n# Name of application module used in jaas.conf\n#kerberos_jaas_application=JMeter\n\n# Should ports be stripped from urls before constructing SPNs\n# for SPNEGO authentication\n#kerberos.spnego.strip_port=true\n\n#---------------------------------------------------------------------------\n# Apache HttpComponents HTTPClient configuration (HTTPClient4)\n#---------------------------------------------------------------------------\n\n# define a properties file for overriding Apache HttpClient parameters\n# Uncomment this line if you put anything in hc.parameters file\n#hc.parameters.file=hc.parameters\n\n# Preemptively send Authorization Header when BASIC auth is used\n#httpclient4.auth.preemptive=true\n\n# Number of retries to attempt (default 0)\n#httpclient4.retrycount=0\n\n# true if it's OK to retry requests that have been sent\n# This will retry Idempotent and non Idempotent requests\n# This should usually be false, but it can be useful\n# when testing against some Load Balancers like Amazon ELB\n#httpclient4.request_sent_retry_enabled=false\n\n# Idle connection timeout (Milliseconds) to apply if the server does not send\n# Keep-Alive headers (default 0)\n# Set this > 0 to compensate for servers that don't send a Keep-Alive header\n# If <= 0, idle timeout will only apply if the server sends a Keep-Alive header\n#httpclient4.idletimeout=0\n\n# Check connections if the elapsed time (Milliseconds) since the last\n# use of the connection exceed this value\n#httpclient4.validate_after_inactivity=1700\n\n# TTL (in Milliseconds) represents an absolute value.\n# No matter what, the connection will not be re-used beyond its TTL.\n#httpclient4.time_to_live=2000\n\n# Max size in bytes of PUT body to retain in result sampler.\n# Bigger results will be clipped.\n#httpclient4.max_body_retain_size=32768\n\n#---------------------------------------------------------------------------\n# HTTP Cache Manager configuration\n#---------------------------------------------------------------------------\n#\n# Space or comma separated list of methods that can be cached\n#cacheable_methods=GET\n# N.B. This property is currently a temporary solution for Bug 56162\n\n# Since 2.12, JMeter does not create anymore a Sample Result with 204 response\n# code for a resource found in cache which is inline with what browser do.\n#cache_manager.cached_resource_mode=RETURN_NO_SAMPLE\n\n# You can choose between 3 modes:\n# RETURN_NO_SAMPLE (default)\n# RETURN_200_CACHE\n# RETURN_CUSTOM_STATUS\n\n# Those mode have the following behaviours:\n# RETURN_NO_SAMPLE:\n# this mode returns no Sample Result, it has no additional configuration\n\n# RETURN_200_CACHE:\n# this mode will return Sample Result with response code to 200 and\n# response message to \"(ex cache)\", you can modify response message by setting\n# RETURN_200_CACHE.message=(ex cache)\n\n# RETURN_CUSTOM_STATUS:\n# This mode lets you select what response code and message you want to return,\n# if you use this mode you need to set those properties\n# RETURN_CUSTOM_STATUS.code=\n# RETURN_CUSTOM_STATUS.message=\n\n#---------------------------------------------------------------------------\n# Results file configuration\n#---------------------------------------------------------------------------\n\n# This section helps determine how result data will be saved.\n# The commented out values are the defaults.\n\n# legitimate values: xml, csv, db.  Only xml and csv are currently supported.\n#jmeter.save.saveservice.output_format=csv\n\n# The below properties are true when field should be saved; false otherwise\n#\n# assertion_results_failure_message only affects CSV output\n#jmeter.save.saveservice.assertion_results_failure_message=true\n#\n# legitimate values: none, first, all\n#jmeter.save.saveservice.assertion_results=none\n#\n#jmeter.save.saveservice.data_type=true\n#jmeter.save.saveservice.label=true\n#jmeter.save.saveservice.response_code=true\n# response_data is not currently supported for CSV output\n#jmeter.save.saveservice.response_data=false\n# Save ResponseData for failed samples\n#jmeter.save.saveservice.response_data.on_error=false\n#jmeter.save.saveservice.response_message=true\n#jmeter.save.saveservice.successful=true\n#jmeter.save.saveservice.thread_name=true\n#jmeter.save.saveservice.time=true\n#jmeter.save.saveservice.subresults=true\n#jmeter.save.saveservice.assertions=true\n#jmeter.save.saveservice.latency=true\n# Only available with HttpClient4\n#jmeter.save.saveservice.connect_time=true\n#jmeter.save.saveservice.samplerData=false\n#jmeter.save.saveservice.responseHeaders=false\n#jmeter.save.saveservice.requestHeaders=false\n#jmeter.save.saveservice.encoding=false\n#jmeter.save.saveservice.bytes=true\n# Only available with HttpClient4\n#jmeter.save.saveservice.sent_bytes=true\n#jmeter.save.saveservice.url=false\n#jmeter.save.saveservice.filename=false\n#jmeter.save.saveservice.hostname=false\n#jmeter.save.saveservice.thread_counts=true\n#jmeter.save.saveservice.sample_count=false\n#jmeter.save.saveservice.idle_time=true\n\n# Timestamp format - this only affects CSV output files\n# legitimate values: none, ms, or a format suitable for SimpleDateFormat\n#jmeter.save.saveservice.timestamp_format=ms\n#jmeter.save.saveservice.timestamp_format=yyyy/MM/dd HH:mm:ss.SSS\n\n# For use with Comma-separated value (CSV) files or other formats\n# where the fields' values are separated by specified delimiters.\n# Default:\n#jmeter.save.saveservice.default_delimiter=,\n# For TAB, one can use:\n#jmeter.save.saveservice.default_delimiter=\\t\n\n# Only applies to CSV format files:\n# Print field names as first line in CSV\n#jmeter.save.saveservice.print_field_names=true\n\n# Optional list of JMeter variable names whose values are to be saved in the result data files.\n# Use commas to separate the names. For example:\n#sample_variables=SESSION_ID,REFERENCE\n# N.B. The current implementation saves the values in XML as attributes,\n# so the names must be valid XML names.\n# By default JMeter sends the variable to all servers\n# to ensure that the correct data is available at the client.\n\n# Optional xml processing instruction for line 2 of the file:\n# Example:\n#jmeter.save.saveservice.xml_pi=<?xml-stylesheet type=\"text/xsl\" href=\"../extras/jmeter-results-detail-report.xsl\"?>\n# Default value:\n#jmeter.save.saveservice.xml_pi=\n\n# Prefix used to identify filenames that are relative to the current base\n#jmeter.save.saveservice.base_prefix=~/\n\n# AutoFlush on each line written in XML or CSV output\n# Setting this to true will result in less test results data loss in case of Crash\n# but with impact on performances, particularly for intensive tests (low or no pauses)\n# Since JMeter 2.10, this is false by default\n#jmeter.save.saveservice.autoflush=false\n\n#---------------------------------------------------------------------------\n# Settings that affect SampleResults\n#---------------------------------------------------------------------------\n\n# Save the start time stamp instead of the end\n# This also affects the timestamp stored in result files\nsampleresult.timestamp.start=true\n\n# Whether to use System.nanoTime() - otherwise only use System.currentTimeMillis()\n#sampleresult.useNanoTime=true\n\n# Use a background thread to calculate the nanoTime offset\n# Set this to <= 0 to disable the background thread\n#sampleresult.nanoThreadSleep=5000\n\n#---------------------------------------------------------------------------\n# Upgrade property\n#---------------------------------------------------------------------------\n\n# File that holds a record of name changes for backward compatibility issues\nupgrade_properties=/bin/upgrade.properties\n\n#---------------------------------------------------------------------------\n# JMeter Test Script recorder configuration\n#\n# N.B. The element was originally called the Proxy recorder, which is why the\n# properties have the prefix \"proxy\".\n#---------------------------------------------------------------------------\n\n# If the recorder detects a gap of at least 5s (default) between HTTP requests,\n# it assumes that the user has clicked a new URL\n#proxy.pause=5000\n\n# Add numeric prefix to Sampler names (default true)\n#proxy.number.requests=true\n\n# List of URL patterns that will be added to URL Patterns to exclude\n# Separate multiple lines with ;\n#proxy.excludes.suggested=.*\\\\.(bmp|css|js|gif|ico|jpe?g|png|swf|woff|woff2)\n\n# Change the default HTTP Sampler (currently HttpClient4)\n# Java:\n#jmeter.httpsampler=HTTPSampler\n#or\n#jmeter.httpsampler=Java\n#\n# HttpClient4.x\n#jmeter.httpsampler=HttpClient4\n\n# By default JMeter tries to be more lenient with RFC2616 redirects and allows\n# relative paths.\n# If you want to test strict conformance, set this value to true\n# When the property is true, JMeter follows http://tools.ietf.org/html/rfc3986#section-5.2\n#jmeter.httpclient.strict_rfc2616=false\n\n# Default content-type include filter to use\n#proxy.content_type_include=text/html|text/plain|text/xml\n# Default content-type exclude filter to use\n#proxy.content_type_exclude=image/.*|text/css|application/.*\n\n# Default headers to remove from Header Manager elements\n# (Cookie and Authorization are always removed)\n#proxy.headers.remove=If-Modified-Since,If-None-Match,Host\n\n# Binary content-type handling\n# These content-types will be handled by saving the request in a file:\n#proxy.binary.types=application/x-amf,application/x-java-serialized-object\n# The files will be saved in this directory:\n#proxy.binary.directory=user.dir\n# The files will be created with this file filesuffix:\n#proxy.binary.filesuffix=.binary\n\n#---------------------------------------------------------------------------\n# Test Script Recorder certificate configuration\n#---------------------------------------------------------------------------\n\n#proxy.cert.directory=<JMeter bin directory>\n#proxy.cert.file=proxyserver.jks\n#proxy.cert.type=JKS\n#proxy.cert.keystorepass=password\n#proxy.cert.keypassword=password\n#proxy.cert.factory=SunX509\n# define this property if you wish to use your own keystore\n#proxy.cert.alias=<none>\n# The default validity for certificates created by JMeter\n#proxy.cert.validity=7\n# Use dynamic key generation (if supported by JMeter/JVM)\n# If false, will revert to using a single key with no certificate\n#proxy.cert.dynamic_keys=true\n\n#---------------------------------------------------------------------------\n# Test Script Recorder miscellaneous configuration\n#---------------------------------------------------------------------------\n\n# Whether to attempt disabling of samples that resulted from redirects\n# where the generated samples use auto-redirection\n#proxy.redirect.disabling=true\n\n# SSL configuration\n#proxy.ssl.protocol=TLS\n\n#---------------------------------------------------------------------------\n# JMeter Proxy configuration\n#---------------------------------------------------------------------------\n# use command-line flags for user-name and password\n#http.proxyDomain=NTLM domain, if required by HTTPClient sampler\n\n#---------------------------------------------------------------------------\n# HTTPSampleResponse Parser configuration\n#---------------------------------------------------------------------------\n\n# Space-separated list of parser groups\nHTTPResponse.parsers=htmlParser wmlParser cssParser\n# for each parser, there should be a parser.types and a parser.className property\n\n# CSS Parser based on ph-css\ncssParser.className=org.apache.jmeter.protocol.http.parser.CssParser\ncssParser.types=text/css\n\n# CSS parser LRU cache size\n# This cache stores the URLs found in a CSS to avoid continuously parsing the CSS\n# By default the cache size is 400\n# It can be disabled by setting its value to 0\n#css.parser.cache.size=400\n\n# Let the CSS Parser ignore all css errors\n#css.parser.ignore_all_css_errors=true\n\n#---------------------------------------------------------------------------\n# HTML Parser configuration\n#---------------------------------------------------------------------------\n\n# Define the HTML parser to be used.\n# Default parser:\n# This new parser (since 2.10) should perform better than all others\n# see https://bz.apache.org/bugzilla/show_bug.cgi?id=55632\n# Do not comment this property\nhtmlParser.className=org.apache.jmeter.protocol.http.parser.LagartoBasedHtmlParser\n\n# Other parsers:\n# Default parser before 2.10\n#htmlParser.className=org.apache.jmeter.protocol.http.parser.JTidyHTMLParser\n# Note that Regexp extractor may detect references that have been commented out.\n# In many cases it will work OK, but you should be aware that it may generate\n# additional references.\n#htmlParser.className=org.apache.jmeter.protocol.http.parser.RegexpHTMLParser\n# This parser is based on JSoup, it should be the most accurate but less\n# performant than LagartoBasedHtmlParser\n#htmlParser.className=org.apache.jmeter.protocol.http.parser.JsoupBasedHtmlParser\n\n#Used by HTTPSamplerBase to associate htmlParser with content types below\nhtmlParser.types=text/html application/xhtml+xml application/xml text/xml\n\n#---------------------------------------------------------------------------\n# WML Parser configuration\n#---------------------------------------------------------------------------\n\nwmlParser.className=org.apache.jmeter.protocol.http.parser.RegexpHTMLParser\n\n#Used by HTTPSamplerBase to associate wmlParser with content types below\nwmlParser.types=text/vnd.wap.wml\n\n#---------------------------------------------------------------------------\n# Remote batching configuration\n#---------------------------------------------------------------------------\n# How is Sample sender implementations configured:\n# - true (default) means client configuration will be used\n# - false means server configuration will be used\n#sample_sender_client_configured=true\n\n# By default when Stripping modes are used JMeter since 3.1 will strip\n# response even for SampleResults in error.\n# If you want to revert to previous behaviour (no stripping of Responses in error)\n# set this property to false\n#sample_sender_strip_also_on_error=true\n\n# Remote batching support\n# Since JMeter 2.9, default is MODE_STRIPPED_BATCH, which returns samples in\n# batch mode (every 100 samples or every minute by default)\n# Note also that MODE_STRIPPED_BATCH strips response data from SampleResult, so if you need it change to\n# another mode\n# Batch returns samples in batches\n# Statistical returns sample summary statistics\n# mode can also be the class name of an implementation of org.apache.jmeter.samplers.SampleSender\n#mode=Standard\n#mode=Batch\n#mode=Statistical\n#Set to true to key statistical samples on threadName rather than threadGroup\n#key_on_threadname=false\n#mode=Stripped\n#mode=StrippedBatch\n#mode=org.example.load.MySampleSender\n#\n#num_sample_threshold=100\n# Value is in milliseconds\n#time_threshold=60000\n#\n# Asynchronous sender; uses a queue and background worker process to return the samples\n#mode=Asynch\n# default queue size\n#asynch.batch.queue.size=100\n# Same as Asynch but strips response data from SampleResult\n#mode=StrippedAsynch\n#\n# DiskStore: Serialises the samples to disk, rather than saving in memory\n#mode=DiskStore\n# Same as DiskStore but strips response data from SampleResult\n#mode=StrippedDiskStore\n# Note: the mode is currently resolved on the client;\n# other properties (e.g. time_threshold) are resolved on the server.\n\n#---------------------------------------------------------------------------\n# JDBC Request configuration\n#---------------------------------------------------------------------------\n\n# String used to indicate a null value\n#jdbcsampler.nullmarker=]NULL[\n#\n# Max size of BLOBs and CLOBs to store in JDBC sampler. Result will be cut off\n#jdbcsampler.max_retain_result_size=65536\n\n# Database validation query\n# based in https://stackoverflow.com/questions/10684244/dbcp-validationquery-for-different-databases list\njdbc.config.check.query=select 1 from INFORMATION_SCHEMA.SYSTEM_USERS|select 1 from dual|select 1 from sysibm.sysdummy1|select 1|select 1 from rdb$database\njdbc.config.jdbc.driver.class=com.mysql.jdbc.Driver|org.postgresql.Driver|oracle.jdbc.OracleDriver|com.ingres.jdbc.IngresDriver|com.microsoft.sqlserver.jdbc.SQLServerDriver|com.microsoft.jdbc.sqlserver.SQLServerDriver|org.apache.derby.jdbc.ClientDriver|org.hsqldb.jdbc.JDBCDriver|com.ibm.db2.jcc.DB2Driver|org.apache.derby.jdbc.ClientDriver|org.h2.Driver|org.firebirdsql.jdbc.FBDrivery|org.mariadb.jdbc.Driver|org.sqlite.JDBC|net.sourceforge.jtds.jdbc.Driver\n\n#---------------------------------------------------------------------------\n# OS Process Sampler configuration\n#---------------------------------------------------------------------------\n# Polling to see if process has finished its work, used when a timeout is configured on sampler\n#os_sampler.poll_for_timeout=100\n\n#---------------------------------------------------------------------------\n# TCP Sampler configuration\n#---------------------------------------------------------------------------\n\n# The default handler class\n#tcp.handler=TCPClientImpl\n#\n# eolByte = byte value for end of line\n# set this to a value outside the range -128 to +127 to skip eol checking\n#tcp.eolByte=1000\n#\n# TCP Charset, used by org.apache.jmeter.protocol.tcp.sampler.TCPClientImpl\n# default to Platform defaults charset as returned by Charset.defaultCharset().name()\n#tcp.charset=\n#\n# status.prefix and suffix = strings that enclose the status response code\n#tcp.status.prefix=Status=\n#tcp.status.suffix=.\n#\n# status.properties = property file to convert codes to messages\n#tcp.status.properties=mytestfiles/tcpstatus.properties\n\n# The length prefix used by LengthPrefixedBinaryTCPClientImpl implementation\n# defaults to 2 bytes.\n#tcp.binarylength.prefix.length=2\n\n#---------------------------------------------------------------------------\n# Summariser - Generate Summary Results - configuration (mainly applies to non-GUI mode)\n#---------------------------------------------------------------------------\n#\n# Comment the following property to disable the default non-GUI summariser\n# [or change the value to rename it]\n# (applies to non-GUI mode only)\nsummariser.name=summary\n#\n# interval between summaries (in seconds) default 30 seconds\n#summariser.interval=30\n#\n# Write messages to log file\n#summariser.log=true\n#\n# Write messages to System.out\n#summariser.out=true\n\n# Ignore SampleResults generated by TransactionControllers\n# defaults to true\n#summariser.ignore_transaction_controller_sample_result=true\n\n\n#---------------------------------------------------------------------------\n# Aggregate Report and Aggregate Graph - configuration\n#---------------------------------------------------------------------------\n#\n# Percentiles to display in reports\n# Can be float value between 0 and 100\n# First percentile to display, defaults to 90%\n#aggregate_rpt_pct1=90\n# Second percentile to display, defaults to 95%\n#aggregate_rpt_pct2=95\n# Second percentile to display, defaults to 99%\n#aggregate_rpt_pct3=99\n\n#---------------------------------------------------------------------------\n# BackendListener - configuration\n#---------------------------------------------------------------------------\n#\n# Backend metrics window mode (fixed=fixed-size window, timed=time boxed)\n#backend_metrics_window_mode=fixed\n# Backend metrics sliding window size for Percentiles, Min, Max\n#backend_metrics_window=100\n\n# Backend metrics sliding window size for Percentiles, Min, Max\n# when backend_metrics_window_mode is timed\n# Setting this value too high can lead to OOM\n#backend_metrics_large_window=5000\n\n########################\n# Graphite Backend\n########################\n# Send interval in second\n# Defaults to 1 second\n#backend_graphite.send_interval=1\n\n########################\n# Influx Backend\n########################\n\n# Send interval in second\n# Defaults to 5 seconds\n#backend_influxdb.send_interval=5\n#Influxdb timeouts\n#backend_influxdb.connection_timeout=1000\n#backend_influxdb.socket_timeout=3000\n#backend_influxdb.connection_request_timeout=100\n\n#---------------------------------------------------------------------------\n# BeanShell configuration\n#---------------------------------------------------------------------------\n\n# BeanShell Server properties\n#\n# Define the port number as non-zero to start the http server on that port\n#beanshell.server.port=9000\n# The telnet server will be started on the next port\n\n#\n# Define the server initialisation file\nbeanshell.server.file=../extras/startup.bsh\n\n#\n# Define a file to be processed at startup\n# This is processed using its own interpreter.\n#beanshell.init.file=\n\n#\n# Define the intialisation files for BeanShell Sampler, Function and other BeanShell elements\n# N.B. Beanshell test elements do not share interpreters.\n#      Each element in each thread has its own interpreter.\n#      This is retained between samples.\n#beanshell.sampler.init=BeanShellSampler.bshrc\n#beanshell.function.init=BeanShellFunction.bshrc\n#beanshell.assertion.init=BeanShellAssertion.bshrc\n#beanshell.listener.init=etc\n#beanshell.postprocessor.init=etc\n#beanshell.preprocessor.init=etc\n#beanshell.timer.init=etc\n\n# The file BeanShellListeners.bshrc contains sample definitions\n# of Test and Thread Listeners.\n\n#---------------------------------------------------------------------------\n# Groovy function\n#---------------------------------------------------------------------------\n\n#Path to Groovy file containing utility functions to make available to __groovy function\n#groovy.utilities=\n\n# Example\n#groovy.utilities=bin/utility.groovy\n\n#---------------------------------------------------------------------------\n# MailerModel configuration\n#---------------------------------------------------------------------------\n\n# Number of successful samples before a message is sent\n#mailer.successlimit=2\n#\n# Number of failed samples before a message is sent\n#mailer.failurelimit=2\n\n#---------------------------------------------------------------------------\n# CSVRead configuration\n#---------------------------------------------------------------------------\n\n# CSVRead delimiter setting (default \",\")\n# Make sure that there are no trailing spaces or tabs after the delimiter\n# characters, or these will be included in the list of valid delimiters\n#csvread.delimiter=,\n#csvread.delimiter=;\n#csvread.delimiter=!\n#csvread.delimiter=~\n# The following line has a tab after the =\n#csvread.delimiter=\t\n\n#---------------------------------------------------------------------------\n# __time() function configuration\n#\n# The properties below can be used to redefine the default formats\n#---------------------------------------------------------------------------\n#time.YMD=yyyyMMdd\n#time.HMS=HHmmss\n#time.YMDHMS=yyyyMMdd-HHmmss\n#time.USER1=\n#time.USER2=\n\n#---------------------------------------------------------------------------\n# CSV DataSet configuration\n#---------------------------------------------------------------------------\n\n# String to return at EOF (if recycle not used)\n#csvdataset.eofstring=<EOF>\n#list in https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html\ncsvdataset.file.encoding_list=UTF-8|UTF-16|ISO-8859-15|US-ASCII\n\n\n#---------------------------------------------------------------------------\n# LDAP Sampler configuration\n#---------------------------------------------------------------------------\n# Maximum number of search results returned by a search that will be sorted\n# to guarantee a stable ordering (if more results then this limit are returned\n# then no sorting is done). Set to 0 to turn off all sorting, in which case\n# \"Equals\" response assertions will be very likely to fail against search results.\n#\n#ldapsampler.max_sorted_results=1000\n\n# Number of characters to log for each of three sections (starting matching section, diff section,\n#   ending matching section where not all sections will appear for all diffs) diff display when an Equals\n#   assertion fails. So a value of 100 means a maximum of 300 characters of diff text will be displayed\n#   (+ a number of extra characters like \"...\" and \"[[[\"/\"]]]\" which are used to decorate it).\n#assertion.equals_section_diff_len=100\n# test written out to log to signify start/end of diff delta\n#assertion.equals_diff_delta_start=[[[\n#assertion.equals_diff_delta_end=]]]\n\n#---------------------------------------------------------------------------\n# Miscellaneous configuration\n#---------------------------------------------------------------------------\n# Used to control what happens when you start a test and\n# have listeners that could overwrite existing result files\n# Possible values:\n# ASK : Ask user\n# APPEND : Append results to existing file\n# DELETE : Delete existing file and start a new file\n#resultcollector.action_if_file_exists=ASK\n\n# If defined, then start the mirror server on the port\n#mirror.server.port=8081\n\n# ORO PatternCacheLRU size\n#oro.patterncache.size=1000\n\n#TestBeanGui\n#\n#propertyEditorSearchPath=null\n\n# Turn expert mode on/off: expert mode will show expert-mode beans and properties\n#jmeter.expertMode=true\n\n# Max size of bytes stored in memory per SampleResult\n# Ensure you don't exceed max capacity of a Java Array and remember\n# that the higher it is, the higher JMeter will consume heap\n# Defaults to 0, which means no truncation\n#httpsampler.max_bytes_to_store_per_request=0\n\n# Max size of buffer in bytes used when reading responses\n# Defaults to 64k\n#httpsampler.max_buffer_size=66560\n\n# Maximum redirects to follow in a single sequence (default 20)\n#httpsampler.max_redirects=20\n# Maximum frame/iframe nesting depth (default 5)\n#httpsampler.max_frame_depth=5\n\n# Revert to BUG 51939 behaviour (no separate container for embedded resources) by setting the following false:\n#httpsampler.separate.container=true\n\n# If embedded resources download fails due to missing resources or other reasons, if this property is true\n# Parent sample will not be marked as failed\n#httpsampler.ignore_failed_embedded_resources=false\n\n#keep alive time for the parallel download threads (in seconds)\n#httpsampler.parallel_download_thread_keepalive_inseconds=60\n\n# Don't keep the embedded resources response data : just keep the size and the md5\n# default to false\n#httpsampler.embedded_resources_use_md5=false\n\n# List of extra HTTP methods that should be available in select box\n#httpsampler.user_defined_methods=VERSION-CONTROL,REPORT,CHECKOUT,CHECKIN,UNCHECKOUT,MKWORKSPACE,UPDATE,LABEL,MERGE,BASELINE-CONTROL,MKACTIVITY\n\n# The encoding to be used if none is provided (default ISO-8859-1)\n#sampleresult.default.encoding=ISO-8859-1\n\n# CookieManager behaviour - should cookies with null/empty values be deleted?\n# Default is true. Use false to revert to original behaviour\n#CookieManager.delete_null_cookies=true\n\n# CookieManager behaviour - should variable cookies be allowed?\n# Default is true. Use false to revert to original behaviour\n#CookieManager.allow_variable_cookies=true\n\n# CookieManager behaviour - should Cookies be stored as variables?\n# Default is false\n#CookieManager.save.cookies=false\n\n# CookieManager behaviour - prefix to add to cookie name before storing it as a variable\n# Default is COOKIE_; to remove the prefix, define it as one or more spaces\n#CookieManager.name.prefix=\n\n# CookieManager behaviour - check received cookies are valid before storing them?\n# Default is true. Use false to revert to previous behaviour\n#CookieManager.check.cookies=true\n\n# Netscape HTTP Cookie file\ncookies=cookies\n\n# Ability to switch to Nashorn as default Javascript Engine used by IfController and __javaScript function\n# JMeter works as following:\n# - JDK >= 8 and javascript.use_rhino=false or not set : Nashorn\n# - JDK >= 8 and javascript.use_rhino=true: Rhino\n# If you want to use Rhino on JDK8, set this property to true\n#javascript.use_rhino=false\n\n# Number of milliseconds to wait for a thread to stop\n#jmeterengine.threadstop.wait=5000\n\n#Whether to invoke System.exit(0) in server exit code after stopping RMI\n#jmeterengine.remote.system.exit=false\n\n# Whether to call System.exit(1) on failure to stop threads in non-GUI mode.\n# This only takes effect if the test was explicitly requested to stop.\n# If this is disabled, it may be necessary to kill the JVM externally\n#jmeterengine.stopfail.system.exit=true\n\n# Whether to force call System.exit(0) at end of test in non-GUI mode, even if\n# there were no failures and the test was not explicitly asked to stop.\n# Without this, the JVM may never exit if there are other threads spawned by\n# the test which never exit.\n#jmeterengine.force.system.exit=false\n\n# How long to pause (in ms) in the daemon thread before reporting that the JVM has failed to exit.\n# If the value is <= 0, the JMeter does not start the daemon thread\n#jmeter.exit.check.pause=2000\n\n# If running non-GUI, then JMeter listens on the following port for a shutdown message.\n# To disable, set the port to 1000 or less.\n#jmeterengine.nongui.port=4445\n#\n# If the initial port is busy, keep trying until this port is reached\n# (to disable searching, set the value less than or equal to the .port property)\n#jmeterengine.nongui.maxport=4455\n\n# How often to check for shutdown during ramp-up (milliseconds)\n#jmeterthread.rampup.granularity=1000\n\n#Should JMeter expand the tree when loading a test plan?\n# default value is false since JMeter 2.7\n#onload.expandtree=false\n\n#JSyntaxTextArea configuration\n#jsyntaxtextarea.wrapstyleword=true\n#jsyntaxtextarea.linewrap=true\n#jsyntaxtextarea.codefolding=true\n# Set 0 to disable undo feature in JSyntaxTextArea\n#jsyntaxtextarea.maxundos=50\n# Change the font on the (JSyntax) Text Areas. (Useful for HiDPI screens)\n#jsyntaxtextarea.font.family=Hack\n#jsyntaxtextarea.font.size=14\n\n# Set this to false to disable the use of JSyntaxTextArea for the Console Logger panel\n#loggerpanel.usejsyntaxtext=true\n\n# Maximum size of HTML page that can be displayed; default=10 mbytes\n# Set to 0 to disable the size check and display the whole response\n#view.results.tree.max_size=10485760\n\n# Order of Renderers in View Results Tree\n# Note full class names should be used for non jmeter core renderers\n# For JMeter core renderers, class names start with . and are automatically\n# prefixed with org.apache.jmeter.visualizers\nview.results.tree.renderers_order=.RenderAsText,.RenderAsRegexp,.RenderAsBoundaryExtractor,.RenderAsCssJQuery,.RenderAsXPath,org.apache.jmeter.extractor.json.render.RenderAsJsonRenderer,.RenderAsHTML,.RenderAsHTMLFormatted,.RenderAsHTMLWithEmbedded,.RenderAsDocument,.RenderAsJSON,.RenderAsXML\n\n# Maximum number of results in the results tree\n# Set to 0 to store all results (might consume a lot of memory)\n#view.results.tree.max_results=500\n\n# Maximum size of Document that can be parsed by Tika engine; defaut=10 * 1024 * 1024 (10MB)\n# Set to 0 to disable the size check\n#document.max_size=0\n\n#JMS options\n# Enable the following property to stop JMS Point-to-Point Sampler from using\n# the properties java.naming.security.[principal|credentials] when creating the queue connection\n#JMSSampler.useSecurity.properties=false\n\n# Set the following value to true in order to skip the delete confirmation dialogue\n#confirm.delete.skip=false\n\n# Used by JSR223 elements\n# Size of compiled scripts cache\n#jsr223.compiled_scripts_cache_size=100\n\n#---------------------------------------------------------------------------\n# Classpath configuration\n#---------------------------------------------------------------------------\n\n# List of directories (separated by ;) to search for additional JMeter plugin classes,\n# for example new GUI elements and samplers.\n# Any jar file in such a directory will be automatically included,\n# jar files in sub directories are ignored.\n# The given value is in addition to any jars found in the lib/ext directory.\n# Do not use this for utility or plugin dependency jars.\n#search_paths=/app1/lib;/app2/lib\n\n# List of directories that JMeter will search for utility and plugin dependency classes.\n# Use your platform path separator to separate multiple paths.\n# Any jar file in such a directory will be automatically included,\n# jar files in sub directories are ignored.\n# The given value is in addition to any jars found in the lib directory.\n# All entries will be added to the class path of the system class loader\n# and also to the path of the JMeter internal loader.\n# Paths with spaces may cause problems for the JVM\n#user.classpath=../classes;../lib\n\n# List of directories (separated by ;) that JMeter will search for utility\n# and plugin dependency classes.\n# Any jar file in such a directory will be automatically included,\n# jar files in sub directories are ignored.\n# The given value is in addition to any jars found in the lib directory\n# or given by the user.classpath property.\n# All entries will be added to the path of the JMeter internal loader only.\n# For plugin dependencies this property should be used instead of user.classpath.\n#plugin_dependency_paths=../dependencies/lib;../app1/;../app2/\n\n# Classpath finder\n# ================\n# The classpath finder currently needs to load every single JMeter class to find\n# the classes it needs.\n# For non-GUI mode, it's only necessary to scan for Function classes, but all classes\n# are still loaded.\n# All current Function classes include \".function.\" in their name,\n# and none include \".gui.\" in the name, so the number of unwanted classes loaded can be\n# reduced by checking for these. However, if a valid function class name does not match\n# these restrictions, it will not be loaded. If problems are encountered, then comment\n# or change the following properties:\nclassfinder.functions.contain=.functions.\nclassfinder.functions.notContain=.gui.\n\n\n#---------------------------------------------------------------------------\n# Additional property files to load\n#---------------------------------------------------------------------------\n\n# Should JMeter automatically load additional JMeter properties?\n# File name to look for (comment to disable)\nuser.properties=user.properties\n\n# Should JMeter automatically load additional system properties?\n# File name to look for (comment to disable)\nsystem.properties=system.properties\n\n# Comma separated list of files that contain reference to templates and their description\n# Path must be relative to JMeter root folder\n#template.files=/bin/templates/templates.xml\n\n\n#---------------------------------------------------------------------------\n# Thread Group Validation feature\n#---------------------------------------------------------------------------\n\n# Validation is the name of the feature used to rapidly validate a Thread Group runs fine\n# Default implementation is org.apache.jmeter.gui.action.validation.TreeClonerForValidation\n# It runs validation without timers, with 1 thread, 1 iteration and Startup Delay set to 0\n# You can implement your own policy that must extend org.apache.jmeter.engine.TreeCloner\n# JMeter will instantiate it and use it to create the Tree used to run validation on Thread Group\n#testplan_validation.tree_cloner_class=org.apache.jmeter.validation.ComponentTreeClonerForValidation\n\n# Number of threads to use to validate a Thread Group\n#testplan_validation.nb_threads_per_thread_group=1\n\n# Ignore BackendListener when validating the thread group of plan\n#testplan_validation.ignore_backends=true\n\n# Ignore timers when validating the thread group of plan\n#testplan_validation.ignore_timers=true\n\n# Number of iterations to use to validate a Thread Group\n#testplan_validation.number_iterations=1\n\n# Force throuput controllers that work in percentage mode to be a 100%\n# Disabled by default\n#testplan_validation.tpc_force_100_pct=false\n\n#---------------------------------------------------------------------------\n# Think Time configuration\n#---------------------------------------------------------------------------\n\n#\n# Apply a factor on computed pauses by the following Timers:\n# - Gaussian Random Timer\n# - Uniform Random Timer\n# - Poisson Random Timer\n#\n#timer.factor=1.0f\n\n# Default implementation that create the Timer structure to add to Test Plan\n# Implementation of interface org.apache.jmeter.gui.action.thinktime.ThinkTimeCreator\n#think_time_creator.impl=org.apache.jmeter.thinktime.DefaultThinkTimeCreator\n\n# Default Timer GUI class added to Test Plan by DefaultThinkTimeCreator\n#think_time_creator.default_timer_implementation=org.apache.jmeter.timers.gui.UniformRandomTimerGui\n\n# Default constant pause of Timer\n#think_time_creator.default_constant_pause=1000\n\n# Default range pause of Timer\n#think_time_creator.default_range=100\n\n\n# Change this parameter if you want to override the APDEX satisfaction threshold.\njmeter.reportgenerator.apdex_satisfied_threshold=500\n\n# Change this parameter if you want to override the APDEX tolerance threshold.\njmeter.reportgenerator.apdex_tolerated_threshold=1500\n\n#---------------------------------------------------------------------------\n# Naming Policy configuration\n#---------------------------------------------------------------------------\n\n# Prefix used when naming elements\n#naming_policy.prefix=\n# Suffix used when naming elements\n#naming_policy.suffix=\n\n# Implementation of interface org.apache.jmeter.gui.action.TreeNodeNamingPolicy\n#naming_policy.impl=org.apache.jmeter.gui.action.impl.DefaultTreeNodeNamingPolicy\n\n#---------------------------------------------------------------------------\n# Help Documentation\n#---------------------------------------------------------------------------\n\n# Switch that allows using Local documentation opened in JMeter GUI\n# By default we use Online documentation opened in Browser\n#help.local=false"
  },
  {
    "path": "src/test/resources/bin/saveservice.properties",
    "content": "#---------------------------------------------------------\n#         SAVESERVICE PROPERTIES - JMETER INTERNAL USE ONLY\n#---------------------------------------------------------\n\n##   Licensed to the Apache Software Foundation (ASF) under one or more\n##   contributor license agreements.  See the NOTICE file distributed with\n##   this work for additional information regarding copyright ownership.\n##   The ASF licenses this file to You under the Apache License, Version 2.0\n##   (the \"License\"); you may not use this file except in compliance with\n##   the License.  You may obtain a copy of the License at\n## \n##       http://www.apache.org/licenses/LICENSE-2.0\n## \n##   Unless required by applicable law or agreed to in writing, software\n##   distributed under the License is distributed on an \"AS IS\" BASIS,\n##   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n##   See the License for the specific language governing permissions and\n##   limitations under the License.\n\n# This file is used to define how XStream (de-)serializes classnames\n# in JMX test plan files.\n\n#      FOR JMETER INTERNAL USE ONLY\n\n#---------------------------------------------------------\n\n# N.B. To ensure backward compatibility, please do NOT change or delete any entries\n\n# New entries can be added as necessary.\n#\n# Note that keys starting with an underscore are special,\n# and are not used as aliases.\n#\n# Please keep the entries in alphabetical order within the sections\n# to reduce the likelihood of duplicates\n#\n# version number of this file is now computed by a sha1 sum, so no need for\n# an explicit _file_version property anymore.\n#\n# For this sha1 sum we ignore every newline character. It can be computed\n# by the following command:\n#\n# cat bin/saveservice.properties | perl -ne 'chomp; print' | sha1sum\n#\n# Be aware, that every change in this file will change the sha1 sum!\n#\n# Conversion version (for JMX output files)\n# Must be updated if the file has been changed since the previous release\n# Format is:\n# Save service version=JMeter version at which change occurred\n# 1.7 = 2.1.1\n# 1.8 = 2.1.2\n# (Some version updates were missed here...)\n# 2.0 = 2.3.1\n# 2.1 = 2.3.2\n# (Some version updates were missed here...)\n# 2.2 = 2.6\n# 2.3 = 2.7\n# 2.4 = 2.9\n# 2.5 = 2.10\n# 2.6 = 2.11\n# 2.7 = 2.12\n# 2.8 = 2.13\n# 2.9 = 2.14\n# 3.1 = 3.1\n# 3.2 = 3.2\n# 3.4 = 3.4\n_version=4.0\n#\n#\n# Character set encoding used to read and write JMeter XML files and CSV results\n#\n_file_encoding=UTF-8\n#\n#---------------------------------------------------------\n#\n# The following properties are used to create aliases\n# [Must all start with capital letter]\n#\nAccessLogSampler=org.apache.jmeter.protocol.http.sampler.AccessLogSampler\nAjpSampler=org.apache.jmeter.protocol.http.sampler.AjpSampler\nAjpSamplerGui=org.apache.jmeter.protocol.http.control.gui.AjpSamplerGui\nAnchorModifier=org.apache.jmeter.protocol.http.modifier.AnchorModifier\nAnchorModifierGui=org.apache.jmeter.protocol.http.modifier.gui.AnchorModifierGui\nArgument=org.apache.jmeter.config.Argument\nArguments=org.apache.jmeter.config.Arguments\nArgumentsPanel=org.apache.jmeter.config.gui.ArgumentsPanel\nAssertionGui=org.apache.jmeter.assertions.gui.AssertionGui\nAssertionVisualizer=org.apache.jmeter.visualizers.AssertionVisualizer\nAuthManager=org.apache.jmeter.protocol.http.control.AuthManager\nAuthorization=org.apache.jmeter.protocol.http.control.Authorization\nAuthPanel=org.apache.jmeter.protocol.http.gui.AuthPanel\nBackendListener=org.apache.jmeter.visualizers.backend.BackendListener\nBackendListenerGui=org.apache.jmeter.visualizers.backend.BackendListenerGui\nBeanShellAssertion=org.apache.jmeter.assertions.BeanShellAssertion\nBeanShellAssertionGui=org.apache.jmeter.assertions.gui.BeanShellAssertionGui\nBeanShellListener=org.apache.jmeter.visualizers.BeanShellListener\nBeanShellPostProcessor=org.apache.jmeter.extractor.BeanShellPostProcessor\nBeanShellPreProcessor=org.apache.jmeter.modifiers.BeanShellPreProcessor\nBeanShellSampler=org.apache.jmeter.protocol.java.sampler.BeanShellSampler\nBeanShellSamplerGui=org.apache.jmeter.protocol.java.control.gui.BeanShellSamplerGui\nBeanShellTimer=org.apache.jmeter.timers.BeanShellTimer\nBoundaryExtractor=org.apache.jmeter.extractor.BoundaryExtractor\nBoundaryExtractorGui=org.apache.jmeter.extractor.gui.BoundaryExtractorGui\nBSFAssertion=org.apache.jmeter.assertions.BSFAssertion\nBSFListener=org.apache.jmeter.visualizers.BSFListener\nBSFPreProcessor=org.apache.jmeter.modifiers.BSFPreProcessor\nBSFPostProcessor=org.apache.jmeter.extractor.BSFPostProcessor\nBSFSampler=org.apache.jmeter.protocol.java.sampler.BSFSampler\nBSFSamplerGui=org.apache.jmeter.protocol.java.control.gui.BSFSamplerGui\nBSFTimer=org.apache.jmeter.timers.BSFTimer\nCacheManager=org.apache.jmeter.protocol.http.control.CacheManager\nCacheManagerGui=org.apache.jmeter.protocol.http.gui.CacheManagerGui\nCompareAssertion=org.apache.jmeter.assertions.CompareAssertion\nComparisonVisualizer=org.apache.jmeter.visualizers.ComparisonVisualizer\nConfigTestElement=org.apache.jmeter.config.ConfigTestElement\nConstantThroughputTimer=org.apache.jmeter.timers.ConstantThroughputTimer\nConstantTimer=org.apache.jmeter.timers.ConstantTimer\nConstantTimerGui=org.apache.jmeter.timers.gui.ConstantTimerGui\nCookie=org.apache.jmeter.protocol.http.control.Cookie\nCookieManager=org.apache.jmeter.protocol.http.control.CookieManager\nCookiePanel=org.apache.jmeter.protocol.http.gui.CookiePanel\nCounterConfig=org.apache.jmeter.modifiers.CounterConfig\nCriticalSectionController=org.apache.jmeter.control.CriticalSectionController\nCriticalSectionControllerGui=org.apache.jmeter.control.gui.CriticalSectionControllerGui\nCounterConfigGui=org.apache.jmeter.modifiers.gui.CounterConfigGui\nCSVDataSet=org.apache.jmeter.config.CSVDataSet\nDebugPostProcessor=org.apache.jmeter.extractor.DebugPostProcessor\nDebugSampler=org.apache.jmeter.sampler.DebugSampler\n# removed in 3.1, class was deleted in r1763837\nDistributionGraphVisualizer=org.apache.jmeter.visualizers.DistributionGraphVisualizer\nDNSCacheManager=org.apache.jmeter.protocol.http.control.DNSCacheManager\nDNSCachePanel=org.apache.jmeter.protocol.http.gui.DNSCachePanel\nDurationAssertion=org.apache.jmeter.assertions.DurationAssertion\nDurationAssertionGui=org.apache.jmeter.assertions.gui.DurationAssertionGui\nPreciseThroughputTimer=org.apache.jmeter.timers.poissonarrivals.PreciseThroughputTimer\n# Should really have been defined as floatProp to agree with other properties\n# No point changing this now\nFloatProperty=org.apache.jmeter.testelement.property.FloatProperty\nForeachController=org.apache.jmeter.control.ForeachController\nForeachControlPanel=org.apache.jmeter.control.gui.ForeachControlPanel\nFtpConfigGui=org.apache.jmeter.protocol.ftp.config.gui.FtpConfigGui\nFTPSampler=org.apache.jmeter.protocol.ftp.sampler.FTPSampler\nFtpTestSamplerGui=org.apache.jmeter.protocol.ftp.control.gui.FtpTestSamplerGui\nGaussianRandomTimer=org.apache.jmeter.timers.GaussianRandomTimer\nGaussianRandomTimerGui=org.apache.jmeter.timers.gui.GaussianRandomTimerGui\nGenericController=org.apache.jmeter.control.GenericController\nGraphAccumVisualizer=org.apache.jmeter.visualizers.GraphAccumVisualizer\nGraphVisualizer=org.apache.jmeter.visualizers.GraphVisualizer\nHeader=org.apache.jmeter.protocol.http.control.Header\nHeaderManager=org.apache.jmeter.protocol.http.control.HeaderManager\nHeaderPanel=org.apache.jmeter.protocol.http.gui.HeaderPanel\nHTMLAssertion=org.apache.jmeter.assertions.HTMLAssertion\nHTMLAssertionGui=org.apache.jmeter.assertions.gui.HTMLAssertionGui\nHTTPArgument=org.apache.jmeter.protocol.http.util.HTTPArgument\nHTTPArgumentsPanel=org.apache.jmeter.protocol.http.gui.HTTPArgumentsPanel\nHTTPFileArg=org.apache.jmeter.protocol.http.util.HTTPFileArg\nHTTPFileArgs=org.apache.jmeter.protocol.http.util.HTTPFileArgs\nHttpDefaultsGui=org.apache.jmeter.protocol.http.config.gui.HttpDefaultsGui\nHtmlExtractor=org.apache.jmeter.extractor.HtmlExtractor\nHtmlExtractorGui=org.apache.jmeter.extractor.gui.HtmlExtractorGui\n# removed in r1039684, probably not released. Not present in r322831 or since.\n#HttpGenericSampler=org.apache.jmeter.protocol.http.sampler.HttpGenericSampler\n# removed in r1039684, probably not released. Not present in r322831 or since.\n#HttpGenericSamplerGui=org.apache.jmeter.protocol.http.control.gui.HttpGenericSamplerGui\nHttpMirrorControl=org.apache.jmeter.protocol.http.control.HttpMirrorControl\nHttpMirrorControlGui=org.apache.jmeter.protocol.http.control.gui.HttpMirrorControlGui\n# r397955 - removed test class. Keep as commented entry for info only.\n#HTTPNullSampler=org.apache.jmeter.protocol.http.sampler.HTTPNullSampler\n# Merge previous 2 HTTP samplers into one\nHTTPSampler_=org.apache.jmeter.protocol.http.sampler.HTTPSampler\nHTTPSampler2_=org.apache.jmeter.protocol.http.sampler.HTTPSampler2\nHTTPSamplerProxy,HTTPSampler,HTTPSampler2=org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy\n# Merge GUIs\nHttpTestSampleGui,HttpTestSampleGui2=org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui\n#HttpTestSampleGui2=org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui2\nIfController=org.apache.jmeter.control.IfController\nIfControllerPanel=org.apache.jmeter.control.gui.IfControllerPanel\nIncludeController=org.apache.jmeter.control.IncludeController\nIncludeControllerGui=org.apache.jmeter.control.gui.IncludeControllerGui\nInterleaveControl=org.apache.jmeter.control.InterleaveControl\nInterleaveControlGui=org.apache.jmeter.control.gui.InterleaveControlGui\nJavaConfig=org.apache.jmeter.protocol.java.config.JavaConfig\nJavaConfigGui=org.apache.jmeter.protocol.java.config.gui.JavaConfigGui\nJavaSampler=org.apache.jmeter.protocol.java.sampler.JavaSampler\nJavaTest=org.apache.jmeter.protocol.java.test.JavaTest\nJavaTestSamplerGui=org.apache.jmeter.protocol.java.control.gui.JavaTestSamplerGui\nJDBCDataSource=org.apache.jmeter.protocol.jdbc.config.DataSourceElement\nJDBCPostProcessor=org.apache.jmeter.protocol.jdbc.processor.JDBCPostProcessor\nJDBCPreProcessor=org.apache.jmeter.protocol.jdbc.processor.JDBCPreProcessor\nJDBCSampler=org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler\n# Renamed to JMSSamplerGui; keep original entry for backwards compatibility\nJMSConfigGui=org.apache.jmeter.protocol.jms.control.gui.JMSConfigGui\nJMSProperties=org.apache.jmeter.protocol.jms.sampler.JMSProperties\nJMSProperty=org.apache.jmeter.protocol.jms.sampler.JMSProperty\nJMSPublisherGui=org.apache.jmeter.protocol.jms.control.gui.JMSPublisherGui\nJMSSampler=org.apache.jmeter.protocol.jms.sampler.JMSSampler\nJMSSamplerGui=org.apache.jmeter.protocol.jms.control.gui.JMSSamplerGui\nJMSSubscriberGui=org.apache.jmeter.protocol.jms.control.gui.JMSSubscriberGui\nJSONPathAssertion=org.apache.jmeter.assertions.JSONPathAssertion\nJSONPathAssertionGui=org.apache.jmeter.assertions.gui.JSONPathAssertionGui\nJSONPostProcessor=org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor\nJSONPostProcessorGui=org.apache.jmeter.extractor.json.jsonpath.gui.JSONPostProcessorGui\n# Removed in r545311 as Jndi no longer present; keep for compat.\nJndiDefaultsGui=org.apache.jmeter.protocol.jms.control.gui.JndiDefaultsGui\nJSR223Assertion=org.apache.jmeter.assertions.JSR223Assertion\nJSR223Listener=org.apache.jmeter.visualizers.JSR223Listener\nJSR223PostProcessor=org.apache.jmeter.extractor.JSR223PostProcessor\nJSR223PreProcessor=org.apache.jmeter.modifiers.JSR223PreProcessor\nJSR223Sampler=org.apache.jmeter.protocol.java.sampler.JSR223Sampler\nJSR223Timer=org.apache.jmeter.timers.JSR223Timer\nJUnitSampler=org.apache.jmeter.protocol.java.sampler.JUnitSampler\nJUnitTestSamplerGui=org.apache.jmeter.protocol.java.control.gui.JUnitTestSamplerGui\nKeystoreConfig=org.apache.jmeter.config.KeystoreConfig\nLDAPArgument=org.apache.jmeter.protocol.ldap.config.gui.LDAPArgument\nLDAPArguments=org.apache.jmeter.protocol.ldap.config.gui.LDAPArguments\nLDAPArgumentsPanel=org.apache.jmeter.protocol.ldap.config.gui.LDAPArgumentsPanel\nLdapConfigGui=org.apache.jmeter.protocol.ldap.config.gui.LdapConfigGui\nLdapExtConfigGui=org.apache.jmeter.protocol.ldap.config.gui.LdapExtConfigGui\nLDAPExtSampler=org.apache.jmeter.protocol.ldap.sampler.LDAPExtSampler\nLdapExtTestSamplerGui=org.apache.jmeter.protocol.ldap.control.gui.LdapExtTestSamplerGui\nLDAPSampler=org.apache.jmeter.protocol.ldap.sampler.LDAPSampler\nLdapTestSamplerGui=org.apache.jmeter.protocol.ldap.control.gui.LdapTestSamplerGui\nLogicControllerGui=org.apache.jmeter.control.gui.LogicControllerGui\nLoginConfig=org.apache.jmeter.config.LoginConfig\nLoginConfigGui=org.apache.jmeter.config.gui.LoginConfigGui\nLoopController=org.apache.jmeter.control.LoopController\nLoopControlPanel=org.apache.jmeter.control.gui.LoopControlPanel\nMailerModel=org.apache.jmeter.reporters.MailerModel\nMailerResultCollector=org.apache.jmeter.reporters.MailerResultCollector\nMailerVisualizer=org.apache.jmeter.visualizers.MailerVisualizer\nMailReaderSampler=org.apache.jmeter.protocol.mail.sampler.MailReaderSampler\nMailReaderSamplerGui=org.apache.jmeter.protocol.mail.sampler.gui.MailReaderSamplerGui\nMD5HexAssertion=org.apache.jmeter.assertions.MD5HexAssertion\nMD5HexAssertionGUI=org.apache.jmeter.assertions.gui.MD5HexAssertionGUI\nModuleController=org.apache.jmeter.control.ModuleController\nModuleControllerGui=org.apache.jmeter.control.gui.ModuleControllerGui\nMongoScriptSampler=org.apache.jmeter.protocol.mongodb.sampler.MongoScriptSampler\nMongoSourceElement=org.apache.jmeter.protocol.mongodb.config.MongoSourceElement\n\n# removed in 3.2, class was deleted in r\nMonitorHealthVisualizer=org.apache.jmeter.visualizers.MonitorHealthVisualizer\n\nNamePanel=org.apache.jmeter.gui.NamePanel\nObsoleteGui=org.apache.jmeter.config.gui.ObsoleteGui\nOnceOnlyController=org.apache.jmeter.control.OnceOnlyController\nOnceOnlyControllerGui=org.apache.jmeter.control.gui.OnceOnlyControllerGui\n# removed in 3.0, class was deleted in r1722962\nParamMask=org.apache.jmeter.protocol.http.modifier.ParamMask\n# removed in 3.0, class was deleted in r1722757\nParamModifier=org.apache.jmeter.protocol.http.modifier.ParamModifier\n# removed in 3.0, class was deleted in r1722757\nParamModifierGui=org.apache.jmeter.protocol.http.modifier.gui.ParamModifierGui\nPoissonRandomTimer=org.apache.jmeter.timers.PoissonRandomTimer\nPoissonRandomTimerGui=org.apache.jmeter.timers.gui.PoissonRandomTimerGui\nPropertyControlGui=org.apache.jmeter.visualizers.PropertyControlGui\nProxyControl=org.apache.jmeter.protocol.http.proxy.ProxyControl\nProxyControlGui=org.apache.jmeter.protocol.http.proxy.gui.ProxyControlGui\nPublisherSampler=org.apache.jmeter.protocol.jms.sampler.PublisherSampler\nRandomControlGui=org.apache.jmeter.control.gui.RandomControlGui\nRandomController=org.apache.jmeter.control.RandomController\nRandomOrderController=org.apache.jmeter.control.RandomOrderController\nRandomOrderControllerGui=org.apache.jmeter.control.gui.RandomOrderControllerGui\nRandomVariableConfig=org.apache.jmeter.config.RandomVariableConfig\nRecordController=org.apache.jmeter.protocol.http.control.gui.RecordController\nRecordingController=org.apache.jmeter.protocol.http.control.RecordingController\n# removed in r1039684, class was deleted in r580452\nReflectionThreadGroup=org.apache.jmeter.threads.ReflectionThreadGroup\nRegexExtractor=org.apache.jmeter.extractor.RegexExtractor\nRegexExtractorGui=org.apache.jmeter.extractor.gui.RegexExtractorGui\nRegExUserParameters=org.apache.jmeter.protocol.http.modifier.RegExUserParameters\nRegExUserParametersGui=org.apache.jmeter.protocol.http.modifier.gui.RegExUserParametersGui\nRemoteListenerWrapper=org.apache.jmeter.samplers.RemoteListenerWrapper\nRemoteSampleListenerWrapper=org.apache.jmeter.samplers.RemoteSampleListenerWrapper\nRemoteTestListenerWrapper=org.apache.jmeter.samplers.RemoteTestListenerWrapper\nRemoteThreadsListenerWrapper=org.apache.jmeter.threads.RemoteThreadsListenerWrapper\nResponseAssertion=org.apache.jmeter.assertions.ResponseAssertion\nRespTimeGraphVisualizer=org.apache.jmeter.visualizers.RespTimeGraphVisualizer\nResultAction=org.apache.jmeter.reporters.ResultAction\nResultActionGui=org.apache.jmeter.reporters.gui.ResultActionGui\nResultCollector=org.apache.jmeter.reporters.ResultCollector\nResultSaver=org.apache.jmeter.reporters.ResultSaver\nResultSaverGui=org.apache.jmeter.reporters.gui.ResultSaverGui\nRunTime=org.apache.jmeter.control.RunTime\nRunTimeGui=org.apache.jmeter.control.gui.RunTimeGui\nSampleSaveConfiguration=org.apache.jmeter.samplers.SampleSaveConfiguration\nSampleTimeout=org.apache.jmeter.modifiers.SampleTimeout\nSampleTimeoutGui=org.apache.jmeter.modifiers.gui.SampleTimeoutGui\nSimpleConfigGui=org.apache.jmeter.config.gui.SimpleConfigGui\nSimpleDataWriter=org.apache.jmeter.visualizers.SimpleDataWriter\nSizeAssertion=org.apache.jmeter.assertions.SizeAssertion\nSizeAssertionGui=org.apache.jmeter.assertions.gui.SizeAssertionGui\nSMIMEAssertion=org.apache.jmeter.assertions.SMIMEAssertionTestElement\nSMIMEAssertionGui=org.apache.jmeter.assertions.gui.SMIMEAssertionGui\nSmtpSampler=org.apache.jmeter.protocol.smtp.sampler.SmtpSampler\nSmtpSamplerGui=org.apache.jmeter.protocol.smtp.sampler.gui.SmtpSamplerGui\n\n# removed in 3.2, class was deleted in r\nSoapSampler=org.apache.jmeter.protocol.http.sampler.SoapSampler\n# removed in 3.2, class was deleted in r\nSoapSamplerGui=org.apache.jmeter.protocol.http.control.gui.SoapSamplerGui\n\n# removed in 3.1, class was deleted in r1763837\nSplineVisualizer=org.apache.jmeter.visualizers.SplineVisualizer\n# Originally deleted in r397955 as class is obsolete; needed for compat.\nSqlConfigGui=org.apache.jmeter.protocol.jdbc.config.gui.SqlConfigGui\nStaticHost=org.apache.jmeter.protocol.http.control.StaticHost\nStatGraphVisualizer=org.apache.jmeter.visualizers.StatGraphVisualizer\nStatVisualizer=org.apache.jmeter.visualizers.StatVisualizer\nSubscriberSampler=org.apache.jmeter.protocol.jms.sampler.SubscriberSampler\nSubstitutionElement=org.apache.jmeter.assertions.SubstitutionElement\nSummariser=org.apache.jmeter.reporters.Summariser\nSummariserGui=org.apache.jmeter.reporters.gui.SummariserGui\nSummaryReport=org.apache.jmeter.visualizers.SummaryReport\nSwitchController=org.apache.jmeter.control.SwitchController\nSwitchControllerGui=org.apache.jmeter.control.gui.SwitchControllerGui\nSyncTimer=org.apache.jmeter.timers.SyncTimer\nSystemSampler=org.apache.jmeter.protocol.system.SystemSampler\nSystemSamplerGui=org.apache.jmeter.protocol.system.gui.SystemSamplerGui\nTableVisualizer=org.apache.jmeter.visualizers.TableVisualizer\nTCPConfigGui=org.apache.jmeter.protocol.tcp.config.gui.TCPConfigGui\nTCPSampler=org.apache.jmeter.protocol.tcp.sampler.TCPSampler\nTCPSamplerGui=org.apache.jmeter.protocol.tcp.control.gui.TCPSamplerGui\nTestAction=org.apache.jmeter.sampler.TestAction\nTestActionGui=org.apache.jmeter.sampler.gui.TestActionGui\nTestBeanGUI=org.apache.jmeter.testbeans.gui.TestBeanGUI\nTestFragmentController=org.apache.jmeter.control.TestFragmentController\nTestFragmentControllerGui=org.apache.jmeter.control.gui.TestFragmentControllerGui\nTestPlan=org.apache.jmeter.testelement.TestPlan\nTestPlanGui=org.apache.jmeter.control.gui.TestPlanGui\nThreadGroup=org.apache.jmeter.threads.ThreadGroup\nThreadGroupGui=org.apache.jmeter.threads.gui.ThreadGroupGui\nPostThreadGroup=org.apache.jmeter.threads.PostThreadGroup\nPostThreadGroupGui=org.apache.jmeter.threads.gui.PostThreadGroupGui\nSetupThreadGroup=org.apache.jmeter.threads.SetupThreadGroup\nSetupThreadGroupGui=org.apache.jmeter.threads.gui.SetupThreadGroupGui\nThroughputController=org.apache.jmeter.control.ThroughputController\nThroughputControllerGui=org.apache.jmeter.control.gui.ThroughputControllerGui\nTransactionController=org.apache.jmeter.control.TransactionController\nTransactionControllerGui=org.apache.jmeter.control.gui.TransactionControllerGui\nTransactionSampler=org.apache.jmeter.control.TransactionSampler\nUniformRandomTimer=org.apache.jmeter.timers.UniformRandomTimer\nUniformRandomTimerGui=org.apache.jmeter.timers.gui.UniformRandomTimerGui\nURLRewritingModifier=org.apache.jmeter.protocol.http.modifier.URLRewritingModifier\nURLRewritingModifierGui=org.apache.jmeter.protocol.http.modifier.gui.URLRewritingModifierGui\nUserParameterModifier=org.apache.jmeter.protocol.http.modifier.UserParameterModifier\nUserParameterModifierGui=org.apache.jmeter.protocol.http.modifier.gui.UserParameterModifierGui\nUserParameters=org.apache.jmeter.modifiers.UserParameters\nUserParametersGui=org.apache.jmeter.modifiers.gui.UserParametersGui\nViewResultsFullVisualizer=org.apache.jmeter.visualizers.ViewResultsFullVisualizer\n# removed in 3.0, class was deleted in r1722757\nWebServiceSampler=org.apache.jmeter.protocol.http.sampler.WebServiceSampler\n# removed in 3.0, class was deleted in r1722757\nWebServiceSamplerGui=org.apache.jmeter.protocol.http.control.gui.WebServiceSamplerGui\nWhileController=org.apache.jmeter.control.WhileController\nWhileControllerGui=org.apache.jmeter.control.gui.WhileControllerGui\nWorkBench=org.apache.jmeter.testelement.WorkBench\nWorkBenchGui=org.apache.jmeter.control.gui.WorkBenchGui\nXMLAssertion=org.apache.jmeter.assertions.XMLAssertion\nXMLAssertionGui=org.apache.jmeter.assertions.gui.XMLAssertionGui\nXMLSchemaAssertion=org.apache.jmeter.assertions.XMLSchemaAssertion\nXMLSchemaAssertionGUI=org.apache.jmeter.assertions.gui.XMLSchemaAssertionGUI\nXPathAssertion=org.apache.jmeter.assertions.XPathAssertion\nXPathAssertionGui=org.apache.jmeter.assertions.gui.XPathAssertionGui\nXPathExtractor=org.apache.jmeter.extractor.XPathExtractor\nXPathExtractorGui=org.apache.jmeter.extractor.gui.XPathExtractorGui\n#\n# Properties - all start with lower case letter and end with Prop\n#\nboolProp=org.apache.jmeter.testelement.property.BooleanProperty\ncollectionProp=org.apache.jmeter.testelement.property.CollectionProperty\ndoubleProp=org.apache.jmeter.testelement.property.DoubleProperty\nelementProp=org.apache.jmeter.testelement.property.TestElementProperty\n# see above - already defined as FloatProperty\n#floatProp=org.apache.jmeter.testelement.property.FloatProperty\nintProp=org.apache.jmeter.testelement.property.IntegerProperty\nlongProp=org.apache.jmeter.testelement.property.LongProperty\nmapProp=org.apache.jmeter.testelement.property.MapProperty\nobjProp=org.apache.jmeter.testelement.property.ObjectProperty\nstringProp=org.apache.jmeter.testelement.property.StringProperty\n#\n# Other - must start with a lower case letter (and not end with Prop)\n# (otherwise they could clash with the initial set of aliases)\n#\nhashTree=org.apache.jorphan.collections.ListedHashTree\njmeterTestPlan=org.apache.jmeter.save.ScriptWrapper\nsample=org.apache.jmeter.samplers.SampleResult\nhttpSample=org.apache.jmeter.protocol.http.sampler.HTTPSampleResult\nstatSample=org.apache.jmeter.samplers.StatisticalSampleResult\ntestResults=org.apache.jmeter.save.TestResultWrapper\nassertionResult=org.apache.jmeter.assertions.AssertionResult\n\n# removed in 3.2, class was deleted in r\nmonitorStats=org.apache.jmeter.visualizers.MonitorStats\nsampleEvent=org.apache.jmeter.samplers.SampleEvent\n#\n# Converters to register.  Must start line with '_'\n# If the converter is a collection of subitems, set equal to \"collection\"\n# If the converter needs to know the class mappings but is not a collection of \n#      subitems, set it equal to \"mapping\"\n#_org.apache.jmeter.protocol.http.sampler.HTTPSamplerBaseConverter=collection\n#_org.apache.jmeter.protocol.http.util.HTTPResultConverter=collection\n_org.apache.jmeter.save.converters.BooleanPropertyConverter=\n_org.apache.jmeter.save.converters.IntegerPropertyConverter=\n_org.apache.jmeter.save.converters.LongPropertyConverter=\n_org.apache.jmeter.save.converters.MultiPropertyConverter=collection\n_org.apache.jmeter.save.converters.SampleEventConverter=\n_org.apache.jmeter.save.converters.SampleResultConverter=collection\n_org.apache.jmeter.save.converters.SampleSaveConfigurationConverter=collection\n_org.apache.jmeter.save.converters.StringPropertyConverter=\n_org.apache.jmeter.save.converters.HashTreeConverter=collection\n_org.apache.jmeter.save.converters.TestElementConverter=collection\n_org.apache.jmeter.save.converters.TestElementPropertyConverter=collection\n_org.apache.jmeter.save.converters.TestResultWrapperConverter=collection\n_org.apache.jmeter.save.ScriptWrapperConverter=mapping\n#\n#\tRemember to update the _version entry\n#\n"
  },
  {
    "path": "src/test/resources/bin/upgrade.properties",
    "content": "# Class, property and value upgrade equivalences.\n\n##   Licensed to the Apache Software Foundation (ASF) under one or more\n##   contributor license agreements.  See the NOTICE file distributed with\n##   this work for additional information regarding copyright ownership.\n##   The ASF licenses this file to You under the Apache License, Version 2.0\n##   (the \"License\"); you may not use this file except in compliance with\n##   the License.  You may obtain a copy of the License at\n## \n##       http://www.apache.org/licenses/LICENSE-2.0\n## \n##   Unless required by applicable law or agreed to in writing, software\n##   distributed under the License is distributed on an \"AS IS\" BASIS,\n##   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n##   See the License for the specific language governing permissions and\n##   limitations under the License.\n\n#\n# Format is as follows --\n# for renamed test element & GUI classes:\n# \t\told.class.Name=new.class.Name\n# \t\told.class.Name|guiClassName=new.class.Name\n#\t\t(e.g. for ConfigTestElement)\n#\n# for renamed / deleted properties:\n#\t\tclass.Name/Old.propertyName=newPropertyName\n#\t\tif newPropertyName is omitted, then property is deleted\n#\n# for renamed values:\n#\t\told.class.Name.old.propertyName/oldValue=newValue\n#\n\norg.apache.jmeter.protocol.http.config.gui.UrlConfigGui=org.apache.jmeter.protocol.http.config.gui.HttpDefaultsGui\norg.apache.jmeter.assertions.Assertion=org.apache.jmeter.assertions.ResponseAssertion\norg.apache.jmeter.protocol.http.sampler.HTTPSamplerFull=org.apache.jmeter.protocol.http.sampler.HTTPSampler\norg.apache.jmeter.control.gui.RecordController=org.apache.jmeter.protocol.http.control.gui.RecordController\n\norg.apache.jmeter.timers.gui.ConstantThroughputTimerGui=org.apache.jmeter.testbeans.gui.TestBeanGUI\norg.apache.jmeter.timers.ConstantThroughputTimer/ConstantThroughputTimer.throughput=throughput\n\norg.apache.jmeter.protocol.jdbc.control.gui.JdbcTestSampleGui=org.apache.jmeter.testbeans.gui.TestBeanGUI\norg.apache.jmeter.protocol.jdbc.sampler.JDBCSampler/JDBCSampler.query=query\n#org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler.JDBCSampler.dataSource/NULL=\n\n# Convert DBconfig\norg.apache.jmeter.protocol.jdbc.config.gui.DbConfigGui=org.apache.jmeter.testbeans.gui.TestBeanGUI\norg.apache.jmeter.config.ConfigTestElement|org.apache.jmeter.protocol.jdbc.config.gui.DbConfigGui=org.apache.jmeter.protocol.jdbc.config.DataSourceElement\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/JDBCSampler.url=dbUrl\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/JDBCSampler.driver=driver\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/JDBCSampler.query=query\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/ConfigTestElement.username=username\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/ConfigTestElement.password=password\n\n# Convert PoolConfig\norg.apache.jmeter.protocol.jdbc.config.gui.PoolConfigGui=org.apache.jmeter.testbeans.gui.TestBeanGUI\norg.apache.jmeter.config.ConfigTestElement|org.apache.jmeter.protocol.jdbc.config.gui.PoolConfigGui=org.apache.jmeter.protocol.jdbc.config.DataSourceElement\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/JDBCSampler.connections=\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/JDBCSampler.connPoolClass=\norg.apache.jmeter.protocol.jdbc.config.DataSourceElement/JDBCSampler.maxuse=poolMax\n\n# SQL Config\norg.apache.jmeter.config.ConfigTestElement/JDBCSampler.query=query\norg.apache.jmeter.protocol.http.control.Header/TestElement.name=Header.name\n\n# Upgrade AccessLogSampler\norg.apache.jmeter.protocol.http.control.gui.AccessLogSamplerGui=org.apache.jmeter.testbeans.gui.TestBeanGUI\norg.apache.jmeter.protocol.http.sampler.AccessLogSampler/AccessLogSampler.log_file=logFile\norg.apache.jmeter.protocol.http.sampler.AccessLogSampler/HTTPSampler.port=portString\n#Is the following used now?\n#org.apache.jmeter.protocol.http.sampler.AccessLogSampler/AccessLogSampler.generator_class_name=\n#Looks to be a new field\n#filterClassName\norg.apache.jmeter.protocol.http.sampler.AccessLogSampler/HTTPSampler.domain=domain\norg.apache.jmeter.protocol.http.sampler.AccessLogSampler/AccessLogSampler.parser_class_name=parserClassName\norg.apache.jmeter.protocol.http.sampler.AccessLogSampler/HTTPSampler.image_parser=imageParsing\n\n# Renamed class\norg.apache.jmeter.protocol.jms.control.gui.JMSConfigGui=org.apache.jmeter.protocol.jms.control.gui.JMSSamplerGui\n\n# These classes have been deleted; there's no defined replacement\norg.apache.jmeter.protocol.jdbc.config.gui.SqlConfigGui=org.apache.jmeter.config.gui.ObsoleteGui\norg.apache.jmeter.protocol.jms.control.gui.JndiDefaultsGui=org.apache.jmeter.config.gui.ObsoleteGui\n# Should probably map to something other than ObsoleteGui...\norg.apache.jmeter.threads.ReflectionThreadGroup=org.apache.jmeter.config.gui.ObsoleteGui\n\n# Convert BSFSamplerGui\norg.apache.jmeter.protocol.java.control.gui.BSFSamplerGui=org.apache.jmeter.testbeans.gui.TestBeanGUI\norg.apache.jmeter.protocol.java.sampler.BSFSampler/BSFSampler.filename=filename\norg.apache.jmeter.protocol.java.sampler.BSFSampler/BSFSampler.language=scriptLanguage\norg.apache.jmeter.protocol.java.sampler.BSFSampler/BSFSampler.parameters=parameters\norg.apache.jmeter.protocol.java.sampler.BSFSampler/BSFSampler.query=script\n\n# Obsolete Http user Parameters modifier test element\n# Note: ConfigTestElement is the test element associated with ObsoleteGui\norg.apache.jmeter.protocol.http.modifier.UserParameterModifier=org.apache.jmeter.config.ConfigTestElement\norg.apache.jmeter.protocol.http.modifier.gui.UserParameterModifierGui=org.apache.jmeter.config.gui.ObsoleteGui\n\n# Obsolete Graph Full Results listener\norg.apache.jmeter.visualizers.GraphAccumVisualizer=org.apache.jmeter.config.gui.ObsoleteGui\n# removed in 3.0, class was deleted in r1722757\norg.apache.jmeter.protocol.http.sampler.WebServiceSampler=org.apache.jmeter.config.ConfigTestElement\n# removed in 3.0, class was deleted in r1722757\norg.apache.jmeter.protocol.http.control.gui.WebServiceSamplerGui=org.apache.jmeter.config.gui.ObsoleteGui\n# removed in 3.0, class was deleted in r1722757\norg.apache.jmeter.protocol.http.modifier.ParamModifier=org.apache.jmeter.config.ConfigTestElement\n# removed in 3.0, class was deleted in r1722962\norg.apache.jmeter.protocol.http.modifier.ParamMask=org.apache.jmeter.config.ConfigTestElement\n# removed in 3.0, class was deleted in r1722757\norg.apache.jmeter.protocol.http.modifier.gui.ParamModifierGui=org.apache.jmeter.config.gui.ObsoleteGui\n\n# removed in 3.1, class was deleted in r1774947\norg.apache.jmeter.visualizers.SplineVisualizer=org.apache.jmeter.config.gui.ObsoleteGui\n# removed in 3.1 class was deleted in r1763837\norg.apache.jmeter.visualizers.DistributionGraphVisualizer=org.apache.jmeter.config.gui.ObsoleteGui\n\n# removed in 3.2 class was deleted in r1771608\norg.apache.jmeter.visualizers.MonitorStats=org.apache.jmeter.config.ConfigTestElement\norg.apache.jmeter.visualizers.MonitorHealthVisualizer=org.apache.jmeter.config.gui.ObsoleteGui\n\n# removed in 3.2 class was deleted in r1783280\norg.apache.jmeter.protocol.http.sampler.HTTPSampler2=org.apache.jmeter.config.ConfigTestElement\norg.apache.jmeter.protocol.http.sampler.SoapSampler=org.apache.jmeter.config.ConfigTestElement\norg.apache.jmeter.protocol.http.control.gui.SoapSamplerGui=org.apache.jmeter.config.gui.ObsoleteGui"
  },
  {
    "path": "src/test/resources/bin/user.properties",
    "content": ""
  },
  {
    "path": "src/test/resources/log4j2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n   Licensed to the Apache Software Foundation (ASF) under one or more\n   contributor license agreements.  See the NOTICE file distributed with\n   this work for additional information regarding copyright ownership.\n   The ASF licenses this file to You under the Apache License, Version 2.0\n   (the \"License\"); you may not use this file except in compliance with\n   the License.  You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<Configuration status=\"WARN\" packages=\"org.apache.jmeter.gui.logging\">\n\n  <Appenders>\n\n    <Console name=\"STDOUT\" target=\"SYSTEM_OUT\">\n      <PatternLayout pattern=\"%m%n\"/>\n    </Console>\n\n  </Appenders>\n\n  <Loggers>\n\n    <Root level=\"info\">\n      <AppenderRef ref=\"STDOUT\" />\n    </Root>\n\n    <Logger name=\"org.apache.jmeter.junit\" level=\"debug\" />\n    <Logger name=\"com.github.johrstrom\" level=\"debug\" />\n    <!--\n    <Logger name=\"org.apache.jmeter.control\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.testbeans\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.engine\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.threads\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.gui\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.testelement\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.util\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.protocol.http\" level=\"debug\" />\n    -->\n    <!-- # For CookieManager, AuthManager etc: -->\n    <!--\n    <Logger name=\"org.apache.jmeter.protocol.http.control\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.protocol.ftp\" level=\"warn\" />\n    <Logger name=\"org.apache.jmeter.protocol.jdbc\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.protocol.java\" level=\"warn\" />\n    <Logger name=\"org.apache.jmeter.testelements.property\" level=\"debug\" />\n    -->\n    <Logger name=\"org.apache.jorphan\" level=\"info\" />\n\n    <!--\n      # Apache HttpClient logging examples\n    -->\n    <!-- # Enable header wire + context logging - Best for Debugging -->\n    <!--\n    <Logger name=\"org.apache.http\" level=\"debug\" />\n    <Logger name=\"org.apache.http.wire\" level=\"error\" />\n    -->\n\n    <!-- # Enable full wire + context logging -->\n    <!-- <Logger name=\"org.apache.http\" level=\"debug\" /> -->\n\n    <!-- # Enable context logging for connection management -->\n    <!-- <Logger name=\"org.apache.http.impl.conn\" level=\"debug\" /> -->\n\n    <!-- # Enable context logging for connection management / request execution -->\n    <!--\n    <Logger name=\"org.apache.http.impl.conn\" level=\"debug\" />\n    <Logger name=\"org.apache.http.impl.client\" level=\"debug\" />\n    <Logger name=\"org.apache.http.client\" level=\"debug\" />\n    -->\n\n    <!--\n      # Reporting logging configuration examples\n    -->\n    <!-- # If you want to debug reporting, uncomment this line -->\n    <!-- <Logger name=\"org.apache.jmeter.report\" level=\"debug\" /> -->\n\n    <!--\n      # More user specific logging configuration examples.\n    -->\n    <!-- <Logger name=\"org.apache.jorphan.reflect\" level=\"debug\" /> -->\n    <!--\n      # Warning: Enabling the next debug line causes javax.net.ssl.SSLException: Received fatal alert: unexpected_message\n                 for certain sites when used with the default HTTP Sampler\n    -->\n    <!--\n    <Logger name=\"org.apache.jmeter.util.HttpSSLProtocolSocketFactory\" level=\"debug\" />\n    <Logger name=\"org.apache.jmeter.util.JsseSSLManager\" level=\"debug\" />\n    -->\n\n    <!--\n      # Enable Proxy request debug\n    -->\n    <!-- <Logger name=\"org.apache.jmeter.protocol.http.proxy.HttpRequestHdr\" level=\"debug\" /> -->\n\n  </Loggers>\n\n</Configuration>\n"
  }
]