diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 0507c02c..61167366 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -20,9 +20,12 @@ jobs: java-version: ${{ matrix.version }} distribution: 'zulu' cache: gradle - - name: Grant execute permission for gradlew run: chmod +x gradlew + + - uses: editorconfig-checker/action-editorconfig-checker@v2 + - run: editorconfig-checker + - name: Build with Gradle run: ./gradlew build publishToMavenLocal -x :examples:build - name: Build Examples diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5ab82c3f..cadc2c0a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,8 +1,9 @@ name: Publish package to the Clojars on: workflow_dispatch: - release: - types: [ "created" ] + push: + tags: + - v* jobs: publish: diff --git a/CHANGELOG.md b/CHANGELOG.md index ea4721b2..3b071b27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,4 +22,3 @@ - Deadlining for APIs - ability to specify execution timeouts for gRPC stubs at service end - Task/Upstream request retries using Hedged Requests - Tool recommendations for testing - diff --git a/LICENSE b/LICENSE index 261eeb9e..3d0d6f6b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 + Apache License + Version 2.0, January 2004 http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/build.gradle b/build.gradle index 409e2c85..e2604740 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ allprojects { } tasks.withType(GenerateModuleMetadata) { - enabled = false + enabled = false } def gJEXVersion = '1.40-SNAPSHOT' @@ -40,10 +40,10 @@ publishing { from components.java pom { licenses { - license { + license { name = "The Apache License, Version 2.0" url = "http://www.apache.org/licenses/LICENSE-2.0.txt" - } + } } } } @@ -106,6 +106,3 @@ subprojects { ] } } - - - diff --git a/contrib/hibernate/settings.gradle b/contrib/hibernate/settings.gradle index 7eee8c6d..d0ed2ed5 100644 --- a/contrib/hibernate/settings.gradle +++ b/contrib/hibernate/settings.gradle @@ -1,2 +1 @@ rootProject.name = 'hibernate' - diff --git a/core/build.gradle b/core/build.gradle index 2ee48c1e..876b4343 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,7 +5,7 @@ plugins { } tasks.withType(GenerateModuleMetadata) { - enabled = false + enabled = false } publishing { @@ -15,10 +15,10 @@ publishing { from components.java pom { licenses { - license { + license { name = "The Apache License, Version 2.0" url = "http://www.apache.org/licenses/LICENSE-2.0.txt" - } + } } } } diff --git a/core/src/main/java/com/flipkart/gjex/Constants.java b/core/src/main/java/com/flipkart/gjex/Constants.java index 21e89060..c0410d4d 100644 --- a/core/src/main/java/com/flipkart/gjex/Constants.java +++ b/core/src/main/java/com/flipkart/gjex/Constants.java @@ -22,31 +22,31 @@ */ public interface Constants { - String LOGGING_ID = "loggingId"; + String LOGGING_ID = "loggingId"; - String CONFIG_FILE_PROPERTY = "gjex.configurationFile"; + String CONFIG_FILE_PROPERTY = "gjex.configurationFile"; /** - * Root for all packaged configs. - */ + * Root for all packaged configs. + */ String CONFIG_ROOT = "packaged"; /** - * Configuration yml resource path. - */ + * Configuration yml resource path. + */ String CONFIGURATION_YML = CONFIG_ROOT + "/configuration.yml"; /** - * Config file name for GRPC Service module class names - */ + * Config file name for GRPC Service module class names + */ String GRPC_MODULE_NAMES_CONFIG = "grpc_modules.yml"; /** - * The GRPC Service modules property name - */ + * The GRPC Service modules property name + */ String GRPC_MODULE_NAMES = "GJEX.grpc.module.names"; - /** Static name for GJEX core application name used to distinguish Resources added by the framework with custom ones added by the GJEX applications */ - String GJEX_CORE_APPLICATION = "@@GJEXCoreApplication@@"; + /** Static name for GJEX core application name used to distinguish Resources added by the framework with custom ones added by the GJEX applications */ + String GJEX_CORE_APPLICATION = "@@GJEXCoreApplication@@"; } diff --git a/core/src/main/java/com/flipkart/gjex/core/Application.java b/core/src/main/java/com/flipkart/gjex/core/Application.java index d4da1d33..0a78c695 100644 --- a/core/src/main/java/com/flipkart/gjex/core/Application.java +++ b/core/src/main/java/com/flipkart/gjex/core/Application.java @@ -35,90 +35,90 @@ @SuppressWarnings("rawtypes") public abstract class Application implements Logging { - /** The GJEX startup display contents*/ - private static final MessageFormat STARTUP_DISPLAY = new MessageFormat( + /** The GJEX startup display contents*/ + private static final MessageFormat STARTUP_DISPLAY = new MessageFormat( "\n*************************************************************************\n" + - " ╔═╗ ╦╔═╗═╗ ╦ " + " Application name : {0} \n" + - " ║ ╦ ║║╣ ╔╩╦╝ " + " Startup Time : {1}" + " ms\n" + - " ╚═╝╚╝╚═╝╩ ╚═ " + " Host Name: {2} \n " + - "*************************************************************************" + " ╔═╗ ╦╔═╗═╗ ╦ " + " Application name : {0} \n" + + " ║ ╦ ║║╣ ╔╩╦╝ " + " Startup Time : {1}" + " ms\n" + + " ╚═╝╚╝╚═╝╩ ╚═ " + " Host Name: {2} \n " + + "*************************************************************************" ); - /** The machine name where this GJEX instance is running */ - private String hostName; + /** The machine name where this GJEX instance is running */ + private String hostName; - private final ArgumentParserWrapper argumentParser; + private final ArgumentParserWrapper argumentParser; /** - * Constructor for this class - */ + * Constructor for this class + */ public Application() { - this.argumentParser = new ArgumentParserWrapper(); - try { - this.hostName = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - //ignore the exception, not critical information - } + this.argumentParser = new ArgumentParserWrapper(); + try { + this.hostName = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + //ignore the exception, not critical information + } } - /** - * Gets the name of this GJEX application - * @return - */ - public String getName() { + /** + * Gets the name of this GJEX application + * @return + */ + public String getName() { return getClass().getSimpleName(); } - /** - * Initializes this Application using the Bootstrap provided. Derived types may perform startup/one-time initializations - * by implementing this method. - * @param bootstrap the Bootstrap for this Application - */ - public abstract void initialize(Bootstrap bootstrap); - - /** - * When the application runs, this is called after the {@link Bundle}s are run. Override it to add - * providers, resources, etc. for your application. - * - * @param configuration the parsed {@link GJEXConfiguration} object - * @param environment the application's {@link Environment} - * @throws Exception if something goes wrong - */ - public abstract void run(T configuration, U configMap, Environment environment) throws Exception; - - /** - * Parses command-line arguments and runs this Application. Usually called from a {@code public - * static void main} entry point - * - * @param arguments command-line arguments for starting this Application - * @throws Exception in case of errors during run - */ - public final void run(String... arguments) throws Exception { - info("** GJEX starting up... **"); - long start = System.currentTimeMillis(); - - Namespace namespace = argumentParser.parseArguments(arguments); - String configFilePath = namespace.getString("file"); - final Bootstrap bootstrap = new Bootstrap<>(this, configFilePath, getConfigurationClass()); - - /* Hook for applications to initialize their pre-start environment using bootstrap's properties */ + /** + * Initializes this Application using the Bootstrap provided. Derived types may perform startup/one-time initializations + * by implementing this method. + * @param bootstrap the Bootstrap for this Application + */ + public abstract void initialize(Bootstrap bootstrap); + + /** + * When the application runs, this is called after the {@link Bundle}s are run. Override it to add + * providers, resources, etc. for your application. + * + * @param configuration the parsed {@link GJEXConfiguration} object + * @param environment the application's {@link Environment} + * @throws Exception if something goes wrong + */ + public abstract void run(T configuration, U configMap, Environment environment) throws Exception; + + /** + * Parses command-line arguments and runs this Application. Usually called from a {@code public + * static void main} entry point + * + * @param arguments command-line arguments for starting this Application + * @throws Exception in case of errors during run + */ + public final void run(String... arguments) throws Exception { + info("** GJEX starting up... **"); + long start = System.currentTimeMillis(); + + Namespace namespace = argumentParser.parseArguments(arguments); + String configFilePath = namespace.getString("file"); + final Bootstrap bootstrap = new Bootstrap<>(this, configFilePath, getConfigurationClass()); + + /* Hook for applications to initialize their pre-start environment using bootstrap's properties */ initialize(bootstrap); /* Create Environment */ Environment environment = new Environment(getName(), bootstrap.getMetricRegistry()); - /* Run bundles etc */ - bootstrap.run(environment); + /* Run bundles etc */ + bootstrap.run(environment); /* Run this Application */ run(bootstrap.getConfiguration(), bootstrap.getConfigMap(), environment); - final Object[] displayArgs = {this.getName(), (System.currentTimeMillis() - start), hostName}; - info(STARTUP_DISPLAY.format(displayArgs)); - info("** GJEX startup complete **"); + final Object[] displayArgs = {this.getName(), (System.currentTimeMillis() - start), hostName}; + info(STARTUP_DISPLAY.format(displayArgs)); + info("** GJEX startup complete **"); - } - public Class getConfigurationClass() { - return Generics.getTypeParameter(getClass(), GJEXConfiguration.class); - } + } + public Class getConfigurationClass() { + return Generics.getTypeParameter(getClass(), GJEXConfiguration.class); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/Bundle.java b/core/src/main/java/com/flipkart/gjex/core/Bundle.java index f2e0306f..e277f481 100644 --- a/core/src/main/java/com/flipkart/gjex/core/Bundle.java +++ b/core/src/main/java/com/flipkart/gjex/core/Bundle.java @@ -36,52 +36,52 @@ public interface Bundle { /** - * Initializes this Bundle with the application bootstrap. - * - * @param bootstrap the application bootstrap - */ + * Initializes this Bundle with the application bootstrap. + * + * @param bootstrap the application bootstrap + */ void initialize(Bootstrap bootstrap); /** - * Runs this Bundle in the application environment. - * - * @param environment the application environment - */ + * Runs this Bundle in the application environment. + * + * @param environment the application environment + */ void run(T configuration, U configMap, Environment environment); /** - * Returns Service instances loaded by this Bundle - * @return List containing Service instances - */ + * Returns Service instances loaded by this Bundle + * @return List containing Service instances + */ List getServices(); /** - * Returns Filter instances loaded by this Bundle - * @return List containing Filter instances - */ - List getGrpcFilters(); + * Returns Filter instances loaded by this Bundle + * @return List containing Filter instances + */ + List getGrpcFilters(); /** - * Returns HealthCheck instances loaded by this Bundle - * @return List containing HealthCheck instances - */ + * Returns HealthCheck instances loaded by this Bundle + * @return List containing HealthCheck instances + */ List getHealthChecks(); /** - * Returns the TracingSampler instances loaded by this Bundle - * @return the TracingSampler instances - */ + * Returns the TracingSampler instances loaded by this Bundle + * @return the TracingSampler instances + */ List getTracingSamplers(); /** - * Returns the ScheduledJob instances loaded by this Bundle - * @return the ScheduledJob instances - */ + * Returns the ScheduledJob instances loaded by this Bundle + * @return the ScheduledJob instances + */ List getScheduledJobs(); /** - * Returns list of custom {@link ResourceConfig} instances configured by the GJEX application - * @return ResourceConfig instances - */ + * Returns list of custom {@link ResourceConfig} instances configured by the GJEX application + * @return ResourceConfig instances + */ List getResourceConfigs(); } diff --git a/core/src/main/java/com/flipkart/gjex/core/GJEXError.java b/core/src/main/java/com/flipkart/gjex/core/GJEXError.java index 9c288eb7..73c49c52 100644 --- a/core/src/main/java/com/flipkart/gjex/core/GJEXError.java +++ b/core/src/main/java/com/flipkart/gjex/core/GJEXError.java @@ -24,51 +24,51 @@ */ public class GJEXError extends RuntimeException { - /** Default serial version UID */ - private static final long serialVersionUID = 1L; + /** Default serial version UID */ + private static final long serialVersionUID = 1L; - /** Default Fill in stack trace setting*/ - private static final boolean DEFAULT_FILL_IN_STACK_TRACE = true; + /** Default Fill in stack trace setting*/ + private static final boolean DEFAULT_FILL_IN_STACK_TRACE = true; /** Enum of error types*/ public enum ErrorType { runtime,timeout,retriable } - /** The type of error*/ + /** The type of error*/ private ErrorType type; /** The flag for filling in the stack trace*/ private boolean fillInStackTrace = DEFAULT_FILL_IN_STACK_TRACE; /** Constructors */ - public GJEXError(ErrorType type, String errorMessage, Throwable rootCause) { - super(errorMessage, rootCause); - this.type = type; - } - public GJEXError(ErrorType type, String errorMessage, Throwable rootCause, boolean fillInStackTrace) { - this(type, errorMessage, rootCause); - this.fillInStackTrace = fillInStackTrace; - } + public GJEXError(ErrorType type, String errorMessage, Throwable rootCause) { + super(errorMessage, rootCause); + this.type = type; + } + public GJEXError(ErrorType type, String errorMessage, Throwable rootCause, boolean fillInStackTrace) { + this(type, errorMessage, rootCause); + this.fillInStackTrace = fillInStackTrace; + } - /** - * Fills in the stack trace based on how this Exception was created - * @see java.lang.Throwable#fillInStackTrace() - */ + /** + * Fills in the stack trace based on how this Exception was created + * @see java.lang.Throwable#fillInStackTrace() + */ public Throwable fillInStackTrace() { - if (!fillInStackTrace) { - return null; - } else { - return super.fillInStackTrace(); - } + if (!fillInStackTrace) { + return null; + } else { + return super.fillInStackTrace(); + } } - /** Accessor/Mutator methods*/ - public ErrorType getType() { - return type; - } - public void setType(ErrorType type) { - this.type = type; - } + /** Accessor/Mutator methods*/ + public ErrorType getType() { + return type; + } + public void setType(ErrorType type) { + this.type = type; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/GJEXObjectMapper.java b/core/src/main/java/com/flipkart/gjex/core/GJEXObjectMapper.java index e330225a..a87aa2bd 100644 --- a/core/src/main/java/com/flipkart/gjex/core/GJEXObjectMapper.java +++ b/core/src/main/java/com/flipkart/gjex/core/GJEXObjectMapper.java @@ -23,19 +23,19 @@ public class GJEXObjectMapper { private GJEXObjectMapper() { /* singleton */ } /** - * Creates a new {@link ObjectMapper} - */ + * Creates a new {@link ObjectMapper} + */ public static ObjectMapper newObjectMapper() { final ObjectMapper mapper = new ObjectMapper(); return configure(mapper); } /** - * Creates a new {@link ObjectMapper} with a custom {@link com.fasterxml.jackson.core.JsonFactory} - * - * @param jsonFactory instance of {@link com.fasterxml.jackson.core.JsonFactory} to use - * for the created {@link com.fasterxml.jackson.databind.ObjectMapper} instance. - */ + * Creates a new {@link ObjectMapper} with a custom {@link com.fasterxml.jackson.core.JsonFactory} + * + * @param jsonFactory instance of {@link com.fasterxml.jackson.core.JsonFactory} to use + * for the created {@link com.fasterxml.jackson.databind.ObjectMapper} instance. + */ public static ObjectMapper newObjectMapper(JsonFactory jsonFactory) { final ObjectMapper mapper = new ObjectMapper(jsonFactory); return configure(mapper); diff --git a/core/src/main/java/com/flipkart/gjex/core/Generics.java b/core/src/main/java/com/flipkart/gjex/core/Generics.java index 264780cf..f8003fbd 100644 --- a/core/src/main/java/com/flipkart/gjex/core/Generics.java +++ b/core/src/main/java/com/flipkart/gjex/core/Generics.java @@ -29,33 +29,33 @@ public class Generics { private Generics() { /* singleton */ } /** - * Finds the type parameter for the given class. - * - * @param klass a parameterized class - * @return the class's type parameter - */ + * Finds the type parameter for the given class. + * + * @param klass a parameterized class + * @return the class's type parameter + */ public static Class getTypeParameter(Class klass) { return getTypeParameter(klass, Object.class); } /** - * Finds the type parameter for the given class which is assignable to the bound class. - * - * @param klass a parameterized class - * @param bound the type bound - * @param the type bound - * @return the class's type parameter - */ + * Finds the type parameter for the given class which is assignable to the bound class. + * + * @param klass a parameterized class + * @param bound the type bound + * @param the type bound + * @return the class's type parameter + */ public static Class getTypeParameter(Class klass, Class bound) { Type t = requireNonNull(klass); while (t instanceof Class) { t = ((Class) t).getGenericSuperclass(); } /* This is not guaranteed to work for all cases with convoluted piping - * of type parameters: but it can at least resolve straight-forward - * extension with single type parameter (as per [Issue-89]). - * And when it fails to do that, will indicate with specific exception. - */ + * of type parameters: but it can at least resolve straight-forward + * extension with single type parameter (as per [Issue-89]). + * And when it fails to do that, will indicate with specific exception. + */ if (t instanceof ParameterizedType) { // should typically have one of type parameters (first one) that matches: for (Type param : ((ParameterizedType) t).getActualTypeArguments()) { diff --git a/core/src/main/java/com/flipkart/gjex/core/config/BaseConfigurationFactory.java b/core/src/main/java/com/flipkart/gjex/core/config/BaseConfigurationFactory.java index 87e71d70..c6aa8b25 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/BaseConfigurationFactory.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/BaseConfigurationFactory.java @@ -57,12 +57,12 @@ public abstract class BaseConfigurationFactory klass, diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationException.java b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationException.java index 15198f15..e17faa89 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationException.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationException.java @@ -31,30 +31,30 @@ public abstract class ConfigurationException extends Exception { /** Default */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; protected static final String NEWLINE = String.format("%n"); private final Collection errors; /** - * Creates a new ConfigurationException for the given path with the given errors. - * - * @param path the bad configuration path - * @param errors the errors in the path - */ + * Creates a new ConfigurationException for the given path with the given errors. + * + * @param path the bad configuration path + * @param errors the errors in the path + */ public ConfigurationException(String path, Collection errors) { super(formatMessage(path, errors)); this.errors = errors; } /** - * Creates a new ConfigurationException for the given path with the given errors and cause. - * - * @param path the bad configuration path - * @param errors the errors in the path - * @param cause the cause of the error(s) - */ + * Creates a new ConfigurationException for the given path with the given errors and cause. + * + * @param path the bad configuration path + * @param errors the errors in the path + * @param cause the cause of the error(s) + */ public ConfigurationException(String path, Collection errors, Throwable cause) { super(formatMessage(path, errors), cause); this.errors = errors; diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactory.java b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactory.java index 67520e13..762d91d8 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactory.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactory.java @@ -28,43 +28,43 @@ public interface ConfigurationFactory { /** - * Loads, parses, binds, and validates a configuration object. - * - * @param provider the provider to to use for reading configuration files - * @param path the path of the configuration file - * @return A pair of validated configuration object (T) and a map representing the configuration (U) - * @throws IOException if there is an error reading the file - * @throws ConfigurationException if there is an error parsing or validating the file - */ + * Loads, parses, binds, and validates a configuration object. + * + * @param provider the provider to to use for reading configuration files + * @param path the path of the configuration file + * @return A pair of validated configuration object (T) and a map representing the configuration (U) + * @throws IOException if there is an error reading the file + * @throws ConfigurationException if there is an error parsing or validating the file + */ Pair build(ConfigurationSourceProvider provider, String path) throws IOException, ConfigurationException; /** - * Loads, parses, binds, and validates a configuration object from a file. - * - * @param file the path of the configuration file - * @return A pair of validated configuration object (T) and a map representing the configuration (U) - * @throws IOException if there is an error reading the file - * @throws ConfigurationException if there is an error parsing or validating the file - */ + * Loads, parses, binds, and validates a configuration object from a file. + * + * @param file the path of the configuration file + * @return A pair of validated configuration object (T) and a map representing the configuration (U) + * @throws IOException if there is an error reading the file + * @throws ConfigurationException if there is an error parsing or validating the file + */ default Pair build(File file) throws IOException, ConfigurationException { return build(new FileConfigurationSourceProvider(), file.toString()); } /** - * Loads, parses, binds, and validates a configuration object from an empty document. - * - * @return A pair of validated configuration object (T) and a map representing configuration (U) - * @throws IOException if there is an error reading the file - * @throws ConfigurationException if there is an error parsing or validating the file - */ + * Loads, parses, binds, and validates a configuration object from an empty document. + * + * @return A pair of validated configuration object (T) and a map representing configuration (U) + * @throws IOException if there is an error reading the file + * @throws ConfigurationException if there is an error parsing or validating the file + */ Pair build() throws IOException, ConfigurationException; /** - * This function returns an un-flattened json for given flattened json (json flattened using separator) - * @param flattenedJson flattened json - * @param separator character with which @flattenedJson has been flattened - * @return Un-flattened json as string - */ + * This function returns an un-flattened json for given flattened json (json flattened using separator) + * @param flattenedJson flattened json + * @param separator character with which @flattenedJson has been flattened + * @return Un-flattened json as string + */ default String getUnFlattenedJson(String flattenedJson, char separator) { return new JsonUnflattener(flattenedJson). withPrintMode(PrintMode.PRETTY). diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactory.java b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactory.java index d1193d46..8bd1d283 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactory.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactory.java @@ -25,6 +25,6 @@ public interface ConfigurationFactoryFactory { ConfigurationFactory create(Class klass, - Validator validator, - ObjectMapper objectMapper); + Validator validator, + ObjectMapper objectMapper); } diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationParsingException.java b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationParsingException.java index fcb93aa6..9e984cb7 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationParsingException.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationParsingException.java @@ -32,9 +32,9 @@ public class ConfigurationParsingException extends ConfigurationException { /** Default */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public static class Builder { + public static class Builder { private static final int MAX_SUGGESTIONS = 5; private String summary; @@ -52,93 +52,93 @@ public static class Builder { } /** - * Returns a brief message summarizing the error. - * - * @return a brief message summarizing the error. - */ + * Returns a brief message summarizing the error. + * + * @return a brief message summarizing the error. + */ public String getSummary() { return summary.trim(); } /** - * Returns a detailed description of the error. - * - * @return a detailed description of the error or the empty String if there is none. - */ + * Returns a detailed description of the error. + * + * @return a detailed description of the error or the empty String if there is none. + */ public String getDetail() { return detail.trim(); } /** - * Determines if a detailed description of the error has been set. - * - * @return true if there is a detailed description of the error; false if there is not. - */ + * Determines if a detailed description of the error has been set. + * + * @return true if there is a detailed description of the error; false if there is not. + */ public boolean hasDetail() { return detail != null && !detail.isEmpty(); } /** - * Returns the path to the problematic JSON field, if there is one. - * - * @return a {@link List} with each element in the path in order, beginning at the root; or - * an empty list if there is no JSON field in the context of this error. - */ + * Returns the path to the problematic JSON field, if there is one. + * + * @return a {@link List} with each element in the path in order, beginning at the root; or + * an empty list if there is no JSON field in the context of this error. + */ public List getFieldPath() { return fieldPath; } /** - * Determines if the path to a JSON field has been set. - * - * @return true if the path to a JSON field has been set for the error; false if no path has - * yet been set. - */ + * Determines if the path to a JSON field has been set. + * + * @return true if the path to a JSON field has been set for the error; false if no path has + * yet been set. + */ public boolean hasFieldPath() { return fieldPath != null && !fieldPath.isEmpty(); } /** - * Returns the line number of the source of the problem. - *

- * Note: the line number is indexed from zero. - * - * @return the line number of the source of the problem, or -1 if unknown. - */ + * Returns the line number of the source of the problem. + *

+ * Note: the line number is indexed from zero. + * + * @return the line number of the source of the problem, or -1 if unknown. + */ public int getLine() { return line; } /** - * Returns the column number of the source of the problem. - *

- * Note: the column number is indexed from zero. - * - * @return the column number of the source of the problem, or -1 if unknown. - */ + * Returns the column number of the source of the problem. + *

+ * Note: the column number is indexed from zero. + * + * @return the column number of the source of the problem, or -1 if unknown. + */ public int getColumn() { return column; } /** - * Determines if a location (line and column numbers) have been set. - * - * @return true if both a line and column number has been set; false if only one or neither - * have been set. - */ + * Determines if a location (line and column numbers) have been set. + * + * @return true if both a line and column number has been set; false if only one or neither + * have been set. + */ public boolean hasLocation() { return line > -1 && column > -1; } /** - * Returns a list of suggestions. - *

- * If a {@link #getSuggestionBase() suggestion-base} has been set, the suggestions will be - * sorted according to the suggestion-base such that suggestions close to the base appear - * first in the list. - * - * @return a list of suggestions, or the empty list if there are no suggestions available. - */ + * Returns a list of suggestions. + *

+ * If a {@link #getSuggestionBase() suggestion-base} has been set, the suggestions will be + * sorted according to the suggestion-base such that suggestions close to the base appear + * first in the list. + * + * @return a list of suggestions, or the empty list if there are no suggestions available. + */ public List getSuggestions() { if (suggestionsSorted || !hasSuggestionBase()) { @@ -152,50 +152,50 @@ public List getSuggestions() { } /** - * Determines whether suggestions are available. - * - * @return true if suggestions are available; false if they are not. - */ + * Determines whether suggestions are available. + * + * @return true if suggestions are available; false if they are not. + */ public boolean hasSuggestions() { return suggestions != null && !suggestions.isEmpty(); } /** - * Returns the base for ordering suggestions. - *

- * Suggestions will be ordered such that suggestions closer to the base will appear first. - * - * @return the base for suggestions. - */ + * Returns the base for ordering suggestions. + *

+ * Suggestions will be ordered such that suggestions closer to the base will appear first. + * + * @return the base for suggestions. + */ public String getSuggestionBase() { return suggestionBase; } /** - * Determines whether a suggestion base is available. - *

- * If no base is available, suggestions will not be sorted. - * - * @return true if a base is available for suggestions; false if there is none. - */ + * Determines whether a suggestion base is available. + *

+ * If no base is available, suggestions will not be sorted. + * + * @return true if a base is available for suggestions; false if there is none. + */ public boolean hasSuggestionBase() { return suggestionBase != null && !suggestionBase.isEmpty(); } /** - * Returns the {@link Exception} that encapsulates the problem itself. - * - * @return an Exception representing the cause of the problem, or null if there is none. - */ + * Returns the {@link Exception} that encapsulates the problem itself. + * + * @return an Exception representing the cause of the problem, or null if there is none. + */ public Exception getCause() { return cause; } /** - * Determines whether a cause has been set. - * - * @return true if there is a cause; false if there is none. - */ + * Determines whether a cause has been set. + * + * @return true if there is a cause; false if there is none. + */ public boolean hasCause() { return cause != null; } @@ -315,17 +315,17 @@ public LevenshteinComparator(String base) { } /** - * Compares two Strings with respect to the base String, by Levenshtein distance. - *

- * The input that is the closest match to the base String will sort before the other. - * - * @param a an input to compare relative to the base. - * @param b an input to compare relative to the base. - * - * @return -1 if {@code a} is closer to the base than {@code b}; 1 if {@code b} is - * closer to the base than {@code a}; 0 if both {@code a} and {@code b} are - * equally close to the base. - */ + * Compares two Strings with respect to the base String, by Levenshtein distance. + *

+ * The input that is the closest match to the base String will sort before the other. + * + * @param a an input to compare relative to the base. + * @param b an input to compare relative to the base. + * + * @return -1 if {@code a} is closer to the base than {@code b}; 1 if {@code b} is + * closer to the base than {@code a}; 0 if both {@code a} and {@code b} are + * equally close to the base. + */ @Override public int compare(String a, String b) { @@ -354,33 +354,33 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo } /** - * Create a mutable {@link Builder} to incrementally build a {@link ConfigurationParsingException}. - * - * @param brief the brief summary of the error. - * - * @return a mutable builder to incrementally build a {@link ConfigurationParsingException}. - */ + * Create a mutable {@link Builder} to incrementally build a {@link ConfigurationParsingException}. + * + * @param brief the brief summary of the error. + * + * @return a mutable builder to incrementally build a {@link ConfigurationParsingException}. + */ public static Builder builder(String brief) { return new Builder(brief); } /** - * Creates a new ConfigurationParsingException for the given path with the given error. - * - * @param path the bad configuration path - * @param msg the full error message - */ + * Creates a new ConfigurationParsingException for the given path with the given error. + * + * @param path the bad configuration path + * @param msg the full error message + */ private ConfigurationParsingException(String path, String msg) { super(path, ImmutableSet.of(msg)); } /** - * Creates a new ConfigurationParsingException for the given path with the given error. - * - * @param path the bad configuration path - * @param msg the full error message - * @param cause the cause of the parsing error. - */ + * Creates a new ConfigurationParsingException for the given path with the given error. + * + * @param path the bad configuration path + * @param msg the full error message + * @param cause the cause of the parsing error. + */ private ConfigurationParsingException(String path, String msg, Throwable cause) { super(path, ImmutableSet.of(msg), cause); } diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationSourceProvider.java b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationSourceProvider.java index f671fe76..183737ae 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationSourceProvider.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationSourceProvider.java @@ -25,13 +25,13 @@ */ public interface ConfigurationSourceProvider { /** - * Returns an {@link InputStream} that contains the source of the configuration for the - * application. The caller is responsible for closing the result. - * - * @param path the path to the configuration - * @return an {@link InputStream} - * @throws IOException if there is an error reading the data at {@code path} - */ + * Returns an {@link InputStream} that contains the source of the configuration for the + * application. The caller is responsible for closing the result. + * + * @param path the path to the configuration + * @return an {@link InputStream} + * @throws IOException if there is an error reading the data at {@code path} + */ InputStream open(String path) throws IOException; } diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationValidationException.java b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationValidationException.java index 17913f23..8277f8d3 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationValidationException.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConfigurationValidationException.java @@ -26,26 +26,26 @@ public class ConfigurationValidationException extends ConfigurationException { /** Default */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final ImmutableSet> constraintViolations; + private final ImmutableSet> constraintViolations; /** - * Creates a new ConfigurationException for the given path with the given errors. - * - * @param path the bad configuration path - * @param errors the errors in the path - */ + * Creates a new ConfigurationException for the given path with the given errors. + * + * @param path the bad configuration path + * @param errors the errors in the path + */ public ConfigurationValidationException(String path, Set> errors) { super(path, ConstraintViolations.format(errors)); this.constraintViolations = ConstraintViolations.copyOf(errors); } /** - * Returns the set of constraint violations in the configuration. - * - * @return the set of constraint violations - */ + * Returns the set of constraint violations in the configuration. + * + * @return the set of constraint violations + */ public ImmutableSet> getConstraintViolations() { return constraintViolations; } diff --git a/core/src/main/java/com/flipkart/gjex/core/config/ConstraintViolations.java b/core/src/main/java/com/flipkart/gjex/core/config/ConstraintViolations.java index f8d93d26..ea584d82 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/ConstraintViolations.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/ConstraintViolations.java @@ -54,4 +54,3 @@ public static ImmutableSet> copyOf(Set create(Class klass, Validator validator, Ob } /** - * Provides additional configuration for the {@link ObjectMapper} used to read - * the configuration. By default {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} - * is enabled to protect against misconfiguration. - * - * @param objectMapper template to be configured - * @return configured object objectMapper - */ + * Provides additional configuration for the {@link ObjectMapper} used to read + * the configuration. By default {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} + * is enabled to protect against misconfiguration. + * + * @param objectMapper template to be configured + * @return configured object objectMapper + */ protected ObjectMapper configureObjectMapper(ObjectMapper objectMapper) { return objectMapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } diff --git a/core/src/main/java/com/flipkart/gjex/core/config/FileLocator.java b/core/src/main/java/com/flipkart/gjex/core/config/FileLocator.java index 3a284991..75c8021f 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/FileLocator.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/FileLocator.java @@ -33,153 +33,153 @@ */ public class FileLocator { - /** - * The Log instance for this class - */ + /** + * The Log instance for this class + */ private static final Logger LOGGER = LoggerFactory.getLogger(FileLocator.class); - /** The config root folder of GJEX */ - private static final String DEPLOYED_CONFIG_ROOT; - static { - DEPLOYED_CONFIG_ROOT = FileLocator.class.getClassLoader().getResource(Constants.CONFIG_ROOT).getPath(); - } + /** The config root folder of GJEX */ + private static final String DEPLOYED_CONFIG_ROOT; + static { + DEPLOYED_CONFIG_ROOT = FileLocator.class.getClassLoader().getResource(Constants.CONFIG_ROOT).getPath(); + } - /** - * Finds the unique instance of config file with name as the specified string. - * @param fileName name of the file - * @return the unique file occurrence - * @throws ConfigurationException in case multiple files are found with the specified name - */ - public static File findUniqueFile(String fileName) throws ConfigurationException { - return findUniqueFile(fileName, null); - } + /** + * Finds the unique instance of config file with name as the specified string. + * @param fileName name of the file + * @return the unique file occurrence + * @throws ConfigurationException in case multiple files are found with the specified name + */ + public static File findUniqueFile(String fileName) throws ConfigurationException { + return findUniqueFile(fileName, null); + } - /** - * Finds the unique instance of config file with name as the specified string. - * @param fileName name of the file - * @return the unique file occurrence - * @throws ConfigurationException in case multiple files are found with the specified name - */ - public static File findUniqueFile(String fileName, String path) throws ConfigurationException { - File[] files = findFiles(fileName, path); - if (files.length == 0) { - StringBuilder errorMessage = new StringBuilder("No file found that matches specified name : "); - errorMessage.append(fileName); - if (path != null) { - errorMessage.append(" under path " + path); - } - LOGGER.error(errorMessage.toString()); - throw new RuntimeException(errorMessage.toString()); - } else if (files.length > 1) { - StringBuilder foundFiles = new StringBuilder(); - for (int i = 0; i < files.length; i++) { - foundFiles.append(files[i].getAbsolutePath() + "\n"); - } - StringBuffer errorMessage = new StringBuffer("Multiple files found that match specified name : "); - errorMessage.append(fileName); - if (path != null) { - errorMessage.append(" under path " + path); - } - errorMessage.append(" as: \n "); - errorMessage.append(foundFiles.toString()); - LOGGER.error(errorMessage.toString()); - throw new RuntimeException(errorMessage.toString()); - } - return files[0]; - } + /** + * Finds the unique instance of config file with name as the specified string. + * @param fileName name of the file + * @return the unique file occurrence + * @throws ConfigurationException in case multiple files are found with the specified name + */ + public static File findUniqueFile(String fileName, String path) throws ConfigurationException { + File[] files = findFiles(fileName, path); + if (files.length == 0) { + StringBuilder errorMessage = new StringBuilder("No file found that matches specified name : "); + errorMessage.append(fileName); + if (path != null) { + errorMessage.append(" under path " + path); + } + LOGGER.error(errorMessage.toString()); + throw new RuntimeException(errorMessage.toString()); + } else if (files.length > 1) { + StringBuilder foundFiles = new StringBuilder(); + for (int i = 0; i < files.length; i++) { + foundFiles.append(files[i].getAbsolutePath() + "\n"); + } + StringBuffer errorMessage = new StringBuffer("Multiple files found that match specified name : "); + errorMessage.append(fileName); + if (path != null) { + errorMessage.append(" under path " + path); + } + errorMessage.append(" as: \n "); + errorMessage.append(foundFiles.toString()); + LOGGER.error(errorMessage.toString()); + throw new RuntimeException(errorMessage.toString()); + } + return files[0]; + } - /** - * Returns an array of files that match the specified name located under - * the projects' configuration folders. - * This method tries to load the file as a resource using the classloader - * in case the specified file is not found under projects' root. - * @param fileName the case insensitive name of the file to be located - * @return array of matching files. May be an empty array if no matching files are found - */ - public static File[] findFiles(String fileName) { - return findFiles(fileName,null); - } + /** + * Returns an array of files that match the specified name located under + * the projects' configuration folders. + * This method tries to load the file as a resource using the classloader + * in case the specified file is not found under projects' root. + * @param fileName the case insensitive name of the file to be located + * @return array of matching files. May be an empty array if no matching files are found + */ + public static File[] findFiles(String fileName) { + return findFiles(fileName,null); + } - /** - * Returns an array of files that match the specified name located under - * the projects' configuration folders. - * This method tries to load the file as a resource using the classloader - * in case the specified file is not found under projects' root. - * @param fileName the case insensitive name of the file to be located - * @param path Will narrow search to specific path within project root. - * @return array of matching files. May be an empty array if no matching files are found - */ - public static File[] findFiles(String fileName,String path) { - return findFiles(fileName, path, false); - } + /** + * Returns an array of files that match the specified name located under + * the projects' configuration folders. + * This method tries to load the file as a resource using the classloader + * in case the specified file is not found under projects' root. + * @param fileName the case insensitive name of the file to be located + * @param path Will narrow search to specific path within project root. + * @return array of matching files. May be an empty array if no matching files are found + */ + public static File[] findFiles(String fileName,String path) { + return findFiles(fileName, path, false); + } - /** - * Returns an array of directories that match the specified name located under - * the projects' configuration folders. - * This method tries to load the directory as a resource using the classloader - * in case the specified directory is not found under projects' root. - * @param directoryName the case insensitive name of the directory to be located - * @param path Will narrow search to specific path within project root. - * @return array of matching directories. May be an empty array if no matching directories are found - */ - public static File[] findDirectories(String directoryName,String path) { - return findFiles(directoryName, path, true); - } + /** + * Returns an array of directories that match the specified name located under + * the projects' configuration folders. + * This method tries to load the directory as a resource using the classloader + * in case the specified directory is not found under projects' root. + * @param directoryName the case insensitive name of the directory to be located + * @param path Will narrow search to specific path within project root. + * @return array of matching directories. May be an empty array if no matching directories are found + */ + public static File[] findDirectories(String directoryName,String path) { + return findFiles(directoryName, path, true); + } - /** - * Returns an array of files that match the specified name located under - * the projects' configuration folders. - * This method tries to load the file as a resource using the classloader - * in case the specified file is not found under projects' root. - * @param fileName the case insensitive name of the file to be located - * @param path Will narrow search to specific path within project root. - * @param isDirectory if true, find only directory - * if false, find only file. - * @return array of matching files. May be an empty array if no matching files are found - */ + /** + * Returns an array of files that match the specified name located under + * the projects' configuration folders. + * This method tries to load the file as a resource using the classloader + * in case the specified file is not found under projects' root. + * @param fileName the case insensitive name of the file to be located + * @param path Will narrow search to specific path within project root. + * @param isDirectory if true, find only directory + * if false, find only file. + * @return array of matching files. May be an empty array if no matching files are found + */ private static File[] findFiles(String fileName,String path, boolean isDirectory) { - File projectRootFolder = null; - if(path!=null) { - File pathFile = new File(path); - if (pathFile.isAbsolute() || pathFile.isDirectory()) { // use the path as-is if it is absolute or a directory by itself - projectRootFolder = pathFile; - } else { - projectRootFolder = new File(DEPLOYED_CONFIG_ROOT,path); - } - } else { - projectRootFolder = new File(DEPLOYED_CONFIG_ROOT); - } - ArrayList locatedFiles = new ArrayList(); - locateFiles(fileName, locatedFiles, projectRootFolder, false, isDirectory); - if (locatedFiles.size() == 0) { - try { - Enumeration enumeration = FileLocator.class.getClassLoader().getResources(fileName); - while (enumeration.hasMoreElements()) { - URL resource = enumeration.nextElement(); - if (resource.getFile().contains(".jar!") && !isDirectory) { // resource is a file inside a jar - locatedFiles.add(getTempFileFromResource(fileName, resource.openStream())); - } else { - locatedFiles.add(new File(resource.getFile())); - } - } - } catch (IOException e) { - // log the error and return an empty array - LOGGER.error("Unable to locate files that match: " + fileName, e); - } - } - return (File[])locatedFiles.toArray(new File[locatedFiles.size()]); - } + File projectRootFolder = null; + if(path!=null) { + File pathFile = new File(path); + if (pathFile.isAbsolute() || pathFile.isDirectory()) { // use the path as-is if it is absolute or a directory by itself + projectRootFolder = pathFile; + } else { + projectRootFolder = new File(DEPLOYED_CONFIG_ROOT,path); + } + } else { + projectRootFolder = new File(DEPLOYED_CONFIG_ROOT); + } + ArrayList locatedFiles = new ArrayList(); + locateFiles(fileName, locatedFiles, projectRootFolder, false, isDirectory); + if (locatedFiles.size() == 0) { + try { + Enumeration enumeration = FileLocator.class.getClassLoader().getResources(fileName); + while (enumeration.hasMoreElements()) { + URL resource = enumeration.nextElement(); + if (resource.getFile().contains(".jar!") && !isDirectory) { // resource is a file inside a jar + locatedFiles.add(getTempFileFromResource(fileName, resource.openStream())); + } else { + locatedFiles.add(new File(resource.getFile())); + } + } + } catch (IOException e) { + // log the error and return an empty array + LOGGER.error("Unable to locate files that match: " + fileName, e); + } + } + return (File[])locatedFiles.toArray(new File[locatedFiles.size()]); + } /** - * Helper method to create a physical temporary file containing the contents read from the specified InputStream - * @param fileName the File name prefix for the temporary file - * @param input the InputStream to read contents from - * @return temporary File - * @throws IOException - */ + * Helper method to create a physical temporary file containing the contents read from the specified InputStream + * @param fileName the File name prefix for the temporary file + * @param input the InputStream to read contents from + * @return temporary File + * @throws IOException + */ private static File getTempFileFromResource(String fileName, InputStream input) throws IOException { - File tempFile = File.createTempFile(fileName, null); - OutputStream out = new FileOutputStream(tempFile); + File tempFile = File.createTempFile(fileName, null); + OutputStream out = new FileOutputStream(tempFile); int read; byte[] bytes = new byte[1024]; while ((read = input.read(bytes)) != -1) { @@ -189,39 +189,39 @@ private static File getTempFileFromResource(String fileName, InputStream input) out.close(); input.close(); tempFile.deleteOnExit(); - return tempFile; + return tempFile; } - /** - * Helper method to recursively look for files with the specified name - * under the specified folder or match the specified name with the specified file name. - * @param fileName the name of the file to search for - * @param locatedFiles ArrayList containing File instances post matching - * @param isConfigFolder true if the specified file is a config folder or one of its sub folders - * @param isDirectory if true, find only directory - * if false, find only file. - * @param file file or folder to match filename against - */ - private static void locateFiles(String fileName, ArrayList locatedFiles, - File file, boolean isConfigFolder, boolean isDirectory) { - // trim any leading and trailing spaces in the specified file name - fileName = fileName.trim(); - if (file.exists()) { - if (file.isDirectory()) { - if (!isConfigFolder) { - isConfigFolder = file.getName().equalsIgnoreCase(Constants.CONFIG_ROOT); - } - File[] files = file.listFiles(); - for (int i = 0; i < files.length; i++) { - locateFiles(fileName, locatedFiles, files[i], isConfigFolder, isDirectory); - } - } - if (isConfigFolder && file.getName().equalsIgnoreCase(fileName)) { - if(isDirectory ^ file.isFile()) { - locatedFiles.add(file); - } - } - } - } + /** + * Helper method to recursively look for files with the specified name + * under the specified folder or match the specified name with the specified file name. + * @param fileName the name of the file to search for + * @param locatedFiles ArrayList containing File instances post matching + * @param isConfigFolder true if the specified file is a config folder or one of its sub folders + * @param isDirectory if true, find only directory + * if false, find only file. + * @param file file or folder to match filename against + */ + private static void locateFiles(String fileName, ArrayList locatedFiles, + File file, boolean isConfigFolder, boolean isDirectory) { + // trim any leading and trailing spaces in the specified file name + fileName = fileName.trim(); + if (file.exists()) { + if (file.isDirectory()) { + if (!isConfigFolder) { + isConfigFolder = file.getName().equalsIgnoreCase(Constants.CONFIG_ROOT); + } + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + locateFiles(fileName, locatedFiles, files[i], isConfigFolder, isDirectory); + } + } + if (isConfigFolder && file.getName().equalsIgnoreCase(fileName)) { + if(isDirectory ^ file.isFile()) { + locatedFiles.add(file); + } + } + } + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/config/YamlConfigurationFactory.java b/core/src/main/java/com/flipkart/gjex/core/config/YamlConfigurationFactory.java index b4ab09a9..d1555ee4 100644 --- a/core/src/main/java/com/flipkart/gjex/core/config/YamlConfigurationFactory.java +++ b/core/src/main/java/com/flipkart/gjex/core/config/YamlConfigurationFactory.java @@ -31,12 +31,12 @@ public class YamlConfigurationFactory extends BaseConfigurationFactory { /** - * Creates a new configuration factory for the given class. - * - * @param klass the configuration class - * @param validator the validator to use - * @param objectMapper the Jackson {@link ObjectMapper} to use - */ + * Creates a new configuration factory for the given class. + * + * @param klass the configuration class + * @param validator the validator to use + * @param objectMapper the Jackson {@link ObjectMapper} to use + */ public YamlConfigurationFactory(Class klass, Validator validator, ObjectMapper objectMapper) { diff --git a/core/src/main/java/com/flipkart/gjex/core/context/GJEXContext.java b/core/src/main/java/com/flipkart/gjex/core/context/GJEXContext.java index 69f37460..e3240d83 100644 --- a/core/src/main/java/com/flipkart/gjex/core/context/GJEXContext.java +++ b/core/src/main/java/com/flipkart/gjex/core/context/GJEXContext.java @@ -30,71 +30,71 @@ */ public class GJEXContext { - public static final String KEY_ROOT_SPAN_NAME = "io.opentracing.root-span"; - public static final String KEY_ACTIVE_SPAN_NAME = "io.opentracing.active-span"; - public static final String KEY_CONTEXT_NAME = "io.opentracing.active-span-context"; - public static final String KEY_TRACING_SAMPLER_NAME = "io.opentracing.active-tracing-sampler"; - public static final String KEY_HEADERS_NAME = "com.flipkart.gjex.headers"; + public static final String KEY_ROOT_SPAN_NAME = "io.opentracing.root-span"; + public static final String KEY_ACTIVE_SPAN_NAME = "io.opentracing.active-span"; + public static final String KEY_CONTEXT_NAME = "io.opentracing.active-span-context"; + public static final String KEY_TRACING_SAMPLER_NAME = "io.opentracing.active-tracing-sampler"; + public static final String KEY_HEADERS_NAME = "com.flipkart.gjex.headers"; - private static final Context.Key KEY_ROOT_SPAN = Context.key(KEY_ROOT_SPAN_NAME); - private static final Context.Key KEY_ACTIVE_SPAN = Context.key(KEY_ACTIVE_SPAN_NAME); - private static final Context.Key KEY_CONTEXT = Context.key(KEY_CONTEXT_NAME); - private static final Context.Key KEY_TRACING_SAMPLER = Context.key(KEY_TRACING_SAMPLER_NAME); - private static final Context.Key KEY_HEADERS = Context.key(KEY_HEADERS_NAME); + private static final Context.Key KEY_ROOT_SPAN = Context.key(KEY_ROOT_SPAN_NAME); + private static final Context.Key KEY_ACTIVE_SPAN = Context.key(KEY_ACTIVE_SPAN_NAME); + private static final Context.Key KEY_CONTEXT = Context.key(KEY_CONTEXT_NAME); + private static final Context.Key KEY_TRACING_SAMPLER = Context.key(KEY_TRACING_SAMPLER_NAME); + private static final Context.Key KEY_HEADERS = Context.key(KEY_HEADERS_NAME); - /** - * @return the OpenTracing context key for Root span - */ - public static Context.Key getKeyRoot() { - return KEY_ROOT_SPAN; - } - /** - * @return the active root span for the current request - */ - public static Span activeRootSpan() { - return KEY_ROOT_SPAN.get(); - } - /** - * @return the OpenTracing context key for Active span - */ - public static Context.Key getKeyActiveSpan() { - return KEY_ACTIVE_SPAN; - } - /** - * @return the active span for the current request - */ - public static Span activeSpan() { - return KEY_ACTIVE_SPAN.get(); - } + /** + * @return the OpenTracing context key for Root span + */ + public static Context.Key getKeyRoot() { + return KEY_ROOT_SPAN; + } + /** + * @return the active root span for the current request + */ + public static Span activeRootSpan() { + return KEY_ROOT_SPAN.get(); + } + /** + * @return the OpenTracing context key for Active span + */ + public static Context.Key getKeyActiveSpan() { + return KEY_ACTIVE_SPAN; + } + /** + * @return the active span for the current request + */ + public static Span activeSpan() { + return KEY_ACTIVE_SPAN.get(); + } - /** - * @return the OpenTracing context key for span context - */ - public static Context.Key getSpanContextKey() { - return KEY_CONTEXT; - } - public static SpanContext activeSpanContext() { - return KEY_CONTEXT.get(); - } + /** + * @return the OpenTracing context key for span context + */ + public static Context.Key getSpanContextKey() { + return KEY_CONTEXT; + } + public static SpanContext activeSpanContext() { + return KEY_CONTEXT.get(); + } - /** - * @return the GJEX TracingSampler key and active sampler - */ - public static Context.Key getTracingSamplerKey() { - return KEY_TRACING_SAMPLER; - } - public static TracingSampler activeTracingSampler() { - return KEY_TRACING_SAMPLER.get(); - } + /** + * @return the GJEX TracingSampler key and active sampler + */ + public static Context.Key getTracingSamplerKey() { + return KEY_TRACING_SAMPLER; + } + public static TracingSampler activeTracingSampler() { + return KEY_TRACING_SAMPLER.get(); + } - /** - * @return the GJEX headers key and forwarded headers - */ - public static Context.Key getHeadersKey() { - return KEY_HEADERS; - } - public static Metadata activeHeaders() { - return KEY_HEADERS.get(); - } + /** + * @return the GJEX headers key and forwarded headers + */ + public static Context.Key getHeadersKey() { + return KEY_HEADERS; + } + public static Metadata activeHeaders() { + return KEY_HEADERS.get(); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/Filter.java b/core/src/main/java/com/flipkart/gjex/core/filter/Filter.java index 218204d8..fd0860b9 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/Filter.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/Filter.java @@ -29,23 +29,23 @@ public abstract class Filter { public void destroy(){} /** - * Call-back to decorate or inspect the Request body/message. This Filter cannot fail processing of the Request body and hence there is no support for indicating failure. - * This method should be viewed almost like a proxy for the Request body. - * @param req the Request body/message - * @param requestParams prams - */ + * Call-back to decorate or inspect the Request body/message. This Filter cannot fail processing of the Request body and hence there is no support for indicating failure. + * This method should be viewed almost like a proxy for the Request body. + * @param req the Request body/message + * @param requestParams prams + */ public void doProcessRequest(Req req, RequestParams requestParams){} /** - * Call-back to decorate or inspect the Response headers. Implementations may use this method to set additional headers in the response. - * @param responseHeaders the Response Headers - */ + * Call-back to decorate or inspect the Response headers. Implementations may use this method to set additional headers in the response. + * @param responseHeaders the Response Headers + */ public void doProcessResponseHeaders(M responseHeaders) {} /** - * Call-back to decorate or inspect the Response body/message. This Filter cannot fail processing of the Response body and hence there is no support for indicating failure. - * This method should be viewed almost like a proxy for the Response body. - * @param response the Response body/message - */ + * Call-back to decorate or inspect the Response body/message. This Filter cannot fail processing of the Response body and hence there is no support for indicating failure. + * This method should be viewed almost like a proxy for the Response body. + * @param response the Response body/message + */ public void doProcessResponse(Res response) {} } diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/AccessLogGrpcFilter.java b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/AccessLogGrpcFilter.java index f0125ba4..88f746fd 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/AccessLogGrpcFilter.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/AccessLogGrpcFilter.java @@ -52,11 +52,11 @@ public class AccessLogGrpcFilter requestParamsInput) { startTime = System.currentTimeMillis(); @@ -64,37 +64,37 @@ public void doProcessRequest(R req, RequestParams requestParamsInput) } /** - * Placeholder method for processing response headers. Currently does not perform any operations. - * - * @param responseHeaders The metadata associated with the gRPC response. - */ + * Placeholder method for processing response headers. Currently does not perform any operations. + * + * @param responseHeaders The metadata associated with the gRPC response. + */ @Override public void doProcessResponseHeaders(Metadata responseHeaders) {} /** - * Processes the outgoing gRPC response by logging relevant request and response details. - * Logs the client IP, requested resource path, size of the response message, and the time taken to process the request. - * - * @param response The outgoing gRPC response message. - */ + * Processes the outgoing gRPC response by logging relevant request and response details. + * Logs the client IP, requested resource path, size of the response message, and the time taken to process the request. + * + * @param response The outgoing gRPC response message. + */ @Override public void doProcessResponse(S response) { String size = null; if (response != null){ - size = String.valueOf(response.getSerializedSize()); + size = String.valueOf(response.getSerializedSize()); } if (logger.isInfoEnabled()){ - logger.info("{} {} {} {}", - requestParams.getClientIp(), requestParams.getResourcePath(), size, System.currentTimeMillis()-startTime); + logger.info("{} {} {} {}", + requestParams.getClientIp(), requestParams.getResourcePath(), size, System.currentTimeMillis()-startTime); } } /** - * Provides an instance of this filter. This method facilitates the creation of new instances of the - * AccessLogGrpcFilter for each gRPC call, ensuring thread safety and isolation of request data. - * - * @return A new instance of {@link AccessLogGrpcFilter}. - */ + * Provides an instance of this filter. This method facilitates the creation of new instances of the + * AccessLogGrpcFilter for each gRPC call, ensuring thread safety and isolation of request data. + * + * @return A new instance of {@link AccessLogGrpcFilter}. + */ @Override public GrpcFilter getInstance(){ return new AccessLogGrpcFilter<>(); diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/ApplicationHeaders.java b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/ApplicationHeaders.java index de22fff4..f74eb2cb 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/ApplicationHeaders.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/ApplicationHeaders.java @@ -27,12 +27,12 @@ */ public class ApplicationHeaders { - /** - * Returns forwarded headers, if any - * @return null or forwarded headers - */ - public static Metadata getHeaders() { - return GJEXContext.activeHeaders(); - } + /** + * Returns forwarded headers, if any + * @return null or forwarded headers + */ + public static Metadata getHeaders() { + return GJEXContext.activeHeaders(); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/GrpcFilter.java b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/GrpcFilter.java index ecf077c5..5c1aa8e3 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/GrpcFilter.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/GrpcFilter.java @@ -27,23 +27,23 @@ * @author ajay.jalgaonkar */ public abstract class GrpcFilter - extends Filter { + extends Filter { - /** Lifecycle methods for initializing and cleaning up resources used by this Filter*/ - public void init(){} + /** Lifecycle methods for initializing and cleaning up resources used by this Filter*/ + public void init(){} - /** - * Function for creating an instance of this {@link Filter} - * Use only this function to get the {@link Filter} instance - */ - public abstract GrpcFilter getInstance(); + /** + * Function for creating an instance of this {@link Filter} + * Use only this function to get the {@link Filter} instance + */ + public abstract GrpcFilter getInstance(); - /** - * Returns array of {@link Metadata.Key} identifiers for headers to be forwarded - * @return array of {@link Metadata.Key} - */ - @SuppressWarnings("rawtypes") - public Metadata.Key[] getForwardHeaderKeys(){ - return new Metadata.Key[] {}; - } + /** + * Returns array of {@link Metadata.Key} identifiers for headers to be forwarded + * @return array of {@link Metadata.Key} + */ + @SuppressWarnings("rawtypes") + public Metadata.Key[] getForwardHeaderKeys(){ + return new Metadata.Key[] {}; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/MethodFilters.java b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/MethodFilters.java index 29a67e6e..34bb98af 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/grpc/MethodFilters.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/grpc/MethodFilters.java @@ -30,7 +30,7 @@ @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodFilters { - // Not parameterizing Filter here as the Language doesnot support it for Annotations - @SuppressWarnings("rawtypes") - public Class[] value(); + // Not parameterizing Filter here as the Language doesnot support it for Annotations + @SuppressWarnings("rawtypes") + public Class[] value(); } diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/http/AccessLogHttpFilter.java b/core/src/main/java/com/flipkart/gjex/core/filter/http/AccessLogHttpFilter.java index be580418..08800cbc 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/http/AccessLogHttpFilter.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/http/AccessLogHttpFilter.java @@ -45,11 +45,11 @@ public HttpFilter getInstance() { } /** - * Processes the incoming request by initializing the start time and storing the request parameters. - * - * @param req The incoming servlet request. - * @param requestParamsInput Parameters of the request, including client IP and any additional metadata. - */ + * Processes the incoming request by initializing the start time and storing the request parameters. + * + * @param req The incoming servlet request. + * @param requestParamsInput Parameters of the request, including client IP and any additional metadata. + */ @Override public void doProcessRequest(ServletRequest req, RequestParams> requestParamsInput) { startTime = System.currentTimeMillis(); @@ -57,22 +57,22 @@ public void doProcessRequest(ServletRequest req, RequestParams> requ } /** - * Processes the outgoing response by logging relevant request and response details. - * Logs the client IP, requested URI, response status, content length, and the time taken to process the request. - * - * @param response The outgoing servlet response. - */ + * Processes the outgoing response by logging relevant request and response details. + * Logs the client IP, requested URI, response status, content length, and the time taken to process the request. + * + * @param response The outgoing servlet response. + */ @Override public void doProcessResponse(ServletResponse response) { if (logger.isInfoEnabled()) { - HttpServletResponse httpServletResponse = (HttpServletResponse) response; - logger.info("{} {} {} {} {}", - requestParams.getClientIp(), - requestParams.getResourcePath(), - httpServletResponse.getStatus(), - httpServletResponse.getHeader(CONTENT_LENGTH_HEADER), - System.currentTimeMillis() - startTime - ); + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + logger.info("{} {} {} {} {}", + requestParams.getClientIp(), + requestParams.getResourcePath(), + httpServletResponse.getStatus(), + httpServletResponse.getHeader(CONTENT_LENGTH_HEADER), + System.currentTimeMillis() - startTime + ); } } diff --git a/core/src/main/java/com/flipkart/gjex/core/filter/http/HttpFilter.java b/core/src/main/java/com/flipkart/gjex/core/filter/http/HttpFilter.java index d2415ee5..9645c4ff 100644 --- a/core/src/main/java/com/flipkart/gjex/core/filter/http/HttpFilter.java +++ b/core/src/main/java/com/flipkart/gjex/core/filter/http/HttpFilter.java @@ -41,9 +41,9 @@ public abstract class HttpFilter extends Filter> { /** - * Function for creating an instance of this {@link Filter} - * Use only this function to get the {@link Filter} instance - */ + * Function for creating an instance of this {@link Filter} + * Use only this function to get the {@link Filter} instance + */ public abstract HttpFilter getInstance(); } diff --git a/core/src/main/java/com/flipkart/gjex/core/healthcheck/GrpcHealthCheckService.java b/core/src/main/java/com/flipkart/gjex/core/healthcheck/GrpcHealthCheckService.java index 9a7ec961..e3886961 100644 --- a/core/src/main/java/com/flipkart/gjex/core/healthcheck/GrpcHealthCheckService.java +++ b/core/src/main/java/com/flipkart/gjex/core/healthcheck/GrpcHealthCheckService.java @@ -29,9 +29,9 @@ public void check(HealthCheckRequest request, StreamObserver responseObserver) { HealthCheckResponse.Builder builder = HealthCheckResponse.newBuilder(); if (rotationManagementBasedHealthCheck.inRotation()) { - builder.setStatus(HealthCheckResponse.ServingStatus.SERVING); + builder.setStatus(HealthCheckResponse.ServingStatus.SERVING); } else { - builder.setStatus(HealthCheckResponse.ServingStatus.NOT_SERVING); + builder.setStatus(HealthCheckResponse.ServingStatus.NOT_SERVING); } responseObserver.onNext(builder.build()); responseObserver.onCompleted(); diff --git a/core/src/main/java/com/flipkart/gjex/core/healthcheck/HealthCheckRegistry.java b/core/src/main/java/com/flipkart/gjex/core/healthcheck/HealthCheckRegistry.java index 13cbb63e..e78c580f 100644 --- a/core/src/main/java/com/flipkart/gjex/core/healthcheck/HealthCheckRegistry.java +++ b/core/src/main/java/com/flipkart/gjex/core/healthcheck/HealthCheckRegistry.java @@ -27,20 +27,20 @@ */ public class HealthCheckRegistry extends io.dropwizard.metrics5.health.HealthCheckRegistry { - /** Name for this HealthCheckRegistry*/ - public static final String HEALTHCHECK_REGISTRY_NAME = "GJEX_HealthCheckRegistry"; + /** Name for this HealthCheckRegistry*/ + public static final String HEALTHCHECK_REGISTRY_NAME = "GJEX_HealthCheckRegistry"; - private ExecutorService executorService; + private ExecutorService executorService; public HealthCheckRegistry(ExecutorService executorService) { - this.executorService = executorService; - } + this.executorService = executorService; + } /** - * Runs HealthChecks concurrently using the ExecutorService - */ + * Runs HealthChecks concurrently using the ExecutorService + */ @Override public SortedMap runHealthChecks() { - return this.runHealthChecks(this.executorService); + return this.runHealthChecks(this.executorService); } } diff --git a/core/src/main/java/com/flipkart/gjex/core/healthcheck/RotationManagementBasedHealthCheck.java b/core/src/main/java/com/flipkart/gjex/core/healthcheck/RotationManagementBasedHealthCheck.java index 51166984..599dd484 100644 --- a/core/src/main/java/com/flipkart/gjex/core/healthcheck/RotationManagementBasedHealthCheck.java +++ b/core/src/main/java/com/flipkart/gjex/core/healthcheck/RotationManagementBasedHealthCheck.java @@ -22,9 +22,9 @@ public RotationManagementBasedHealthCheck() {} @Override protected Result check() { if (inRotation()) { - return Result.healthy("Server is " + getStatus()); + return Result.healthy("Server is " + getStatus()); } else { - return Result.unhealthy("Server is " + getStatus()); + return Result.unhealthy("Server is " + getStatus()); } } diff --git a/core/src/main/java/com/flipkart/gjex/core/job/ScheduledJob.java b/core/src/main/java/com/flipkart/gjex/core/job/ScheduledJob.java index f820dc7c..28714662 100644 --- a/core/src/main/java/com/flipkart/gjex/core/job/ScheduledJob.java +++ b/core/src/main/java/com/flipkart/gjex/core/job/ScheduledJob.java @@ -13,31 +13,31 @@ public abstract class ScheduledJob implements Runnable, Logging { public enum IntervalType { /** - * Schedule using {@link java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate} - */ + * Schedule using {@link java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate} + */ RATE, /** - * Schedule using {@link java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay} - */ + * Schedule using {@link java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay} + */ DELAY } /** - * @return interval type for scheduling - */ + * @return interval type for scheduling + */ @NotNull public abstract IntervalType getIntervalType(); /** - * @return interval for scheduling. Must be >= 1ms - */ + * @return interval for scheduling. Must be >= 1ms + */ @NotNull public abstract Duration getInterval(); /** - * Implement job logic here - */ + * Implement job logic here + */ public abstract void doJob(); @Override diff --git a/core/src/main/java/com/flipkart/gjex/core/logging/Logging.java b/core/src/main/java/com/flipkart/gjex/core/logging/Logging.java index 97fcf6b1..6dcc5d47 100644 --- a/core/src/main/java/com/flipkart/gjex/core/logging/Logging.java +++ b/core/src/main/java/com/flipkart/gjex/core/logging/Logging.java @@ -165,9 +165,9 @@ default void errorLog(String msg, Object... args){ } default void addToTrace(String key, String value) { - if (GJEXContext.activeSpan() != null) { - GJEXContext.activeSpan().setTag(key, value); - } + if (GJEXContext.activeSpan() != null) { + GJEXContext.activeSpan().setTag(key, value); + } } } diff --git a/core/src/main/java/com/flipkart/gjex/core/service/AbstractService.java b/core/src/main/java/com/flipkart/gjex/core/service/AbstractService.java index 99e93681..ec638c3b 100644 --- a/core/src/main/java/com/flipkart/gjex/core/service/AbstractService.java +++ b/core/src/main/java/com/flipkart/gjex/core/service/AbstractService.java @@ -26,163 +26,163 @@ */ public abstract class AbstractService implements Service, Logging { - public static final String STOPPED = "STOPPED"; - public static final String FAILED = "FAILED"; - public static final String STARTING = "STARTING"; - public static final String STARTED = "STARTED"; - public static final String STOPPING = "STOPPING"; - public static final String RUNNING = "RUNNING"; - - private static final int FAILED_STATE = -1, STOPPED_STATE = 0, STARTING_STATE = 1, STARTED_STATE = 2, STOPPING_STATE = 3; - - private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); - private final Object lock = new Object(); - private volatile int state = STOPPED_STATE; - private long stopTimeout = 30000; - - protected void doStart() throws Exception { - } - - protected void doStop() { - } - - @Override - public final void start() throws Exception { - synchronized (lock) { - try { - if (state == STARTED_STATE || state == STARTING_STATE) - return; - setStarting(); - doStart(); - setStarted(); - } catch (Throwable e) { - setFailed(e); - throw e; - } - } - } - - @Override - public final void stop() { - synchronized (lock) { - try { - if (state == STOPPING_STATE || state == STOPPED_STATE) - return; - setStopping(); - doStop(); - setStopped(); - } catch (Throwable e) { - setFailed(e); - } - } - } - - @Override - public boolean isRunning() { - final int tempState = state; - - return tempState == STARTED_STATE || state == STARTING_STATE; - } - - @Override - public boolean isStarted() { - return state == STARTED_STATE; - } - - @Override - public boolean isStarting() { - return state == STARTING_STATE; - } - - @Override - public boolean isStopping() { - return state == STOPPING_STATE; - } - - @Override - public boolean isStopped() { - return state == STOPPED_STATE; - } - - @Override - public boolean isFailed() { - return state == FAILED_STATE; - } - - @Override - public void addServiceListener(Service.Listener listener) { - listeners.add(listener); - } - - @Override - public void removeServiceListener(Service.Listener listener) { - listeners.remove(listener); - } - - public String getState() { - switch (state) { - case FAILED_STATE: - return FAILED; - case STARTING_STATE: - return STARTING; - case STARTED_STATE: - return STARTED; - case STOPPING_STATE: - return STOPPING; - case STOPPED_STATE: - return STOPPED; - } - return null; - } - - public static String getState(Service service) { - if (service.isStarting()) - return STARTING; - if (service.isStarted()) - return STARTED; - if (service.isStopping()) - return STOPPING; - if (service.isStopped()) - return STOPPED; - return FAILED; - } - - private void setStarted() { - state = STARTED_STATE; - logDebug(STARTED + " {}", null, this); - listeners.forEach(listener -> listener.serviceStarted(this)); - } - - private void setStarting() { - logDebug("starting {}", null, this); - state = STARTING_STATE; - listeners.forEach(listener -> listener.serviceStarting(this)); - } - - private void setStopping() { - logDebug("stopping {}", null, this); - state = STOPPING_STATE; - listeners.forEach(listener -> listener.serviceStopping(this)); - } - - private void setStopped() { - state = STOPPED_STATE; - logDebug("{} {}", null, STOPPED, this); - listeners.forEach(listener -> listener.serviceStopped(this)); - } - - private void setFailed(Throwable th) { - state = FAILED_STATE; - warnLog(FAILED + " " + this + ": " + th, th); - listeners.forEach(listener -> listener.serviceFailure(this, th)); - } - - public long getStopTimeout() { - return stopTimeout; - } - - public void setStopTimeout(long stopTimeout) { - this.stopTimeout = stopTimeout; - } + public static final String STOPPED = "STOPPED"; + public static final String FAILED = "FAILED"; + public static final String STARTING = "STARTING"; + public static final String STARTED = "STARTED"; + public static final String STOPPING = "STOPPING"; + public static final String RUNNING = "RUNNING"; + + private static final int FAILED_STATE = -1, STOPPED_STATE = 0, STARTING_STATE = 1, STARTED_STATE = 2, STOPPING_STATE = 3; + + private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); + private final Object lock = new Object(); + private volatile int state = STOPPED_STATE; + private long stopTimeout = 30000; + + protected void doStart() throws Exception { + } + + protected void doStop() { + } + + @Override + public final void start() throws Exception { + synchronized (lock) { + try { + if (state == STARTED_STATE || state == STARTING_STATE) + return; + setStarting(); + doStart(); + setStarted(); + } catch (Throwable e) { + setFailed(e); + throw e; + } + } + } + + @Override + public final void stop() { + synchronized (lock) { + try { + if (state == STOPPING_STATE || state == STOPPED_STATE) + return; + setStopping(); + doStop(); + setStopped(); + } catch (Throwable e) { + setFailed(e); + } + } + } + + @Override + public boolean isRunning() { + final int tempState = state; + + return tempState == STARTED_STATE || state == STARTING_STATE; + } + + @Override + public boolean isStarted() { + return state == STARTED_STATE; + } + + @Override + public boolean isStarting() { + return state == STARTING_STATE; + } + + @Override + public boolean isStopping() { + return state == STOPPING_STATE; + } + + @Override + public boolean isStopped() { + return state == STOPPED_STATE; + } + + @Override + public boolean isFailed() { + return state == FAILED_STATE; + } + + @Override + public void addServiceListener(Service.Listener listener) { + listeners.add(listener); + } + + @Override + public void removeServiceListener(Service.Listener listener) { + listeners.remove(listener); + } + + public String getState() { + switch (state) { + case FAILED_STATE: + return FAILED; + case STARTING_STATE: + return STARTING; + case STARTED_STATE: + return STARTED; + case STOPPING_STATE: + return STOPPING; + case STOPPED_STATE: + return STOPPED; + } + return null; + } + + public static String getState(Service service) { + if (service.isStarting()) + return STARTING; + if (service.isStarted()) + return STARTED; + if (service.isStopping()) + return STOPPING; + if (service.isStopped()) + return STOPPED; + return FAILED; + } + + private void setStarted() { + state = STARTED_STATE; + logDebug(STARTED + " {}", null, this); + listeners.forEach(listener -> listener.serviceStarted(this)); + } + + private void setStarting() { + logDebug("starting {}", null, this); + state = STARTING_STATE; + listeners.forEach(listener -> listener.serviceStarting(this)); + } + + private void setStopping() { + logDebug("stopping {}", null, this); + state = STOPPING_STATE; + listeners.forEach(listener -> listener.serviceStopping(this)); + } + + private void setStopped() { + state = STOPPED_STATE; + logDebug("{} {}", null, STOPPED, this); + listeners.forEach(listener -> listener.serviceStopped(this)); + } + + private void setFailed(Throwable th) { + state = FAILED_STATE; + warnLog(FAILED + " " + this + ": " + th, th); + listeners.forEach(listener -> listener.serviceFailure(this, th)); + } + + public long getStopTimeout() { + return stopTimeout; + } + + public void setStopTimeout(long stopTimeout) { + this.stopTimeout = stopTimeout; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/service/Api.java b/core/src/main/java/com/flipkart/gjex/core/service/Api.java index 1d9632fd..7709be54 100644 --- a/core/src/main/java/com/flipkart/gjex/core/service/Api.java +++ b/core/src/main/java/com/flipkart/gjex/core/service/Api.java @@ -33,14 +33,14 @@ @Documented public @interface Api { - /** - * Defines the maximum time GJEX can "expect" the Api to run for. This value is used to set the gRPC Context's Deadline and is evaluated when invoking {@link ConcurrentTask} - * executions within Api methods. - */ + /** + * Defines the maximum time GJEX can "expect" the Api to run for. This value is used to set the gRPC Context's Deadline and is evaluated when invoking {@link ConcurrentTask} + * executions within Api methods. + */ int deadline() default 0; /** - * Deadline configured as a Config property. Note that {@link Api#deadline()} overrides this value - */ + * Deadline configured as a Config property. Note that {@link Api#deadline()} overrides this value + */ String deadlineConfig() default ""; } diff --git a/core/src/main/java/com/flipkart/gjex/core/service/Service.java b/core/src/main/java/com/flipkart/gjex/core/service/Service.java index e2c6680a..978a58f8 100644 --- a/core/src/main/java/com/flipkart/gjex/core/service/Service.java +++ b/core/src/main/java/com/flipkart/gjex/core/service/Service.java @@ -29,61 +29,61 @@ public interface Service { - /** - * Starts the service. This method blocks until the service has completely started. - */ - void start() throws Exception; + /** + * Starts the service. This method blocks until the service has completely started. + */ + void start() throws Exception; - /** - * Stops the service. This method blocks until the service has completely shut down. - */ - void stop(); + /** + * Stops the service. This method blocks until the service has completely shut down. + */ + void stop(); - /** - * @return true if the Service is starting or has been started. - */ - boolean isRunning(); + /** + * @return true if the Service is starting or has been started. + */ + boolean isRunning(); - /** - * @return true if the Service has been started. - * @see #start() - * @see #isStarting() - */ + /** + * @return true if the Service has been started. + * @see #start() + * @see #isStarting() + */ boolean isStarted(); /** - * @return true if the Service is starting. - * @see #isStarted() - */ + * @return true if the Service is starting. + * @see #isStarted() + */ boolean isStarting(); /** - * @return true if the Service is stopping. - * @see #isStopped() - */ + * @return true if the Service is stopping. + * @see #isStopped() + */ boolean isStopping(); /** - * @return true if the Service has been stopped. - * @see #stop() - * @see #isStopping() - */ + * @return true if the Service has been stopped. + * @see #stop() + * @see #isStopping() + */ boolean isStopped(); /** - * @return true if the Service has failed to start or has failed to stop. - */ + * @return true if the Service has failed to start or has failed to stop. + */ boolean isFailed(); /** - * Methods to add/remove Service lifecycle listeners. - */ + * Methods to add/remove Service lifecycle listeners. + */ public void addServiceListener(Service.Listener listener); public void removeServiceListener(Service.Listener listener); /** - * A listener for Service events. - */ + * A listener for Service events. + */ public interface Listener extends EventListener { public default void serviceStarting(Service service) {} diff --git a/core/src/main/java/com/flipkart/gjex/core/setup/Bootstrap.java b/core/src/main/java/com/flipkart/gjex/core/setup/Bootstrap.java index 7085125e..f38d95ae 100644 --- a/core/src/main/java/com/flipkart/gjex/core/setup/Bootstrap.java +++ b/core/src/main/java/com/flipkart/gjex/core/setup/Bootstrap.java @@ -56,168 +56,168 @@ @SuppressWarnings("rawtypes") public class Bootstrap implements Logging { - private final Application application; - private final AppMetricsRegistry appMetricsRegistry; - private final List> bundles; - private final ObjectMapper objectMapper; - private final String configPath; - private final Class configurationClass; - - private ClassLoader classLoader; - - private ConfigurationFactoryFactory configurationFactoryFactory; - private ConfigurationSourceProvider configurationSourceProvider; - private ValidatorFactory validatorFactory; - - // these values are set in ConfigModule when GuiceBundle.initialize(this) gets called. - private T configuration; - private U configMap; - - /** List of initialized Service instances*/ - private List services; - - /** List of initialized Filter instances*/ - private List grpcFilters; - - /** List of initialized ConfigurableTracingSampler instances*/ - private List tracingSamplers; - - /** List of initialized ScheduledJob instances*/ - private List scheduledJobs; - - /** The HealthCheckRegistry*/ - private HealthCheckRegistry healthCheckRegistry; - - public Bootstrap(Application application, String configPath, Class configurationClass) { - this.configurationClass = configurationClass; - this.configPath = configPath; - this.application = application; - this.appMetricsRegistry = new AppMetricsRegistry(new MetricRegistry(), PrometheusRegistry.defaultRegistry); - this.bundles = Lists.newArrayList(); - this.objectMapper = GJEXObjectMapper.newObjectMapper(); - this.classLoader = Thread.currentThread().getContextClassLoader(); - this.configurationFactoryFactory = new DefaultConfigurationFactoryFactory<>(); - this.configurationSourceProvider = new FileConfigurationSourceProvider(); - this.validatorFactory = Validation.buildDefaultValidatorFactory(); - this.initializeConfig(); - } - - /** - * Gets the bootstrap's Application - */ - public Application getApplication() { - return application; - } - - /** - * Returns the bootstrap's class loader. - */ + private final Application application; + private final AppMetricsRegistry appMetricsRegistry; + private final List> bundles; + private final ObjectMapper objectMapper; + private final String configPath; + private final Class configurationClass; + + private ClassLoader classLoader; + + private ConfigurationFactoryFactory configurationFactoryFactory; + private ConfigurationSourceProvider configurationSourceProvider; + private ValidatorFactory validatorFactory; + + // these values are set in ConfigModule when GuiceBundle.initialize(this) gets called. + private T configuration; + private U configMap; + + /** List of initialized Service instances*/ + private List services; + + /** List of initialized Filter instances*/ + private List grpcFilters; + + /** List of initialized ConfigurableTracingSampler instances*/ + private List tracingSamplers; + + /** List of initialized ScheduledJob instances*/ + private List scheduledJobs; + + /** The HealthCheckRegistry*/ + private HealthCheckRegistry healthCheckRegistry; + + public Bootstrap(Application application, String configPath, Class configurationClass) { + this.configurationClass = configurationClass; + this.configPath = configPath; + this.application = application; + this.appMetricsRegistry = new AppMetricsRegistry(new MetricRegistry(), PrometheusRegistry.defaultRegistry); + this.bundles = Lists.newArrayList(); + this.objectMapper = GJEXObjectMapper.newObjectMapper(); + this.classLoader = Thread.currentThread().getContextClassLoader(); + this.configurationFactoryFactory = new DefaultConfigurationFactoryFactory<>(); + this.configurationSourceProvider = new FileConfigurationSourceProvider(); + this.validatorFactory = Validation.buildDefaultValidatorFactory(); + this.initializeConfig(); + } + + /** + * Gets the bootstrap's Application + */ + public Application getApplication() { + return application; + } + + /** + * Returns the bootstrap's class loader. + */ public ClassLoader getClassLoader() { return classLoader; } /** - * Sets the bootstrap's class loader. - */ + * Sets the bootstrap's class loader. + */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } /** - * Adds the given bundle to the bootstrap. - * - * @param bundle a {@link Bundle} - */ + * Adds the given bundle to the bootstrap. + * + * @param bundle a {@link Bundle} + */ public void addBundle(Bundle bundle) { - Preconditions.checkState(!bundles.contains(bundle), - "bundles can be initialized only once"); + Preconditions.checkState(!bundles.contains(bundle), + "bundles can be initialized only once"); bundle.initialize(this); bundles.add(bundle); } - public String getConfigPath() { - return configPath; - } + public String getConfigPath() { + return configPath; + } - /** - * Returns the application's metrics. - */ + /** + * Returns the application's metrics. + */ public MetricRegistry getMetricRegistry() { return appMetricsRegistry.getMetricRegistry(); } - public AppMetricsRegistry getAppMetricsRegistry() { - return appMetricsRegistry; - } + public AppMetricsRegistry getAppMetricsRegistry() { + return appMetricsRegistry; + } - public List getServices() { - return services; - } + public List getServices() { + return services; + } - public List getFilters() { - return grpcFilters; - } + public List getFilters() { + return grpcFilters; + } - public List getTracingSamplers() { - return tracingSamplers; - } + public List getTracingSamplers() { + return tracingSamplers; + } - public ConfigurationFactoryFactory getConfigurationFactoryFactory() { - return configurationFactoryFactory; - } + public ConfigurationFactoryFactory getConfigurationFactoryFactory() { + return configurationFactoryFactory; + } - public void setConfigurationFactoryFactory(ConfigurationFactoryFactory configurationFactoryFactory) { - this.configurationFactoryFactory = configurationFactoryFactory; - } + public void setConfigurationFactoryFactory(ConfigurationFactoryFactory configurationFactoryFactory) { + this.configurationFactoryFactory = configurationFactoryFactory; + } - public ConfigurationSourceProvider getConfigurationSourceProvider() { - return configurationSourceProvider; - } + public ConfigurationSourceProvider getConfigurationSourceProvider() { + return configurationSourceProvider; + } - public void setConfigurationSourceProvider(ConfigurationSourceProvider configurationSourceProvider) { - this.configurationSourceProvider = configurationSourceProvider; - } + public void setConfigurationSourceProvider(ConfigurationSourceProvider configurationSourceProvider) { + this.configurationSourceProvider = configurationSourceProvider; + } - public ValidatorFactory getValidatorFactory() { - return validatorFactory; - } + public ValidatorFactory getValidatorFactory() { + return validatorFactory; + } - public void setValidatorFactory(ValidatorFactory validatorFactory) { - this.validatorFactory = validatorFactory; - } + public void setValidatorFactory(ValidatorFactory validatorFactory) { + this.validatorFactory = validatorFactory; + } - public ObjectMapper getObjectMapper() { - return objectMapper; - } + public ObjectMapper getObjectMapper() { + return objectMapper; + } - public Class getConfigurationClass() { - return configurationClass; - } + public Class getConfigurationClass() { + return configurationClass; + } - public T getConfiguration() { - return configuration; - } + public T getConfiguration() { + return configuration; + } - public void setConfiguration(T configuration) { - this.configuration = configuration; - } + public void setConfiguration(T configuration) { + this.configuration = configuration; + } - public U getConfigMap() { - return configMap; - } + public U getConfigMap() { + return configMap; + } - public void setConfigMap(U configMap) { - this.configMap = configMap; - } + public void setConfigMap(U configMap) { + this.configMap = configMap; + } - /** - * Runs this Bootstrap's bundles in the specified Environment - * @param environment the Application Environment - * @throws Exception in case of errors during run - */ - public void run(Environment environment) throws Exception { - // Identify all Service implementations, start them and register for Runtime shutdown hook + /** + * Runs this Bootstrap's bundles in the specified Environment + * @param environment the Application Environment + * @throws Exception in case of errors during run + */ + public void run(Environment environment) throws Exception { + // Identify all Service implementations, start them and register for Runtime shutdown hook services = new ArrayList<>(); grpcFilters = new ArrayList(); tracingSamplers = new ArrayList(); @@ -234,66 +234,66 @@ public void run(Environment environment) throws Exception { // Register all HealthChecks with the HealthCheckRegistry bundle.getHealthChecks().forEach(hc -> this.healthCheckRegistry.register(hc.getClass().getSimpleName(), hc)); } - services.forEach(service -> { - try { - service.start(); - } catch (Exception e) { - error("Error starting a Service : " + service.getClass().getName(), e); + services.forEach(service -> { + try { + service.start(); + } catch (Exception e) { + error("Error starting a Service : " + service.getClass().getName(), e); throw new RuntimeException(e); - } - }); - grpcFilters.forEach(filter -> { - try { - filter.init(); - } catch (Exception e) { - error("Error initializing a Filter : " + filter.getClass().getName(), e); + } + }); + grpcFilters.forEach(filter -> { + try { + filter.init(); + } catch (Exception e) { + error("Error initializing a Filter : " + filter.getClass().getName(), e); throw new RuntimeException(e); - } - }); - registerServicesForShutdown(); + } + }); + registerServicesForShutdown(); } public HealthCheckRegistry getHealthCheckRegistry() { - return this.healthCheckRegistry; - } + return this.healthCheckRegistry; + } private void registerServicesForShutdown() throws Exception { - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - // Use stdout here since the logger may have been reset by its JVM shutdown hook. - System.out.println("*** Shutting down GJEX server since JVM is shutting down"); - services.forEach(Service::stop); - grpcFilters.forEach(GrpcFilter::destroy); - System.out.println("*** Server shut down"); - } - }); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // Use stdout here since the logger may have been reset by its JVM shutdown hook. + System.out.println("*** Shutting down GJEX server since JVM is shutting down"); + services.forEach(Service::stop); + grpcFilters.forEach(GrpcFilter::destroy); + System.out.println("*** Server shut down"); + } + }); } private void initializeConfig() { - try { - Pair pair = parseConfiguration(this.getConfigurationFactoryFactory(), this.getConfigurationSourceProvider(), - this.getValidatorFactory().getValidator(), this.getConfigPath(), this.getConfigurationClass(), - this.getObjectMapper()); - this.setConfiguration(pair.getKey()); // NOTE - this.setConfigMap(pair.getValue()); // NOTE - } catch (ConfigurationException | IOException e) { - throw new GJEXError(GJEXError.ErrorType.runtime, "Error occurred while reading/parsing configuration " + - "from source " + this.getConfigPath(), e); - } - } - - private Pair parseConfiguration(ConfigurationFactoryFactory configurationFactoryFactory, - ConfigurationSourceProvider provider, - Validator validator, - String configPath, - Class klass, - ObjectMapper objectMapper) throws IOException, ConfigurationException { - final ConfigurationFactory configurationFactory = configurationFactoryFactory - .create(klass, validator, objectMapper); - if (configPath != null) { - return configurationFactory.build(provider, configPath); - } - return configurationFactory.build(); - } + try { + Pair pair = parseConfiguration(this.getConfigurationFactoryFactory(), this.getConfigurationSourceProvider(), + this.getValidatorFactory().getValidator(), this.getConfigPath(), this.getConfigurationClass(), + this.getObjectMapper()); + this.setConfiguration(pair.getKey()); // NOTE + this.setConfigMap(pair.getValue()); // NOTE + } catch (ConfigurationException | IOException e) { + throw new GJEXError(GJEXError.ErrorType.runtime, "Error occurred while reading/parsing configuration " + + "from source " + this.getConfigPath(), e); + } + } + + private Pair parseConfiguration(ConfigurationFactoryFactory configurationFactoryFactory, + ConfigurationSourceProvider provider, + Validator validator, + String configPath, + Class klass, + ObjectMapper objectMapper) throws IOException, ConfigurationException { + final ConfigurationFactory configurationFactory = configurationFactoryFactory + .create(klass, validator, objectMapper); + if (configPath != null) { + return configurationFactory.build(provider, configPath); + } + return configurationFactory.build(); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/setup/Environment.java b/core/src/main/java/com/flipkart/gjex/core/setup/Environment.java index 4d7dd028..aadf1860 100644 --- a/core/src/main/java/com/flipkart/gjex/core/setup/Environment.java +++ b/core/src/main/java/com/flipkart/gjex/core/setup/Environment.java @@ -30,31 +30,31 @@ */ public class Environment { - private final String name; + private final String name; private final MetricRegistry metricRegistry; private final HealthCheckRegistry healthCheckRegistry; public Environment(String name, MetricRegistry metricRegistry) { - this.name = name; - this.metricRegistry = metricRegistry; + this.name = name; + this.metricRegistry = metricRegistry; - // Creating a cached threadpool as the number of HealthCheck instances are anyway unknown and hence no point in bounding it to a number - this.healthCheckRegistry = new HealthCheckRegistry(Executors.newCachedThreadPool(new NamedThreadFactory("GJEX-healthcheck-"))); + // Creating a cached threadpool as the number of HealthCheck instances are anyway unknown and hence no point in bounding it to a number + this.healthCheckRegistry = new HealthCheckRegistry(Executors.newCachedThreadPool(new NamedThreadFactory("GJEX-healthcheck-"))); } - public String getName() { - return name; - } + public String getName() { + return name; + } - public MetricRegistry getMetricRegistry() { - return metricRegistry; - } + public MetricRegistry getMetricRegistry() { + return metricRegistry; + } - public HealthCheckRegistry getHealthCheckRegistry() { - return healthCheckRegistry; - } + public HealthCheckRegistry getHealthCheckRegistry() { + return healthCheckRegistry; + } - private static class NamedThreadFactory implements ThreadFactory { + private static class NamedThreadFactory implements ThreadFactory { private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); diff --git a/core/src/main/java/com/flipkart/gjex/core/task/AsyncResult.java b/core/src/main/java/com/flipkart/gjex/core/task/AsyncResult.java index 34476a56..6019b146 100644 --- a/core/src/main/java/com/flipkart/gjex/core/task/AsyncResult.java +++ b/core/src/main/java/com/flipkart/gjex/core/task/AsyncResult.java @@ -26,36 +26,36 @@ */ public abstract class AsyncResult implements Future { - private static final String ERROR_MSG = "AsyncResult is just a stab and cannot be used as complete implementation of Future"; - - @Override - public boolean cancel(boolean mayInterruptIfRunning) throws UnsupportedOperationException { - throw new UnsupportedOperationException(ERROR_MSG); - } - - @Override - public boolean isCancelled() throws UnsupportedOperationException { - throw new UnsupportedOperationException(ERROR_MSG); - } - - @Override - public boolean isDone() throws UnsupportedOperationException { - throw new UnsupportedOperationException(ERROR_MSG); - } - - @Override - public T get() throws UnsupportedOperationException { - throw new UnsupportedOperationException(ERROR_MSG); - } - - @Override - public T get(long timeout, TimeUnit unit) throws UnsupportedOperationException { - throw new UnsupportedOperationException(ERROR_MSG); - } - - /** - * Abstract method to wrap logic for an asynchronous call - */ - public abstract T invoke(); + private static final String ERROR_MSG = "AsyncResult is just a stab and cannot be used as complete implementation of Future"; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) throws UnsupportedOperationException { + throw new UnsupportedOperationException(ERROR_MSG); + } + + @Override + public boolean isCancelled() throws UnsupportedOperationException { + throw new UnsupportedOperationException(ERROR_MSG); + } + + @Override + public boolean isDone() throws UnsupportedOperationException { + throw new UnsupportedOperationException(ERROR_MSG); + } + + @Override + public T get() throws UnsupportedOperationException { + throw new UnsupportedOperationException(ERROR_MSG); + } + + @Override + public T get(long timeout, TimeUnit unit) throws UnsupportedOperationException { + throw new UnsupportedOperationException(ERROR_MSG); + } + + /** + * Abstract method to wrap logic for an asynchronous call + */ + public abstract T invoke(); } diff --git a/core/src/main/java/com/flipkart/gjex/core/task/ConcurrentTask.java b/core/src/main/java/com/flipkart/gjex/core/task/ConcurrentTask.java index 7234abc3..562c6060 100644 --- a/core/src/main/java/com/flipkart/gjex/core/task/ConcurrentTask.java +++ b/core/src/main/java/com/flipkart/gjex/core/task/ConcurrentTask.java @@ -32,44 +32,43 @@ @Documented public @interface ConcurrentTask { - /** Possible completion values*/ + /** Possible completion values*/ public enum Completion { - Mandatory, Optional; + Mandatory, Optional; } - /** - * Indicates whether the successful completion of this ConcurrentTask is Mandatory or Optional when composing any final responses - * from multiple ConcurrentTask executions - */ - Completion completion() default Completion.Mandatory; + /** + * Indicates whether the successful completion of this ConcurrentTask is Mandatory or Optional when composing any final responses + * from multiple ConcurrentTask executions + */ + Completion completion() default Completion.Mandatory; - /** - * Defines the maximum time GJEX can "expect" the task to run for. - * In case the task does not produce a result within the timeout, the task may be retried by GJEX (based on the number of retries remaining) - * Any result produced by the task after the timeout value has elapsed will be ignored by the GJEX engine and - * will not be used by or passed on to other tasks that may be dependent on these results. - */ + /** + * Defines the maximum time GJEX can "expect" the task to run for. + * In case the task does not produce a result within the timeout, the task may be retried by GJEX (based on the number of retries remaining) + * Any result produced by the task after the timeout value has elapsed will be ignored by the GJEX engine and + * will not be used by or passed on to other tasks that may be dependent on these results. + */ int timeout() default 0; /** - * Timeout configured as a Config property. Note that {@link ConcurrentTask#timeout()} overrides this value - */ + * Timeout configured as a Config property. Note that {@link ConcurrentTask#timeout()} overrides this value + */ String timeoutConfig() default ""; /** - * Indicates if requests may be hedged within the specified timeout duration. - */ + * Indicates if requests may be hedged within the specified timeout duration. + */ boolean withRequestHedging() default false; /** - * Indicates the maximum concurrency for this Task within a single GJEX JVM node. - */ + * Indicates the maximum concurrency for this Task within a single GJEX JVM node. + */ int concurrency() default 10; /** - * Concurrency configured as a Config property. Note that {@link ConcurrentTask#concurrency()} overrides this value - */ + * Concurrency configured as a Config property. Note that {@link ConcurrentTask#concurrency()} overrides this value + */ String concurrencyConfig() default ""; } - diff --git a/core/src/main/java/com/flipkart/gjex/core/task/FutureDecorator.java b/core/src/main/java/com/flipkart/gjex/core/task/FutureDecorator.java index 92d74469..90674d6e 100644 --- a/core/src/main/java/com/flipkart/gjex/core/task/FutureDecorator.java +++ b/core/src/main/java/com/flipkart/gjex/core/task/FutureDecorator.java @@ -39,158 +39,158 @@ */ public class FutureDecorator implements Future { - private static final Logger LOGGER = LoggerFactory.getLogger(FutureDecorator.class); - - /** The Completion indicator*/ - private final ConcurrentTask.Completion completion; - /** The original Future decorated by this Future*/ - private final Future origin; - /** The TaskExecutor producing the Future*/ - private final TaskExecutor taskExecutor; - - /** The BiConsumer to callback on completion*/ - private BiConsumer completionConsumer; - - public FutureDecorator(TaskExecutor taskExecutor, ConcurrentTask.Completion completion) { - this.taskExecutor = taskExecutor; - this.origin = taskExecutor.queue(); - this.completion = completion; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return origin.cancel(mayInterruptIfRunning); - } - - @Override - public boolean isCancelled() { - return origin.isCancelled(); - } - - @Override - public boolean isDone() { - return origin.isDone(); - } - - @SuppressWarnings("unchecked") - @Override - public T get() throws InterruptedException, ExecutionException { - T result = origin.get(); - if (result instanceof Future) { - return ((Future) result).get(); - } - return result; - } - - @SuppressWarnings("unchecked") - @Override - public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - T result = origin.get(timeout, unit); - if (result instanceof Future) { - return ((Future) result).get(); - } - return result; - } - - public ConcurrentTask.Completion getCompletion() { - return completion; - } - public TaskExecutor getTaskExecutor() { - return taskExecutor; - } - - /** - * Registers the specified BiConsumer for callback when this FutureDecorator completes - * @param action the callback BiConsumer action - */ - public void whenComplete(BiConsumer action) { - this.completionConsumer = action; - this.taskExecutor.setCompletionConsumer(this.completionConsumer); - } - - /** Compose response from evaluating results from the specified 2 FutureDecorator instances*/ - @SuppressWarnings("unchecked") - public static R compose (FutureDecoratorfuture1, - FutureDecorator future2, BiFunction composer) throws TaskException { - R r = null; - T1 t1 = (T1)getResultFromFuture(future1); - T2 t2 = (T2)getResultFromFuture(future2); - try { - r = composer.apply(t1, t2); - } catch (Exception e) { - LOGGER.error("Error composing result from Futures : " + e.getMessage(), e); - throw new TaskException("Error composing result from Futures : " + e.getMessage(), e); - } - return r; - } - - /** Compose response from evaluating results from the specified 3 FutureDecorator instances*/ - @SuppressWarnings("unchecked") - public static R compose (FutureDecoratorfuture1, - FutureDecorator future2, FutureDecorator future3, - Function3 composer) throws TaskException { - R r = null; - T1 t1 = (T1)getResultFromFuture(future1); - T2 t2 = (T2)getResultFromFuture(future2); - T3 t3 = (T3)getResultFromFuture(future3); - try { - r = composer.apply(t1, t2, t3); - } catch (Exception e) { - LOGGER.error("Error composing result from Futures : " + e.getMessage(), e); - throw new TaskException("Error composing result from Futures : " + e.getMessage(), e); - } - return r; - } - - /** Convenience method to get the response from completion of the specified FutureDecorator and evaluate completion as defined in the FutureDecorator*/ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static Object getResultFromFuture(FutureDecorator future) { - Object result = null; - Integer futureGetTimeout = null; - // check if a Deadline has been set. This will become the first timeout we consider - if (Context.current().getDeadline() != null) { - if (Context.current().getDeadline().isExpired()) { - LOGGER.error("Task execution evaluation failed.Deadline exceeded in server execution."); - throw new TaskException("Task execution evaluation failed.", - new StatusException(Status.DEADLINE_EXCEEDED.withDescription("Deadline exceeded in server execution."))); - } - futureGetTimeout = (int)Context.current().getDeadline().timeRemaining(TimeUnit.MILLISECONDS); - } - // see if a Task timeout has been set, use the smaller of the Deadline and Task timeout - if (future.getTaskExecutor().getTimeout() > 0) { - futureGetTimeout = Math.min(futureGetTimeout, future.getTaskExecutor().getTimeout()); - } - if (future.getTaskExecutor().isWithRequestHedging() && future.getTaskExecutor().getRollingTailLatency() > 0) { - // we'll take the minimum of deadline and rolling tail latency (if request hedging is enabled) as the timeout for the Future - futureGetTimeout = futureGetTimeout == null ? - (int)future.getTaskExecutor().getRollingTailLatency() - : Math.min(futureGetTimeout, (int)future.getTaskExecutor().getRollingTailLatency()); - } - try { - if (futureGetTimeout != null) { - result = future.get(futureGetTimeout.longValue(), TimeUnit.MILLISECONDS); - } else { - result = future.get(); - } - } catch (TimeoutException e) { - if (future.getTaskExecutor().isWithRequestHedging() && !Context.current().getDeadline().isExpired()) { - // we will reschedule the execution i.e. hedge the request and return the result - LOGGER.info("Sending hedged request for Task : " + future.getTaskExecutor().getInvocation().getMethod().getName()); - result = FutureDecorator.getResultFromFuture(new FutureDecorator(future.getTaskExecutor().clone(), future.getCompletion())); - } - } catch (InterruptedException | ExecutionException e) { - String errorMessage = e.getCause() == null ? e.getMessage() : e.getCause().getMessage(); - if (future.getCompletion().equals(ConcurrentTask.Completion.Mandatory)) { - if (TimeoutException.class.isAssignableFrom(e.getClass())) { - throw new TaskException("Task execution results not available.", - new StatusException(Status.DEADLINE_EXCEEDED.withDescription("Deadline exceeded waiting for results :" + e.getMessage()))); - } - throw new TaskException("Error executing mandatory Task : " + errorMessage, e); - } else { - LOGGER.warn("Execution exception in optional task :" + errorMessage + " . Not failing the execution and proceeding."); - } - } - return result; - } + private static final Logger LOGGER = LoggerFactory.getLogger(FutureDecorator.class); + + /** The Completion indicator*/ + private final ConcurrentTask.Completion completion; + /** The original Future decorated by this Future*/ + private final Future origin; + /** The TaskExecutor producing the Future*/ + private final TaskExecutor taskExecutor; + + /** The BiConsumer to callback on completion*/ + private BiConsumer completionConsumer; + + public FutureDecorator(TaskExecutor taskExecutor, ConcurrentTask.Completion completion) { + this.taskExecutor = taskExecutor; + this.origin = taskExecutor.queue(); + this.completion = completion; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return origin.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return origin.isCancelled(); + } + + @Override + public boolean isDone() { + return origin.isDone(); + } + + @SuppressWarnings("unchecked") + @Override + public T get() throws InterruptedException, ExecutionException { + T result = origin.get(); + if (result instanceof Future) { + return ((Future) result).get(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + T result = origin.get(timeout, unit); + if (result instanceof Future) { + return ((Future) result).get(); + } + return result; + } + + public ConcurrentTask.Completion getCompletion() { + return completion; + } + public TaskExecutor getTaskExecutor() { + return taskExecutor; + } + + /** + * Registers the specified BiConsumer for callback when this FutureDecorator completes + * @param action the callback BiConsumer action + */ + public void whenComplete(BiConsumer action) { + this.completionConsumer = action; + this.taskExecutor.setCompletionConsumer(this.completionConsumer); + } + + /** Compose response from evaluating results from the specified 2 FutureDecorator instances*/ + @SuppressWarnings("unchecked") + public static R compose (FutureDecoratorfuture1, + FutureDecorator future2, BiFunction composer) throws TaskException { + R r = null; + T1 t1 = (T1)getResultFromFuture(future1); + T2 t2 = (T2)getResultFromFuture(future2); + try { + r = composer.apply(t1, t2); + } catch (Exception e) { + LOGGER.error("Error composing result from Futures : " + e.getMessage(), e); + throw new TaskException("Error composing result from Futures : " + e.getMessage(), e); + } + return r; + } + + /** Compose response from evaluating results from the specified 3 FutureDecorator instances*/ + @SuppressWarnings("unchecked") + public static R compose (FutureDecoratorfuture1, + FutureDecorator future2, FutureDecorator future3, + Function3 composer) throws TaskException { + R r = null; + T1 t1 = (T1)getResultFromFuture(future1); + T2 t2 = (T2)getResultFromFuture(future2); + T3 t3 = (T3)getResultFromFuture(future3); + try { + r = composer.apply(t1, t2, t3); + } catch (Exception e) { + LOGGER.error("Error composing result from Futures : " + e.getMessage(), e); + throw new TaskException("Error composing result from Futures : " + e.getMessage(), e); + } + return r; + } + + /** Convenience method to get the response from completion of the specified FutureDecorator and evaluate completion as defined in the FutureDecorator*/ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static Object getResultFromFuture(FutureDecorator future) { + Object result = null; + Integer futureGetTimeout = null; + // check if a Deadline has been set. This will become the first timeout we consider + if (Context.current().getDeadline() != null) { + if (Context.current().getDeadline().isExpired()) { + LOGGER.error("Task execution evaluation failed.Deadline exceeded in server execution."); + throw new TaskException("Task execution evaluation failed.", + new StatusException(Status.DEADLINE_EXCEEDED.withDescription("Deadline exceeded in server execution."))); + } + futureGetTimeout = (int)Context.current().getDeadline().timeRemaining(TimeUnit.MILLISECONDS); + } + // see if a Task timeout has been set, use the smaller of the Deadline and Task timeout + if (future.getTaskExecutor().getTimeout() > 0) { + futureGetTimeout = Math.min(futureGetTimeout, future.getTaskExecutor().getTimeout()); + } + if (future.getTaskExecutor().isWithRequestHedging() && future.getTaskExecutor().getRollingTailLatency() > 0) { + // we'll take the minimum of deadline and rolling tail latency (if request hedging is enabled) as the timeout for the Future + futureGetTimeout = futureGetTimeout == null ? + (int)future.getTaskExecutor().getRollingTailLatency() + : Math.min(futureGetTimeout, (int)future.getTaskExecutor().getRollingTailLatency()); + } + try { + if (futureGetTimeout != null) { + result = future.get(futureGetTimeout.longValue(), TimeUnit.MILLISECONDS); + } else { + result = future.get(); + } + } catch (TimeoutException e) { + if (future.getTaskExecutor().isWithRequestHedging() && !Context.current().getDeadline().isExpired()) { + // we will reschedule the execution i.e. hedge the request and return the result + LOGGER.info("Sending hedged request for Task : " + future.getTaskExecutor().getInvocation().getMethod().getName()); + result = FutureDecorator.getResultFromFuture(new FutureDecorator(future.getTaskExecutor().clone(), future.getCompletion())); + } + } catch (InterruptedException | ExecutionException e) { + String errorMessage = e.getCause() == null ? e.getMessage() : e.getCause().getMessage(); + if (future.getCompletion().equals(ConcurrentTask.Completion.Mandatory)) { + if (TimeoutException.class.isAssignableFrom(e.getClass())) { + throw new TaskException("Task execution results not available.", + new StatusException(Status.DEADLINE_EXCEEDED.withDescription("Deadline exceeded waiting for results :" + e.getMessage()))); + } + throw new TaskException("Error executing mandatory Task : " + errorMessage, e); + } else { + LOGGER.warn("Execution exception in optional task :" + errorMessage + " . Not failing the execution and proceeding."); + } + } + return result; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/task/TaskException.java b/core/src/main/java/com/flipkart/gjex/core/task/TaskException.java index c8cee86c..2a064f0b 100644 --- a/core/src/main/java/com/flipkart/gjex/core/task/TaskException.java +++ b/core/src/main/java/com/flipkart/gjex/core/task/TaskException.java @@ -25,44 +25,44 @@ */ public class TaskException extends GJEXError { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - /** Flag to control logging to the distributed trace*/ - private boolean isTraced = true; + /** Flag to control logging to the distributed trace*/ + private boolean isTraced = true; - /** - * Constructor for TaskException. - * @param msg the detail message - */ - public TaskException(String msg) { - super(GJEXError.ErrorType.runtime, msg, null); - } + /** + * Constructor for TaskException. + * @param msg the detail message + */ + public TaskException(String msg) { + super(GJEXError.ErrorType.runtime, msg, null); + } - /** - * Constructor for TaskException. - * @param msg the detail message - * @param cause the root cause - */ - public TaskException(String msg, Throwable cause) { - super(GJEXError.ErrorType.runtime, msg, cause); - } + /** + * Constructor for TaskException. + * @param msg the detail message + * @param cause the root cause + */ + public TaskException(String msg, Throwable cause) { + super(GJEXError.ErrorType.runtime, msg, cause); + } - /** - * Constructor for TaskException - * @param cause the root cause - * @param isTraced flag to indicate if error should be logged to the distributed trace - */ - public TaskException(Throwable cause, boolean isTraced) { - super(GJEXError.ErrorType.runtime, cause.getMessage(), cause); - this.isTraced = isTraced; - } + /** + * Constructor for TaskException + * @param cause the root cause + * @param isTraced flag to indicate if error should be logged to the distributed trace + */ + public TaskException(Throwable cause, boolean isTraced) { + super(GJEXError.ErrorType.runtime, cause.getMessage(), cause); + this.isTraced = isTraced; + } - public boolean isTraced() { - return isTraced; - } - public void setTraced(boolean isTraced) { - this.isTraced = isTraced; - } + public boolean isTraced() { + return isTraced; + } + public void setTraced(boolean isTraced) { + this.isTraced = isTraced; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/task/TaskExecutor.java b/core/src/main/java/com/flipkart/gjex/core/task/TaskExecutor.java index 4293eafd..40cf1f83 100644 --- a/core/src/main/java/com/flipkart/gjex/core/task/TaskExecutor.java +++ b/core/src/main/java/com/flipkart/gjex/core/task/TaskExecutor.java @@ -37,95 +37,95 @@ */ public class TaskExecutor extends HystrixCommand implements Logging { - /** The MethodInvocation to execute asynchronously*/ - private final MethodInvocation invocation; + /** The MethodInvocation to execute asynchronously*/ + private final MethodInvocation invocation; - /** The currently active gRPC Context*/ - private Context currentContext; + /** The currently active gRPC Context*/ + private Context currentContext; - /** The completion BiConsumer*/ - private BiConsumer completionConsumer; + /** The completion BiConsumer*/ + private BiConsumer completionConsumer; - /** Indicates if requests may be hedged within the configured timeout duration*/ - private boolean withRequestHedging; + /** Indicates if requests may be hedged within the configured timeout duration*/ + private boolean withRequestHedging; - /** The rolling tail latency as seen by Hystrix*/ - private long rollingTailLatency; + /** The rolling tail latency as seen by Hystrix*/ + private long rollingTailLatency; - /** Attributes stored for cloning*/ - private String groupKey; - private String name; - private int concurrency; - private int timeout; + /** Attributes stored for cloning*/ + private String groupKey; + private String name; + private int concurrency; + private int timeout; - public TaskExecutor(MethodInvocation invocation, String groupKey, String name, int concurrency, int timeout, boolean withRequestHedging) { - super(Setter + public TaskExecutor(MethodInvocation invocation, String groupKey, String name, int concurrency, int timeout, boolean withRequestHedging) { + super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) - .andCommandKey(HystrixCommandKey.Factory.asKey(name)) - .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(name + "-tp")) // creating a new thread pool per task by appending "-tp" to the task name - .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(concurrency)) - .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() - .withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD) - .withExecutionTimeoutInMilliseconds(timeout))); - currentContext = Context.current(); // store the current gRPC Context - this.invocation = invocation; - this.timeout = timeout; - this.withRequestHedging = withRequestHedging; - this.rollingTailLatency = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(name)).getTotalTimePercentile(95); - - // attributes copied for cloning, if needed - this.groupKey = groupKey; - this.name = name; - this.concurrency = concurrency; - } - - public void setCompletionConsumer(BiConsumer completionConsumer) { - this.completionConsumer = completionConsumer; - } - public MethodInvocation getInvocation() { - return invocation; - } - public boolean isWithRequestHedging() { - return withRequestHedging; - } - public long getRollingTailLatency() { - return rollingTailLatency; - } - public int getTimeout() { - return timeout; - } - - /** - * Clone this TaskExecutor with values that were specified during instantiation - */ - public TaskExecutor clone() { - TaskExecutor clone = new TaskExecutor(this.invocation, this.groupKey, this.name, this.concurrency, this.timeout, this.withRequestHedging); - return clone; - } - - /** - * Overridden method implementation. Invokes the Method invocation while setting relevant current gRPC Context - * @see com.netflix.hystrix.HystrixCommand#run() - */ - @SuppressWarnings("unchecked") - @Override - protected T run() throws Exception { - Context previous = this.currentContext.attach(); // setting the current gRPC context for the executing Hystrix thread - Throwable error = null; - T result = null; - try { - result = ((AsyncResult)this.invocation.proceed()).invoke(); // call the AsyncResult#invoke() to execute the actual work to be performed asynchronously - return result; - } catch (Throwable e) { - error = e; - error("Error executing task", e); - throw new RuntimeException(e); - } finally { - if (this.completionConsumer != null) { - this.completionConsumer.accept(result, error); // inform the completion status to the registered completion consumer - } - this.currentContext.detach(previous); // unset the current gRPC context - } - } + .andCommandKey(HystrixCommandKey.Factory.asKey(name)) + .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(name + "-tp")) // creating a new thread pool per task by appending "-tp" to the task name + .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(concurrency)) + .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() + .withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD) + .withExecutionTimeoutInMilliseconds(timeout))); + currentContext = Context.current(); // store the current gRPC Context + this.invocation = invocation; + this.timeout = timeout; + this.withRequestHedging = withRequestHedging; + this.rollingTailLatency = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(name)).getTotalTimePercentile(95); + + // attributes copied for cloning, if needed + this.groupKey = groupKey; + this.name = name; + this.concurrency = concurrency; + } + + public void setCompletionConsumer(BiConsumer completionConsumer) { + this.completionConsumer = completionConsumer; + } + public MethodInvocation getInvocation() { + return invocation; + } + public boolean isWithRequestHedging() { + return withRequestHedging; + } + public long getRollingTailLatency() { + return rollingTailLatency; + } + public int getTimeout() { + return timeout; + } + + /** + * Clone this TaskExecutor with values that were specified during instantiation + */ + public TaskExecutor clone() { + TaskExecutor clone = new TaskExecutor(this.invocation, this.groupKey, this.name, this.concurrency, this.timeout, this.withRequestHedging); + return clone; + } + + /** + * Overridden method implementation. Invokes the Method invocation while setting relevant current gRPC Context + * @see com.netflix.hystrix.HystrixCommand#run() + */ + @SuppressWarnings("unchecked") + @Override + protected T run() throws Exception { + Context previous = this.currentContext.attach(); // setting the current gRPC context for the executing Hystrix thread + Throwable error = null; + T result = null; + try { + result = ((AsyncResult)this.invocation.proceed()).invoke(); // call the AsyncResult#invoke() to execute the actual work to be performed asynchronously + return result; + } catch (Throwable e) { + error = e; + error("Error executing task", e); + throw new RuntimeException(e); + } finally { + if (this.completionConsumer != null) { + this.completionConsumer.accept(result, error); // inform the completion status to the registered completion consumer + } + this.currentContext.detach(previous); // unset the current gRPC context + } + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/ActiveSpanSource.java b/core/src/main/java/com/flipkart/gjex/core/tracing/ActiveSpanSource.java index 49a5ff4f..a17af1b8 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/ActiveSpanSource.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/ActiveSpanSource.java @@ -26,9 +26,9 @@ public interface ActiveSpanSource { /** - * ActiveSpanSource implementation that always returns - * null as the active span - */ + * ActiveSpanSource implementation that always returns + * null as the active span + */ public static ActiveSpanSource NONE = new ActiveSpanSource() { @Override public Span getActiveSpan() { @@ -37,10 +37,10 @@ public Span getActiveSpan() { }; /** - * ActiveSpanSource implementation that returns the - * current span stored in the GRPC context under - * {@link GJEXContextKey} - */ + * ActiveSpanSource implementation that returns the + * current span stored in the GRPC context under + * {@link GJEXContextKey} + */ public static ActiveSpanSource GRPC_CONTEXT = new ActiveSpanSource() { @Override public Span getActiveSpan() { @@ -49,7 +49,7 @@ public Span getActiveSpan() { }; /** - * @return the active span - */ + * @return the active span + */ public Span getActiveSpan(); } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/ConfigurableTracingSampler.java b/core/src/main/java/com/flipkart/gjex/core/tracing/ConfigurableTracingSampler.java index 0fa21589..235e910f 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/ConfigurableTracingSampler.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/ConfigurableTracingSampler.java @@ -29,45 +29,45 @@ */ public class ConfigurableTracingSampler implements TracingSampler, Logging { - /** Map of components and their respective samplers */ - private Map componentMap = new HashMap(); + /** Map of components and their respective samplers */ + private Map componentMap = new HashMap(); - /** - * Interface method implementation. - * @see com.flipkart.gjex.core.tracing.TracingSampler#isSampled(java.lang.String) - */ - @Override - public boolean isSampled(String component) { - boolean isSampled = false; - CountingSampler sampler = this.componentMap.get(component); - if (sampler != null) { - return sampler.isSampled(); - } - return isSampled; - } + /** + * Interface method implementation. + * @see com.flipkart.gjex.core.tracing.TracingSampler#isSampled(java.lang.String) + */ + @Override + public boolean isSampled(String component) { + boolean isSampled = false; + CountingSampler sampler = this.componentMap.get(component); + if (sampler != null) { + return sampler.isSampled(); + } + return isSampled; + } - /** - * Interface method implementation.Initializes a {@link CountingSampler} for the specified component with the specified - * sampling rate - * @param component the component/method endpoint/URI path - * @param rate the sampling rate - */ - @Override - public void initializeSamplerFor(String component, float rate) { - CountingSampler sampler = this.componentMap.get(component); - if (sampler == null) { - sampler = new CountingSampler(rate); - this.componentMap.put(component, sampler); - } - } + /** + * Interface method implementation.Initializes a {@link CountingSampler} for the specified component with the specified + * sampling rate + * @param component the component/method endpoint/URI path + * @param rate the sampling rate + */ + @Override + public void initializeSamplerFor(String component, float rate) { + CountingSampler sampler = this.componentMap.get(component); + if (sampler == null) { + sampler = new CountingSampler(rate); + this.componentMap.put(component, sampler); + } + } - /** - * Updates/Replaces the CountingSampler for the specified component with the new sampling rate - * @param component the component identifier - * @param rate the sampling rate - */ - public void updateSamplingRate(String component, float rate) { - this.componentMap.put(component, new CountingSampler(rate)); - } + /** + * Updates/Replaces the CountingSampler for the specified component with the new sampling rate + * @param component the component identifier + * @param rate the sampling rate + */ + public void updateSamplingRate(String component, float rate) { + this.componentMap.put(component, new CountingSampler(rate)); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/CountingSampler.java b/core/src/main/java/com/flipkart/gjex/core/tracing/CountingSampler.java index aefdd491..95458984 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/CountingSampler.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/CountingSampler.java @@ -31,48 +31,48 @@ */ public class CountingSampler { - private int i; // counter - private final BitSet sampleDecisions; + private int i; // counter + private final BitSet sampleDecisions; - public CountingSampler (float rate) { - if (rate < 0.0f || rate > 1) { - throw new IllegalArgumentException("rate should be between 0.0 and 1: was " + rate); - } - if (rate == 0.0f) { - this.sampleDecisions = new BitSet(100); // empty bitset to denote nothing is sampled - } else { - int outOf100 = (int) (rate * 100.0f); - this.sampleDecisions = this.randomBitSet(100, outOf100, new Random()); - } - } + public CountingSampler (float rate) { + if (rate < 0.0f || rate > 1) { + throw new IllegalArgumentException("rate should be between 0.0 and 1: was " + rate); + } + if (rate == 0.0f) { + this.sampleDecisions = new BitSet(100); // empty bitset to denote nothing is sampled + } else { + int outOf100 = (int) (rate * 100.0f); + this.sampleDecisions = this.randomBitSet(100, outOf100, new Random()); + } + } - public boolean isSampled() { - boolean result = sampleDecisions.get(i++); - if (i == 100) i = 0; - return result; - } + public boolean isSampled() { + boolean result = sampleDecisions.get(i++); + if (i == 100) i = 0; + return result; + } - /** - * Reservoir sampling algorithm borrowed from Stack Overflow. - * - * http://stackoverflow.com/questions/12817946/generate-a-random-bitset-with-n-1s - */ - private BitSet randomBitSet(int size, int cardinality, Random rnd) { - BitSet result = new BitSet(size); - int[] chosen = new int[cardinality]; - int i; - for (i = 0; i < cardinality; ++i) { - chosen[i] = i; - result.set(i); - } - for (; i < size; ++i) { - int j = rnd.nextInt(i + 1); - if (j < cardinality) { - result.clear(chosen[j]); - result.set(i); - chosen[j] = i; - } - } - return result; - } + /** + * Reservoir sampling algorithm borrowed from Stack Overflow. + * + * http://stackoverflow.com/questions/12817946/generate-a-random-bitset-with-n-1s + */ + private BitSet randomBitSet(int size, int cardinality, Random rnd) { + BitSet result = new BitSet(size); + int[] chosen = new int[cardinality]; + int i; + for (i = 0; i < cardinality; ++i) { + chosen[i] = i; + result.set(i); + } + for (; i < size; ++i) { + int j = rnd.nextInt(i + 1); + if (j < cardinality) { + result.clear(chosen[j]); + result.set(i); + chosen[j] = i; + } + } + return result; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/OperationNameConstructor.java b/core/src/main/java/com/flipkart/gjex/core/tracing/OperationNameConstructor.java index 6dd0af23..49e34700 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/OperationNameConstructor.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/OperationNameConstructor.java @@ -22,23 +22,23 @@ */ public interface OperationNameConstructor { - /** + /** * Default span operation name constructor, that will return an RPC's method * name when constructOperationName is called. */ - public static OperationNameConstructor DEFAULT = new OperationNameConstructor() { - @Override - public String constructOperationName(MethodDescriptor method) { - return method.getFullMethodName(); - } - }; + public static OperationNameConstructor DEFAULT = new OperationNameConstructor() { + @Override + public String constructOperationName(MethodDescriptor method) { + return method.getFullMethodName(); + } + }; - /** + /** * Constructs a span's operation name from the RPC's method. * @param method the rpc method to extract a name from * @param the rpc request type * @param the rpc response type * @return the operation name */ - public String constructOperationName(MethodDescriptor method); + public String constructOperationName(MethodDescriptor method); } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/Traced.java b/core/src/main/java/com/flipkart/gjex/core/tracing/Traced.java index 7d1af0c7..d888d79a 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/Traced.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/Traced.java @@ -34,23 +34,23 @@ @Documented public @interface Traced { - /** Convenience sampling constants */ - public static final float RATE_NEVER_SAMPLE = 0f; - public static final float RATE_ALWAYS_SAMPLE = 1f; - public static final boolean BOOLEAN_NEVER_SAMPLE = false; - - /** - * Returns the sampling rate on a scale of 0.0 to 1.0, where 0.0 indicates no sampling and 1.0 indicates all requests are sampled - * Default value is to sample always {@link Traced#RATE_ALWAYS_SAMPLE} - * Note that this property is overriden by {@link #withTracingSampler()}, if any is specified - */ - float withSamplingRate() default Traced.RATE_ALWAYS_SAMPLE; - - /** - * Tracing is delegated to the specified sampler. Note that specifying a TracingSampler overrides any {@link #withSamplingRate()} setting. - * NOTE : In GJEX, this attribute is interpreted and used only when specified for gRPC services i.e. sub-types of {@link BindableService} - * @return null or a TracingSampler implementation - */ - Class withTracingSampler() default TracingSampler.class; + /** Convenience sampling constants */ + public static final float RATE_NEVER_SAMPLE = 0f; + public static final float RATE_ALWAYS_SAMPLE = 1f; + public static final boolean BOOLEAN_NEVER_SAMPLE = false; + + /** + * Returns the sampling rate on a scale of 0.0 to 1.0, where 0.0 indicates no sampling and 1.0 indicates all requests are sampled + * Default value is to sample always {@link Traced#RATE_ALWAYS_SAMPLE} + * Note that this property is overriden by {@link #withTracingSampler()}, if any is specified + */ + float withSamplingRate() default Traced.RATE_ALWAYS_SAMPLE; + + /** + * Tracing is delegated to the specified sampler. Note that specifying a TracingSampler overrides any {@link #withSamplingRate()} setting. + * NOTE : In GJEX, this attribute is interpreted and used only when specified for gRPC services i.e. sub-types of {@link BindableService} + * @return null or a TracingSampler implementation + */ + Class withTracingSampler() default TracingSampler.class; } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSampler.java b/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSampler.java index 581e3406..77cddc3e 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSampler.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSampler.java @@ -24,21 +24,21 @@ */ public interface TracingSampler { - /** - * Determines if tracing must happen for the specified component - * Default is to never sample {@link Traced#BOOLEAN_NEVER_SAMPLE} - * @param component the name of the service or method endpoint where tracing originates - * @return boolean true or false - */ - default boolean isSampled(String component) { - return Traced.BOOLEAN_NEVER_SAMPLE; - } + /** + * Determines if tracing must happen for the specified component + * Default is to never sample {@link Traced#BOOLEAN_NEVER_SAMPLE} + * @param component the name of the service or method endpoint where tracing originates + * @return boolean true or false + */ + default boolean isSampled(String component) { + return Traced.BOOLEAN_NEVER_SAMPLE; + } - /** - * Registers for sampling of the component traces at the specified rate - * @param component the component identifier - * @param rate the sampling rate in the range 0.0f to 1.0f - */ - void initializeSamplerFor(String component, float rate); + /** + * Registers for sampling of the component traces at the specified rate + * @param component the component identifier + * @param rate the sampling rate in the range 0.0f to 1.0f + */ + void initializeSamplerFor(String component, float rate); } diff --git a/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSamplerHolder.java b/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSamplerHolder.java index 97a8d928..76cf67ce 100644 --- a/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSamplerHolder.java +++ b/core/src/main/java/com/flipkart/gjex/core/tracing/TracingSamplerHolder.java @@ -31,24 +31,24 @@ @Named("TracingSamplerHolder") public class TracingSamplerHolder extends HashMap { - /** Name for this TracingSamplerHolder*/ - public static final String TRACING_SAMPLER_HOLDER_NAME = "GJEX_TracingSamplerHolder"; - - private static final long serialVersionUID = 1L; - - /** - * Updates the tracing sampler for the component specified with the specified sampling rate - * @param serviceName the service name - * @param samplerComponentName the sampled component name - * @param samplerRate the rate of sampling - */ - public void updateTracingSampler(String serviceName, String samplerComponentName, float samplerRate) { - ConfigurableTracingSampler tracingSampler = (ConfigurableTracingSampler)this.get(serviceName); - if (tracingSampler == null) { - throw new GJEXError(ErrorType.runtime, "No tracing configuration found for : " + samplerComponentName, null); - } - tracingSampler.updateSamplingRate(samplerComponentName, samplerRate); - } + /** Name for this TracingSamplerHolder*/ + public static final String TRACING_SAMPLER_HOLDER_NAME = "GJEX_TracingSamplerHolder"; + + private static final long serialVersionUID = 1L; + + /** + * Updates the tracing sampler for the component specified with the specified sampling rate + * @param serviceName the service name + * @param samplerComponentName the sampled component name + * @param samplerRate the rate of sampling + */ + public void updateTracingSampler(String serviceName, String samplerComponentName, float samplerRate) { + ConfigurableTracingSampler tracingSampler = (ConfigurableTracingSampler)this.get(serviceName); + if (tracingSampler == null) { + throw new GJEXError(ErrorType.runtime, "No tracing configuration found for : " + samplerComponentName, null); + } + tracingSampler.updateSamplingRate(samplerComponentName, samplerRate); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/util/Pair.java b/core/src/main/java/com/flipkart/gjex/core/util/Pair.java index 304ee012..45c40f85 100644 --- a/core/src/main/java/com/flipkart/gjex/core/util/Pair.java +++ b/core/src/main/java/com/flipkart/gjex/core/util/Pair.java @@ -43,7 +43,7 @@ public int hashCode() { } @SuppressWarnings("rawtypes") - @Override + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/core/src/main/java/com/flipkart/gjex/core/web/DashboardHealthCheckResource.java b/core/src/main/java/com/flipkart/gjex/core/web/DashboardHealthCheckResource.java index 9362d5e1..c60859a0 100644 --- a/core/src/main/java/com/flipkart/gjex/core/web/DashboardHealthCheckResource.java +++ b/core/src/main/java/com/flipkart/gjex/core/web/DashboardHealthCheckResource.java @@ -40,16 +40,16 @@ @Named("DashboardHealthCheckResource") public class DashboardHealthCheckResource { - @Context - private ServletContext servletContext; + @Context + private ServletContext servletContext; - @GET - @Produces(MediaType.APPLICATION_JSON) - public Response performHealthChecks() { - HealthCheckRegistry registry = (HealthCheckRegistry) servletContext - .getAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME); - SortedMap results = registry.runHealthChecks(); - return Response.status(Response.Status.OK).entity(results).build(); - } + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response performHealthChecks() { + HealthCheckRegistry registry = (HealthCheckRegistry) servletContext + .getAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME); + SortedMap results = registry.runHealthChecks(); + return Response.status(Response.Status.OK).entity(results).build(); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/web/DashboardResource.java b/core/src/main/java/com/flipkart/gjex/core/web/DashboardResource.java index c2535303..878e32f3 100644 --- a/core/src/main/java/com/flipkart/gjex/core/web/DashboardResource.java +++ b/core/src/main/java/com/flipkart/gjex/core/web/DashboardResource.java @@ -37,14 +37,14 @@ @Named public class DashboardResource { - @Context - private ServletContext servletContext; + @Context + private ServletContext servletContext; - @GET - @Path("/dashboard") - @Produces(MediaType.TEXT_HTML) - public Viewable getHystrixDashboard() { - return new Viewable("/dashboard.ftl"); - } + @GET + @Path("/dashboard") + @Produces(MediaType.TEXT_HTML) + public Viewable getHystrixDashboard() { + return new Viewable("/dashboard.ftl"); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/web/HealthCheckResource.java b/core/src/main/java/com/flipkart/gjex/core/web/HealthCheckResource.java index 7671a199..f89a82aa 100644 --- a/core/src/main/java/com/flipkart/gjex/core/web/HealthCheckResource.java +++ b/core/src/main/java/com/flipkart/gjex/core/web/HealthCheckResource.java @@ -40,16 +40,16 @@ @Named("HealthCheckResource") public class HealthCheckResource { - @Context - private ServletContext servletContext; + @Context + private ServletContext servletContext; - @GET - @Produces(MediaType.APPLICATION_JSON) - public Response performHealthChecks() { - HealthCheckRegistry registry = (HealthCheckRegistry) servletContext - .getAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME); - SortedMap results = registry.runHealthChecks(); - return Response.status(Response.Status.OK).entity(results).build(); - } + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response performHealthChecks() { + HealthCheckRegistry registry = (HealthCheckRegistry) servletContext + .getAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME); + SortedMap results = registry.runHealthChecks(); + return Response.status(Response.Status.OK).entity(results).build(); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/web/ResourceException.java b/core/src/main/java/com/flipkart/gjex/core/web/ResourceException.java index e2e43ce9..b6515e60 100644 --- a/core/src/main/java/com/flipkart/gjex/core/web/ResourceException.java +++ b/core/src/main/java/com/flipkart/gjex/core/web/ResourceException.java @@ -25,23 +25,23 @@ */ public class ResourceException extends GJEXError { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - /** - * Constructor for ResourceException. - * @param msg the detail message - */ - public ResourceException(String msg) { - super(GJEXError.ErrorType.runtime, msg, null); - } + /** + * Constructor for ResourceException. + * @param msg the detail message + */ + public ResourceException(String msg) { + super(GJEXError.ErrorType.runtime, msg, null); + } - /** - * Constructor for ResourceException. - * @param msg the detail message - * @param cause the root cause - */ - public ResourceException(String msg, Throwable cause) { - super(GJEXError.ErrorType.runtime, msg, cause); - } + /** + * Constructor for ResourceException. + * @param msg the detail message + * @param cause the root cause + */ + public ResourceException(String msg, Throwable cause) { + super(GJEXError.ErrorType.runtime, msg, cause); + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/web/TracingResource.java b/core/src/main/java/com/flipkart/gjex/core/web/TracingResource.java index 60bbb6ae..e24cbea4 100644 --- a/core/src/main/java/com/flipkart/gjex/core/web/TracingResource.java +++ b/core/src/main/java/com/flipkart/gjex/core/web/TracingResource.java @@ -39,19 +39,19 @@ @Named public class TracingResource { - @Context - private ServletContext servletContext; + @Context + private ServletContext servletContext; - @POST - @Produces(MediaType.APPLICATION_JSON) - public TracingConfiguration updateTracingConfiguration(TracingConfiguration tracingConfig) { - TracingSamplerHolder tracingSamplerHolder = (TracingSamplerHolder) servletContext - .getAttribute(TracingSamplerHolder.TRACING_SAMPLER_HOLDER_NAME); - try { - tracingSamplerHolder.updateTracingSampler(tracingConfig.getApiName(), tracingConfig.getComponentName(), tracingConfig.getSamplingRate()); - } catch (GJEXError error) { - throw new ResourceException(error.getMessage()); - } - return tracingConfig; - } + @POST + @Produces(MediaType.APPLICATION_JSON) + public TracingConfiguration updateTracingConfiguration(TracingConfiguration tracingConfig) { + TracingSamplerHolder tracingSamplerHolder = (TracingSamplerHolder) servletContext + .getAttribute(TracingSamplerHolder.TRACING_SAMPLER_HOLDER_NAME); + try { + tracingSamplerHolder.updateTracingSampler(tracingConfig.getApiName(), tracingConfig.getComponentName(), tracingConfig.getSamplingRate()); + } catch (GJEXError error) { + throw new ResourceException(error.getMessage()); + } + return tracingConfig; + } } diff --git a/core/src/main/java/com/flipkart/gjex/core/web/dto/TracingConfiguration.java b/core/src/main/java/com/flipkart/gjex/core/web/dto/TracingConfiguration.java index a7966e26..ee8b0da6 100644 --- a/core/src/main/java/com/flipkart/gjex/core/web/dto/TracingConfiguration.java +++ b/core/src/main/java/com/flipkart/gjex/core/web/dto/TracingConfiguration.java @@ -22,38 +22,38 @@ */ public class TracingConfiguration { - private String apiName; - private String componentName; - private float samplingRate; + private String apiName; + private String componentName; + private float samplingRate; - public TracingConfiguration() {} - public TracingConfiguration(String apiName, String componentName, float samplingRate) { - this.apiName = apiName; - this.componentName = componentName; - this.samplingRate = samplingRate; - } + public TracingConfiguration() {} + public TracingConfiguration(String apiName, String componentName, float samplingRate) { + this.apiName = apiName; + this.componentName = componentName; + this.samplingRate = samplingRate; + } - public String getApiName() { - return apiName; - } - public void setApiName(String apiName) { - this.apiName = apiName; - } - public float getSamplingRate() { - return samplingRate; - } - public void setSamplingRate(float samplingRate) { - this.samplingRate = samplingRate; - } - public String getComponentName() { - return componentName; - } - public void setComponentName(String componentName) { - this.componentName = componentName; - } - @Override - public String toString() { - return "TracingConfiguration - " + componentName + " : " + samplingRate; - } + public String getApiName() { + return apiName; + } + public void setApiName(String apiName) { + this.apiName = apiName; + } + public float getSamplingRate() { + return samplingRate; + } + public void setSamplingRate(float samplingRate) { + this.samplingRate = samplingRate; + } + public String getComponentName() { + return componentName; + } + public void setComponentName(String componentName) { + this.componentName = componentName; + } + @Override + public String toString() { + return "TracingConfiguration - " + componentName + " : " + samplingRate; + } } diff --git a/core/src/test/java/com/flipkart/gjex/core/config/BaseConfigurationFactoryTest.java b/core/src/test/java/com/flipkart/gjex/core/config/BaseConfigurationFactoryTest.java index bf1c79b2..2bf36239 100644 --- a/core/src/test/java/com/flipkart/gjex/core/config/BaseConfigurationFactoryTest.java +++ b/core/src/test/java/com/flipkart/gjex/core/config/BaseConfigurationFactoryTest.java @@ -83,7 +83,7 @@ public void setProperties(Map properties) { public static class ExampleConfigWithDefaults extends GJEXConfiguration { - /* + /* private String name = "GJEX"; private int age = 12; */ @@ -102,7 +102,7 @@ public static class ExampleConfigWithDefaults extends GJEXConfiguration { protected File malformedAdvancedFile = new File("/"); @SuppressWarnings("rawtypes") - protected ConfigurationFactory factory = new ConfigurationFactory() { + protected ConfigurationFactory factory = new ConfigurationFactory() { @Override public Pair build(ConfigurationSourceProvider provider, String path) throws IOException, ConfigurationException { @@ -146,7 +146,7 @@ public void incorrectTypeIsFound() throws Exception { } @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test + @Test public void loadsValidConfigFiles() throws Exception { final Pair result = factory.build(validFile); ExampleConfig example = result.getKey(); diff --git a/core/src/test/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactoryTest.java b/core/src/test/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactoryTest.java index 3b042ed2..39742fb0 100644 --- a/core/src/test/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactoryTest.java +++ b/core/src/test/java/com/flipkart/gjex/core/config/ConfigurationFactoryFactoryTest.java @@ -31,11 +31,11 @@ public class ConfigurationFactoryFactoryTest { @SuppressWarnings("rawtypes") - private final ConfigurationFactoryFactory factoryFactory = new DefaultConfigurationFactoryFactory<>(); + private final ConfigurationFactoryFactory factoryFactory = new DefaultConfigurationFactoryFactory<>(); private final Validator validator = null; @SuppressWarnings("rawtypes") - @Test + @Test public void createDefaultFactory() throws Exception { File validFile = new File(Resources.getResource("factory-test-valid.yml").toURI()); ConfigurationFactory factory = @@ -46,7 +46,7 @@ public void createDefaultFactory() throws Exception { } @SuppressWarnings("rawtypes") - @Test + @Test public void createDefaultFactoryFailsUnknownProperty() throws Exception { File validFileWithUnknownProp = new File( Resources.getResource("factory-test-unknown-property.yml").toURI()); @@ -61,7 +61,7 @@ public void createDefaultFactoryFailsUnknownProperty() throws Exception { } @SuppressWarnings("rawtypes") - @Test + @Test public void createFactoryAllowingUnknownProperties() throws Exception { ConfigurationFactoryFactory customFactory = new PassThroughConfigurationFactoryFactory(); @@ -79,7 +79,7 @@ public void createFactoryAllowingUnknownProperties() throws Exception { @SuppressWarnings("rawtypes") - private static final class PassThroughConfigurationFactoryFactory + private static final class PassThroughConfigurationFactoryFactory extends DefaultConfigurationFactoryFactory { @Override protected ObjectMapper configureObjectMapper(ObjectMapper objectMapper) { diff --git a/core/src/test/java/com/flipkart/gjex/core/config/FileConfigurationSourceProviderTest.java b/core/src/test/java/com/flipkart/gjex/core/config/FileConfigurationSourceProviderTest.java index f0521d5a..34688448 100644 --- a/core/src/test/java/com/flipkart/gjex/core/config/FileConfigurationSourceProviderTest.java +++ b/core/src/test/java/com/flipkart/gjex/core/config/FileConfigurationSourceProviderTest.java @@ -31,7 +31,7 @@ public class FileConfigurationSourceProviderTest { @Test public void readsFileContents() throws Exception { try (InputStream input = provider.open(Resources.getResource("example.txt").getFile()); - ByteArrayOutputStream output = new ByteArrayOutputStream()) { + ByteArrayOutputStream output = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int length; while ((length = input.read(buffer)) != -1) { diff --git a/core/src/test/resources/factory-test-empty.yml b/core/src/test/resources/factory-test-empty.yml index e69de29b..8b137891 100644 --- a/core/src/test/resources/factory-test-empty.yml +++ b/core/src/test/resources/factory-test-empty.yml @@ -0,0 +1 @@ + diff --git a/examples/build.gradle b/examples/build.gradle index a6c21ab3..4e399acc 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -113,4 +113,3 @@ applicationDistribution.into('bin') { from(helloWorldClient) fileMode = 0755 } - diff --git a/examples/settings.gradle b/examples/settings.gradle index 3a06588d..9512a19f 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -1,2 +1 @@ rootProject.name = 'examples' - diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/bean/HelloBean.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/bean/HelloBean.java index ca76749b..48641eb0 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/bean/HelloBean.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/bean/HelloBean.java @@ -26,38 +26,38 @@ */ public class HelloBean { - @NotBlank - private String greeting; - @NotNull - private Integer age; - - public HelloBean() { - } - - public HelloBean(String greeting, Integer age) { - this.greeting = greeting; - this.age = age; - } - - public String getGreeting() { - return greeting; - } - - public void setGreeting(String greeting) { - this.greeting = greeting; - } - - public Integer getAge() { - return age; - } - - public void setAge(Integer age) { - this.age = age; - } - - @Override - public String toString() { - return "Greeting : " + greeting + ", Age : " + age; - } + @NotBlank + private String greeting; + @NotNull + private Integer age; + + public HelloBean() { + } + + public HelloBean(String greeting, Integer age) { + this.greeting = greeting; + this.age = age; + } + + public String getGreeting() { + return greeting; + } + + public void setGreeting(String greeting) { + this.greeting = greeting; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + @Override + public String toString() { + return "Greeting : " + greeting + ", Age : " + age; + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClient.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClient.java index 38bb6597..2bbc0ab8 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClient.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClient.java @@ -38,8 +38,8 @@ public class HelloWorldClient { private final GreeterGrpc.GreeterBlockingStub blockingStub; /** - * A custom client. - */ + * A custom client. + */ private HelloWorldClient(String host, int port) { originChannel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() @@ -54,35 +54,35 @@ private void shutdown() throws InterruptedException { } /** - * A simple client method that like {@link io.grpc.examples.helloworld.HelloWorldClient}. - */ + * A simple client method that like {@link io.grpc.examples.helloworld.HelloWorldClient}. + */ private void greet(String name) { logger.info("Will try to greet " + name + " ..."); HelloRequest request = HelloRequest.newBuilder().setName(name).build(); HelloReply response; try { - response = blockingStub.withDeadlineAfter(700, TimeUnit.MILLISECONDS).sayHello(request); + response = blockingStub.withDeadlineAfter(700, TimeUnit.MILLISECONDS).sayHello(request); } catch (StatusRuntimeException e) { - logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); - return; + logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); + return; } logger.info("Greeting: " + response.getMessage()); } /** - * Main start the client from the command line. - */ + * Main start the client from the command line. + */ public static void main(String[] args) throws Exception { HelloWorldClient client = new HelloWorldClient("localhost", 50051); try { - /* Access a service running on the local machine on port 50051 */ - String user = "world"; - if (args.length > 0) { + /* Access a service running on the local machine on port 50051 */ + String user = "world"; + if (args.length > 0) { user = args[0]; /* Use the arg as the name to greet if provided */ - } - client.greet(user); + } + client.greet(user); } finally { - client.shutdown(); + client.shutdown(); } } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClientInterceptor.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClientInterceptor.java index f670b273..a6deb070 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClientInterceptor.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/client/HelloWorldClientInterceptor.java @@ -35,29 +35,29 @@ public class HelloWorldClientInterceptor implements ClientInterceptor { @VisibleForTesting static final Metadata.Key CUSTOM_HEADER_KEY = - Metadata.Key.of("DUMMY_AUTH_TOKEN", Metadata.ASCII_STRING_MARSHALLER); + Metadata.Key.of("DUMMY_AUTH_TOKEN", Metadata.ASCII_STRING_MARSHALLER); @Override public ClientCall interceptCall(MethodDescriptor method, - CallOptions callOptions, Channel next) { + CallOptions callOptions, Channel next) { return new SimpleForwardingClientCall(next.newCall(method, callOptions)) { - @Override - public void start(Listener responseListener, Metadata headers) { + @Override + public void start(Listener responseListener, Metadata headers) { /* put custom header */ headers.put(CUSTOM_HEADER_KEY, "dummyAuthPrincipal"); super.start(new SimpleForwardingClientCallListener(responseListener) { - @Override - public void onHeaders(Metadata headers) { + @Override + public void onHeaders(Metadata headers) { /** - * if you don't need receive header from server, - * you can use {@link io.grpc.stub.MetadataUtils#attachHeaders} - * directly to send header - */ + * if you don't need receive header from server, + * you can use {@link io.grpc.stub.MetadataUtils#attachHeaders} + * directly to send header + */ super.onHeaders(headers); - } + } }, headers); - } + } }; } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/AuthFilter.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/AuthFilter.java index ff63f589..fc68545f 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/AuthFilter.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/AuthFilter.java @@ -36,41 +36,40 @@ @Named("AuthFilter") public class AuthFilter extends GrpcFilter implements Logging { - /** Fictitious authentication key*/ - @SuppressWarnings("rawtypes") - static final Metadata.Key AUTH_KEY = Metadata.Key.of("DUMMY_AUTH_TOKEN", Metadata.ASCII_STRING_MARSHALLER); + /** Fictitious authentication key*/ + @SuppressWarnings("rawtypes") + static final Metadata.Key AUTH_KEY = Metadata.Key.of("DUMMY_AUTH_TOKEN", Metadata.ASCII_STRING_MARSHALLER); - /** Flag to control authentication check*/ - private final boolean isAuth = false; + /** Flag to control authentication check*/ + private final boolean isAuth = false; - @Override - public GrpcFilter getInstance(){ - return new AuthFilter(); - } + @Override + public GrpcFilter getInstance(){ + return new AuthFilter(); + } - @Override - public void doProcessRequest(HelloRequest request, RequestParams requestParams) throws StatusRuntimeException { - info("Headers found in the request : " + requestParams.getMetadata().toString()); - this.checkAuth(requestParams.getMetadata()); - } + @Override + public void doProcessRequest(HelloRequest request, RequestParams requestParams) throws StatusRuntimeException { + info("Headers found in the request : " + requestParams.getMetadata().toString()); + this.checkAuth(requestParams.getMetadata()); + } - @SuppressWarnings("rawtypes") - @Override - public Metadata.Key[] getForwardHeaderKeys() { - return new Metadata.Key[] {AUTH_KEY}; - } + @SuppressWarnings("rawtypes") + @Override + public Metadata.Key[] getForwardHeaderKeys() { + return new Metadata.Key[] {AUTH_KEY}; + } - @SuppressWarnings("unchecked") - private void checkAuth(Metadata requestHeaders) throws StatusRuntimeException { - if (this.isAuth() && requestHeaders.get(AUTH_KEY) == null) { - error("Rejecting this request as it is unauthenticated!"); - throw new StatusRuntimeException(Status.UNAUTHENTICATED.withDescription("Auth token is missing in request headers!"), requestHeaders); - } - } + @SuppressWarnings("unchecked") + private void checkAuth(Metadata requestHeaders) throws StatusRuntimeException { + if (this.isAuth() && requestHeaders.get(AUTH_KEY) == null) { + error("Rejecting this request as it is unauthenticated!"); + throw new StatusRuntimeException(Status.UNAUTHENTICATED.withDescription("Auth token is missing in request headers!"), requestHeaders); + } + } - public boolean isAuth() { - return this.isAuth; - } + public boolean isAuth() { + return this.isAuth; + } } - diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/LoggingFilter.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/LoggingFilter.java index 3b90c347..47cb2205 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/LoggingFilter.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/filter/LoggingFilter.java @@ -30,7 +30,7 @@ @Named("LoggingFilter") public class LoggingFilter extends GrpcFilter implements Logging { - /** Custom response key to indicate request was logged on the server*/ + /** Custom response key to indicate request was logged on the server*/ static final Metadata.Key CUSTOM_HEADER_KEY = Metadata.Key.of("request_response_logged_header_key", Metadata.ASCII_STRING_MARSHALLER); @Override diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java index 071ecd47..8c6e9d9a 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/guice/HelloWorldModule.java @@ -21,6 +21,7 @@ import com.flipkart.gjex.core.tracing.TracingSampler; import com.flipkart.gjex.examples.helloworld.filter.AuthFilter; import com.flipkart.gjex.examples.helloworld.filter.LoggingFilter; +import com.flipkart.gjex.examples.helloworld.filter.ModFilter; import com.flipkart.gjex.examples.helloworld.service.GreeterService; import com.flipkart.gjex.examples.helloworld.tracing.AllWhitelistTracingSampler; import com.flipkart.gjex.examples.helloworld.web.HelloWorldResourceConfig; @@ -41,23 +42,23 @@ */ public class HelloWorldModule extends AbstractModule { - public HelloWorldModule() {} + public HelloWorldModule() {} - @Override - protected void configure() { - ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost",50051) - .usePlaintext() - .build(); + @Override + protected void configure() { + ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost",50051) + .usePlaintext() + .build(); // install(new ClientModule(GreeterGrpc.GreeterBlockingStub.class,new ChannelConfig("localhost",9999))); - bind(GreeterGrpc.GreeterBlockingStub.class).toInstance(GreeterGrpc.newBlockingStub(channel)); - bind(BindableService.class).annotatedWith(Names.named("GreeterService")).to(GreeterService.class); - bind(GrpcFilter.class).annotatedWith(Names.named("LoggingFilter")).to(LoggingFilter.class); - bind(GrpcFilter.class).annotatedWith(Names.named("AuthFilter")).to(AuthFilter.class); - bind(TracingSampler.class).to(AllWhitelistTracingSampler.class); - bind(ResourceConfig.class).annotatedWith(Names.named("HelloWorldResourceConfig")).to(HelloWorldResourceConfig.class); - bind(HttpFilterParams.class).annotatedWith(Names.named("ExampleHttpFilterParams")) - .toInstance(HttpFilterParams.builder().filter(new ExampleHttpFilter()).pathSpec("/*").build()); + bind(GreeterGrpc.GreeterBlockingStub.class).toInstance(GreeterGrpc.newBlockingStub(channel)); + bind(BindableService.class).annotatedWith(Names.named("GreeterService")).to(GreeterService.class); + bind(GrpcFilter.class).annotatedWith(Names.named("LoggingFilter")).to(LoggingFilter.class); + bind(GrpcFilter.class).annotatedWith(Names.named("AuthFilter")).to(AuthFilter.class); + bind(TracingSampler.class).to(AllWhitelistTracingSampler.class); + bind(ResourceConfig.class).annotatedWith(Names.named("HelloWorldResourceConfig")).to(HelloWorldResourceConfig.class); + bind(HttpFilterParams.class).annotatedWith(Names.named("ExampleHttpFilterParams")) + .toInstance(HttpFilterParams.builder().filter(new ExampleHttpFilter()).pathSpec("/*").build()); bind(JavaxFilterParams.class).annotatedWith(Names.named("ExampleJavaxFilter")) .toInstance(JavaxFilterParams.builder().filter(new ExampleJavaxFilter()).pathSpec("/*").build()); - } + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/GreeterService.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/GreeterService.java index b98c78a9..7747104f 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/GreeterService.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/GreeterService.java @@ -19,6 +19,7 @@ import javax.inject.Named; import com.flipkart.gjex.examples.helloworld.filter.AuthFilter; +import com.flipkart.gjex.examples.helloworld.filter.ModFilter; import io.dropwizard.metrics5.annotation.Timed; import com.flipkart.gjex.core.filter.grpc.ApplicationHeaders; import com.flipkart.gjex.core.filter.grpc.MethodFilters; @@ -45,114 +46,113 @@ @Named("GreeterService") public class GreeterService extends GreeterGrpc.GreeterImplBase implements Logging { - /** Flag to return bad values in Validation check*/ - private final boolean isFailValidation = false; - - /** Property read from configuration*/ - private String greeting; - - /** Injected business logic class where validation is performed */ - private HelloBeanService helloBeanService; - - /** A stub to call an external grpc service.This would be injected via @{@link com.flipkart.gjex.guice.module.ClientModule}**/ - @Inject - GreeterGrpc.GreeterBlockingStub blockingStub; - - /** Demonstrate injecting custom properties from configuration */ - @Inject - public GreeterService(@Named("hw.greeting") String greeting, HelloBeanService helloBeanService) { - this.greeting = greeting; - this.helloBeanService = helloBeanService; - } - - @Override - @Api(deadlineConfig = "apiProperties.sayhello.deadline") // specify an API level Deadline that will cascade to all @ConcurrentTask invoked in serving this API - @Timed // the Timed annotation for publishing JMX metrics via MBean - @MethodFilters({LoggingFilter.class, AuthFilter.class}) // Method level filters - @Traced(withSamplingRate=0.0f) // Start a new Trace or participate in a Client-initiated distributed trace - public void sayHello(HelloRequest req, StreamObserver responseObserver) { - - info("Saying hello in Greeter service"); - info("Headers in service : " + ApplicationHeaders.getHeaders()); - - try { - // invoke business logic implemented in a separate injected class - helloBeanService.sayHelloInBean(this.getHelloBean()); - } catch (Exception exception) { // demonstrates returning any business logic or Task execution exceptions as a suitable response to the client - this.handleException(exception, responseObserver); - return; - } - - // build a reply for this method invocation - HelloReply reply = HelloReply.newBuilder().setMessage(this.greeting + req.getName()).build(); - - // invoke external gRPC call - //this.invokeGrpcCall(req, reply); - - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - - public boolean isFailValidation() { - return isFailValidation; - } - - private HelloBean getHelloBean() { - return this.isFailValidation() ? new HelloBean() : new HelloBean("hello",10); - } - - /** Handle exceptions in invoking delegate methods.*/ - private void handleException(Exception e, StreamObserver responseObserver) { - if (TaskException.class.isAssignableFrom(e.getClass())) { - TaskException te = (TaskException)e; - if (te.getCause() != null && StatusException.class.isAssignableFrom(te.getCause().getClass())) { - responseObserver.onError((StatusException)te.getCause()); - return; - } - } - responseObserver.onError(new StatusRuntimeException(Status.INTERNAL.withDescription(e.getMessage()), new Metadata())); - } - - /** Invoke an external gRPC call as a client*/ - @SuppressWarnings("unused") - private void invokeGrpcCall(HelloRequest req, HelloReply reply) { - info("Saying hello to an external grpc service"); - try { - reply = blockingStub.sayHello(req); - }catch (Exception e){ - warn("Failed to say hello to external grpc service.Ensure Greeter service is running"); - } - } - - @Override - @Timed // the Timed annotation for publishing JMX metrics via MBean - @MethodFilters({LoggingFilter.class, AuthFilter.class}) // Method level filters - @Traced(withSamplingRate=0.0f) // Start a new Trace or participate in a Client-initiated distributed trace - public StreamObserver pingPong(StreamObserver responseObserver) { - - StreamObserver requestObserver = new StreamObserver() { - @Override - public void onNext(Ping ping) { - info("Received ping from client : " + ping.getMessage()); - Pong pong = Pong.newBuilder().setMessage("Pong").build(); - responseObserver.onNext(pong); - } - - @Override - public void onError(Throwable throwable) { - error("Error in pingPong"); - } - - @Override - public void onCompleted() { - info("Completed pingPong"); - responseObserver.onCompleted(); - } - }; - - return requestObserver; - } + /** Flag to return bad values in Validation check*/ + private final boolean isFailValidation = false; + + /** Property read from configuration*/ + private String greeting; + + /** Injected business logic class where validation is performed */ + private HelloBeanService helloBeanService; + + /** A stub to call an external grpc service.This would be injected via @{@link com.flipkart.gjex.guice.module.ClientModule}**/ + @Inject + GreeterGrpc.GreeterBlockingStub blockingStub; + + /** Demonstrate injecting custom properties from configuration */ + @Inject + public GreeterService(@Named("hw.greeting") String greeting, HelloBeanService helloBeanService) { + this.greeting = greeting; + this.helloBeanService = helloBeanService; + } + + @Override + @Api(deadlineConfig = "apiProperties.sayhello.deadline") // specify an API level Deadline that will cascade to all @ConcurrentTask invoked in serving this API + @Timed // the Timed annotation for publishing JMX metrics via MBean + @MethodFilters({LoggingFilter.class, AuthFilter.class}) // Method level filters + @Traced(withSamplingRate=0.0f) // Start a new Trace or participate in a Client-initiated distributed trace + public void sayHello(HelloRequest req, StreamObserver responseObserver) { + + info("Saying hello in Greeter service"); + info("Headers in service : " + ApplicationHeaders.getHeaders()); + + try { + // invoke business logic implemented in a separate injected class + helloBeanService.sayHelloInBean(this.getHelloBean()); + } catch (Exception exception) { // demonstrates returning any business logic or Task execution exceptions as a suitable response to the client + this.handleException(exception, responseObserver); + return; + } + + // build a reply for this method invocation + HelloReply reply = HelloReply.newBuilder().setMessage(this.greeting + req.getName()).build(); + + // invoke external gRPC call + //this.invokeGrpcCall(req, reply); + + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + public boolean isFailValidation() { + return isFailValidation; + } + + private HelloBean getHelloBean() { + return this.isFailValidation() ? new HelloBean() : new HelloBean("hello",10); + } + + /** Handle exceptions in invoking delegate methods.*/ + private void handleException(Exception e, StreamObserver responseObserver) { + if (TaskException.class.isAssignableFrom(e.getClass())) { + TaskException te = (TaskException)e; + if (te.getCause() != null && StatusException.class.isAssignableFrom(te.getCause().getClass())) { + responseObserver.onError((StatusException)te.getCause()); + return; + } + } + responseObserver.onError(new StatusRuntimeException(Status.INTERNAL.withDescription(e.getMessage()), new Metadata())); + } + + /** Invoke an external gRPC call as a client*/ + @SuppressWarnings("unused") + private void invokeGrpcCall(HelloRequest req, HelloReply reply) { + info("Saying hello to an external grpc service"); + try { + reply = blockingStub.sayHello(req); + }catch (Exception e){ + warn("Failed to say hello to external grpc service.Ensure Greeter service is running"); + } + } + + @Override + @Timed // the Timed annotation for publishing JMX metrics via MBean + @MethodFilters({LoggingFilter.class, AuthFilter.class}) // Method level filters + @Traced(withSamplingRate=0.0f) // Start a new Trace or participate in a Client-initiated distributed trace + public StreamObserver pingPong(StreamObserver responseObserver) { + + StreamObserver requestObserver = new StreamObserver() { + @Override + public void onNext(Ping ping) { + info("Received ping from client : " + ping.getMessage()); + Pong pong = Pong.newBuilder().setMessage("Pong").build(); + responseObserver.onNext(pong); + } + + @Override + public void onError(Throwable throwable) { + error("Error in pingPong"); + } + + @Override + public void onCompleted() { + info("Completed pingPong"); + responseObserver.onCompleted(); + } + }; + + return requestObserver; + } } - diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/HelloBeanService.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/HelloBeanService.java index c75618ab..84929884 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/HelloBeanService.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/service/HelloBeanService.java @@ -38,119 +38,119 @@ */ public class HelloBeanService implements Logging { - /** Method params with javax.validation annotations to force validation*/ - @Traced - public void sayHelloInBean(@NotNull @Valid HelloBean helloBean) { - info("A valid HelloBean has been passed."); - addToTrace("Request", helloBean.toString()); - this.tracedMethod1(); - } + /** Method params with javax.validation annotations to force validation*/ + @Traced + public void sayHelloInBean(@NotNull @Valid HelloBean helloBean) { + info("A valid HelloBean has been passed."); + addToTrace("Request", helloBean.toString()); + this.tracedMethod1(); + } - // ------- Methods to demonstrate Tracing flow and async execution + // ------- Methods to demonstrate Tracing flow and async execution - /** Traced method with serial invocation of next method*/ - @Traced - public void tracedMethod1() { - info("Invoked trace method1"); - this.tracedMethod2(); - } + /** Traced method with serial invocation of next method*/ + @Traced + public void tracedMethod1() { + info("Invoked trace method1"); + this.tracedMethod2(); + } - /** - * Traced method with following - * 1. Invoke two methods concurrently - * 2. Compose response - * 3. Invoke next method serially - */ - @Traced - public void tracedMethod2() { - info("Invoked trace method2"); - ResponseEntity entity = new ResponseEntity(); - FutureDecorator future1 = (FutureDecorator)this.tracedMethod3(entity); - FutureDecorator future2 = (FutureDecorator)this.tracedMethod4(entity); - ResponseEntity finalEntity = FutureDecorator.compose(future1, future2, - new BiFunction() { - @Override - public ResponseEntity apply(ResponseEntity t1, ResponseEntity t2) throws Exception { - ResponseEntity finalEntity = new ResponseEntity(); - finalEntity.method2 = entity.method2; - if (t1 != null) { // call to get t1 may have timedout or errored. Execution was allowed to proceed as it is optional - finalEntity.method3 = entity.method3; // we use the value only if the call was successful, else it is null - } - finalEntity.method4 = t2.method4; - return finalEntity; - } - }); - this.tracedMethod5(finalEntity); - info("Final response : " + finalEntity.method5); - } + /** + * Traced method with following + * 1. Invoke two methods concurrently + * 2. Compose response + * 3. Invoke next method serially + */ + @Traced + public void tracedMethod2() { + info("Invoked trace method2"); + ResponseEntity entity = new ResponseEntity(); + FutureDecorator future1 = (FutureDecorator)this.tracedMethod3(entity); + FutureDecorator future2 = (FutureDecorator)this.tracedMethod4(entity); + ResponseEntity finalEntity = FutureDecorator.compose(future1, future2, + new BiFunction() { + @Override + public ResponseEntity apply(ResponseEntity t1, ResponseEntity t2) throws Exception { + ResponseEntity finalEntity = new ResponseEntity(); + finalEntity.method2 = entity.method2; + if (t1 != null) { // call to get t1 may have timedout or errored. Execution was allowed to proceed as it is optional + finalEntity.method3 = entity.method3; // we use the value only if the call was successful, else it is null + } + finalEntity.method4 = t2.method4; + return finalEntity; + } + }); + this.tracedMethod5(finalEntity); + info("Final response : " + finalEntity.method5); + } - /** - * A Concurrent Traced task executing in its own threadpool via HystrixCommand. Modifies the entity passed to it - * Here timeout is configured as an explicit value. This method is also marked as {@link ConcurrentTask.Completion#Optional} - */ - @Traced - @ConcurrentTask(completion=ConcurrentTask.Completion.Optional, timeout = 300, withRequestHedging = true) - // Task is marked to timeout, execution will proceed as this is marked as Optional, Request hedging is enabled for this Task - public Future tracedMethod3(ResponseEntity entity) { - return new AsyncResult() { + /** + * A Concurrent Traced task executing in its own threadpool via HystrixCommand. Modifies the entity passed to it + * Here timeout is configured as an explicit value. This method is also marked as {@link ConcurrentTask.Completion#Optional} + */ + @Traced + @ConcurrentTask(completion=ConcurrentTask.Completion.Optional, timeout = 300, withRequestHedging = true) + // Task is marked to timeout, execution will proceed as this is marked as Optional, Request hedging is enabled for this Task + public Future tracedMethod3(ResponseEntity entity) { + return new AsyncResult() { @Override public ResponseEntity invoke() { - sleep(200); - info("Headers in method3 : " + ApplicationHeaders.getHeaders()); - info("Invoked trace method3"); - entity.method3 = "InvokedMethod3"; - return entity; + sleep(200); + info("Headers in method3 : " + ApplicationHeaders.getHeaders()); + info("Invoked trace method3"); + entity.method3 = "InvokedMethod3"; + return entity; } }; - } + } - /** - * A Concurrent Traced task executing in its own threadpool via HystrixCommand. Modifies the entity passed to it - * Timeout is configured as a config property in application configuration i.e. hello_world_config.yml in this example - * This method is implicitly(default) marked as {@link ConcurrentTask.Completion#Mandatory} - */ - @Traced - @ConcurrentTask(timeoutConfig = "taskProperties.hello.timeout") - public Future tracedMethod4(ResponseEntity entity) { - return new AsyncResult() { + /** + * A Concurrent Traced task executing in its own threadpool via HystrixCommand. Modifies the entity passed to it + * Timeout is configured as a config property in application configuration i.e. hello_world_config.yml in this example + * This method is implicitly(default) marked as {@link ConcurrentTask.Completion#Mandatory} + */ + @Traced + @ConcurrentTask(timeoutConfig = "taskProperties.hello.timeout") + public Future tracedMethod4(ResponseEntity entity) { + return new AsyncResult() { @Override public ResponseEntity invoke() { - sleep(100); - info("Invoked trace method4"); - entity.method4 = "InvokedMethod4"; - return entity; + sleep(100); + info("Invoked trace method4"); + entity.method4 = "InvokedMethod4"; + return entity; } }; - } + } - /** Traced method invoking the next method serially*/ - @Traced - public void tracedMethod5(ResponseEntity entity) { - info("Invoked trace method5"); - entity.method5 = entity.method3 + "-" + entity.method4; - this.tracedMethod6(); - } + /** Traced method invoking the next method serially*/ + @Traced + public void tracedMethod5(ResponseEntity entity) { + info("Invoked trace method5"); + entity.method5 = entity.method3 + "-" + entity.method4; + this.tracedMethod6(); + } - /** Traced method that doesnot invoke anything else*/ - @Traced - public void tracedMethod6() { - info("Invoked trace method6"); - } + /** Traced method that doesnot invoke anything else*/ + @Traced + public void tracedMethod6() { + info("Invoked trace method6"); + } - /** Collector/Entity to collect/hold data from multiple concurrent task executions*/ - class ResponseEntity { - String method2; - String method3; - String method4; - String method5; - } + /** Collector/Entity to collect/hold data from multiple concurrent task executions*/ + class ResponseEntity { + String method2; + String method3; + String method4; + String method5; + } - /** Puts the current Thread to sleep*/ - private void sleep(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - // do nothing - } - } + /** Puts the current Thread to sleep*/ + private void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + // do nothing + } + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/tracing/AllWhitelistTracingSampler.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/tracing/AllWhitelistTracingSampler.java index 3ddda541..471d71fe 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/tracing/AllWhitelistTracingSampler.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/tracing/AllWhitelistTracingSampler.java @@ -24,8 +24,8 @@ */ public class AllWhitelistTracingSampler extends ConfigurableTracingSampler { - @Override - public boolean isSampled(String component) { - return true; - } + @Override + public boolean isSampled(String component) { + return true; + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource1.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource1.java index 94412f0a..3b5bcada 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource1.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource1.java @@ -34,11 +34,11 @@ @Named public class HelloWorldResource1 { - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/hellocontrol1") - public Response performHelloControl() { - return Response.status(Response.Status.OK).entity("Hello Control 1 invoked").build(); - } + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/hellocontrol1") + public Response performHelloControl() { + return Response.status(Response.Status.OK).entity("Hello Control 1 invoked").build(); + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource2.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource2.java index 2c6c7570..a2eed393 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource2.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResource2.java @@ -34,11 +34,11 @@ @Named public class HelloWorldResource2 { - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/hellocontrol2") - public Response performHelloControl() { - return Response.status(Response.Status.OK).entity("Hello Control 2 invoked").build(); - } + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/hellocontrol2") + public Response performHelloControl() { + return Response.status(Response.Status.OK).entity("Hello Control 2 invoked").build(); + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java index 02514bbf..7e17ee53 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/HelloWorldResourceConfig.java @@ -30,11 +30,11 @@ @Named("HelloWorldResourceConfig") public class HelloWorldResourceConfig extends ResourceConfig { - @Inject - public HelloWorldResourceConfig (HelloWorldResource1 helloWorldresource1, - HelloWorldResource2 helloWorldresource2) { - register(helloWorldresource1); - register(helloWorldresource2); - } + @Inject + public HelloWorldResourceConfig (HelloWorldResource1 helloWorldresource1, + HelloWorldResource2 helloWorldresource2) { + register(helloWorldresource1); + register(helloWorldresource2); + } } diff --git a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/httpfilter/ExampleHttpFilter.java b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/httpfilter/ExampleHttpFilter.java index 500c43b0..87479909 100644 --- a/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/httpfilter/ExampleHttpFilter.java +++ b/examples/src/main/java/com/flipkart/gjex/examples/helloworld/web/httpfilter/ExampleHttpFilter.java @@ -20,15 +20,15 @@ public HttpFilter getInstance() { @Override public void doProcessResponse(ServletResponse response) { if (logger.isInfoEnabled()){ - HttpServletResponse httpServletResponse = (HttpServletResponse) response; - logger.info("{} {} {} {} {} {}", - this.getClass().getSimpleName(), - requestParams.getClientIp(), - requestParams.getResourcePath(), - httpServletResponse.getStatus(), - httpServletResponse.getHeader(CONTENT_LENGTH_HEADER), - System.currentTimeMillis()-startTime - ); + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + logger.info("{} {} {} {} {} {}", + this.getClass().getSimpleName(), + requestParams.getClientIp(), + requestParams.getResourcePath(), + httpServletResponse.getStatus(), + httpServletResponse.getHeader(CONTENT_LENGTH_HEADER), + System.currentTimeMillis()-startTime + ); } } } diff --git a/gradlew b/gradlew index 1b6c7873..5b175fec 100755 --- a/gradlew +++ b/gradlew @@ -75,8 +75,8 @@ do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; esac done @@ -142,13 +142,13 @@ fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( - max*) + max*) MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( - '' | soft) :;; #( - *) + '' | soft) :;; #( + *) ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -173,10 +173,10 @@ if "$cygwin" || "$msys" ; then for arg do if case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( - *) false ;; + *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) diff --git a/grpc-jexpress-template/.gitignore b/grpc-jexpress-template/.gitignore index a54780a8..cbd0b1b4 100644 --- a/grpc-jexpress-template/.gitignore +++ b/grpc-jexpress-template/.gitignore @@ -23,4 +23,4 @@ hs_err_pid* .idea/* -.idea \ No newline at end of file +.idea diff --git a/grpc-jexpress-template/envoy.yml b/grpc-jexpress-template/envoy.yml index ad0103e9..710607fe 100644 --- a/grpc-jexpress-template/envoy.yml +++ b/grpc-jexpress-template/envoy.yml @@ -7,32 +7,32 @@ static_resources: listeners: - name: listener1 address: - socket_address: { address: 0.0.0.0, port_value: 51051 } + socket_address: { address: 0.0.0.0, port_value: 51051 } filter_chains: - filters: - - name: envoy.http_connection_manager + - name: envoy.http_connection_manager config: - stat_prefix: grpc_json - codec_type: AUTO - route_config: + stat_prefix: grpc_json + codec_type: AUTO + route_config: name: local_route virtual_hosts: - name: local_service - domains: ["*"] - routes: - - match: { prefix: "/" , grpc: {}} + domains: ["*"] + routes: + - match: { prefix: "/" , grpc: {}} route: { cluster: grpc, timeout: { seconds: 60 } } - http_filters: - - name: envoy.grpc_json_transcoder + http_filters: + - name: envoy.grpc_json_transcoder config: - proto_descriptor: "/tmp/sample_proto_descriptor_set.pb" - services: ["service.UserService"] - print_options: + proto_descriptor: "/tmp/sample_proto_descriptor_set.pb" + services: ["service.UserService"] + print_options: add_whitespace: true always_print_primitive_fields: true always_print_enums_as_ints: false preserve_proto_field_names: false - - name: envoy.router + - name: envoy.router clusters: - name: grpc diff --git a/grpc-jexpress-template/sample_proto_descriptor_set.pb b/grpc-jexpress-template/sample_proto_descriptor_set.pb index 0188318c..977def62 100644 Binary files a/grpc-jexpress-template/sample_proto_descriptor_set.pb and b/grpc-jexpress-template/sample_proto_descriptor_set.pb differ diff --git a/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/healthcheck/AllIsWellHealthCheck.java b/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/healthcheck/AllIsWellHealthCheck.java index 1af823e8..7b9def8f 100644 --- a/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/healthcheck/AllIsWellHealthCheck.java +++ b/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/healthcheck/AllIsWellHealthCheck.java @@ -20,10 +20,10 @@ public class AllIsWellHealthCheck extends HealthCheck implements Logging { - @Override - protected Result check() throws Exception { - info("Returning healthy status."); - return Result.healthy("All Is Well"); - } + @Override + protected Result check() throws Exception { + info("Returning healthy status."); + return Result.healthy("All Is Well"); + } } diff --git a/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/service/SampleService.java b/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/service/SampleService.java index 5cf9d3af..672037db 100644 --- a/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/service/SampleService.java +++ b/grpc-jexpress-template/src/main/java/com/flipkart/grpc/jexpress/service/SampleService.java @@ -28,10 +28,10 @@ public class SampleService extends UserServiceGrpc.UserServiceImplBase implement @Inject public SampleService(SampleConfiguration sampleConfiguration, - @Named("GlobalFlattenedConfig") Configuration flattenedConfig, - @Named("GlobalMapConfig") Map mapConfig, - @Named("database.driverClass") String driverClass, - @Named("database.properties.hibernate.session.events.log") boolean hibernateGenerateEventLog) + @Named("GlobalFlattenedConfig") Configuration flattenedConfig, + @Named("GlobalMapConfig") Map mapConfig, + @Named("database.driverClass") String driverClass, + @Named("database.properties.hibernate.session.events.log") boolean hibernateGenerateEventLog) { this.sampleConfiguration = sampleConfiguration; this.flattenedConfig = flattenedConfig; diff --git a/grpc-jexpress-template/src/main/proto/userservice.proto b/grpc-jexpress-template/src/main/proto/userservice.proto index e675d62b..3d32bec5 100644 --- a/grpc-jexpress-template/src/main/proto/userservice.proto +++ b/grpc-jexpress-template/src/main/proto/userservice.proto @@ -11,15 +11,15 @@ package service; service UserService { rpc GetUser (GetRequest) returns (GetResponse) { option (google.api.http) = { - get: "/v1/userservice/{id}" - }; + get: "/v1/userservice/{id}" + }; } rpc CreateUser (CreateRequest) returns (CreateResponse) { option (google.api.http) = { - post: "/v1/userservice" - body: "*" - }; + post: "/v1/userservice" + body: "*" + }; } } diff --git a/grpc-jexpress-template/src/main/resources/configuration.yml b/grpc-jexpress-template/src/main/resources/configuration.yml index 838c0a8c..515b387e 100644 --- a/grpc-jexpress-template/src/main/resources/configuration.yml +++ b/grpc-jexpress-template/src/main/resources/configuration.yml @@ -41,4 +41,3 @@ database: charSet: UTF-8 hibernate.generate_statistics: false hibernate.session.events.log: false - diff --git a/guice/.gitignore b/guice/.gitignore index e0d24228..bc5a71c1 100644 --- a/guice/.gitignore +++ b/guice/.gitignore @@ -4,4 +4,4 @@ /bin/ /build/ ./out -./build \ No newline at end of file +./build diff --git a/guice/build.gradle b/guice/build.gradle index 73c88810..80b338b0 100644 --- a/guice/build.gradle +++ b/guice/build.gradle @@ -4,7 +4,7 @@ plugins { } tasks.withType(GenerateModuleMetadata) { - enabled = false + enabled = false } publishing { @@ -14,10 +14,10 @@ publishing { from components.java pom { licenses { - license { + license { name = "The Apache License, Version 2.0" url = "http://www.apache.org/licenses/LICENSE-2.0.txt" - } + } } } } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/channel/ChannelConfig.java b/guice/src/main/java/com/flipkart/gjex/grpc/channel/ChannelConfig.java index e187e6b8..a19a858d 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/channel/ChannelConfig.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/channel/ChannelConfig.java @@ -26,27 +26,27 @@ public class ChannelConfig { private int port; private long deadlineInMs=Long.MAX_VALUE; - public ChannelConfig(String hostname, int port) { - super(); - this.hostname = hostname; - this.port = port; - } - public String getHostname() { - return hostname; - } - public void setHostname(String hostname) { - this.hostname = hostname; - } - public int getPort() { - return port; - } - public void setPort(int port) { - this.port = port; - } - public long getDeadlineInMs() { - return deadlineInMs; - } - public void setDeadlineInMs(long deadlineInMs) { - this.deadlineInMs = deadlineInMs; - } + public ChannelConfig(String hostname, int port) { + super(); + this.hostname = hostname; + this.port = port; + } + public String getHostname() { + return hostname; + } + public void setHostname(String hostname) { + this.hostname = hostname; + } + public int getPort() { + return port; + } + public void setPort(int port) { + this.port = port; + } + public long getDeadlineInMs() { + return deadlineInMs; + } + public void setDeadlineInMs(long deadlineInMs) { + this.deadlineInMs = deadlineInMs; + } } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/channel/InstrumentedChannel.java b/guice/src/main/java/com/flipkart/gjex/grpc/channel/InstrumentedChannel.java index 8838d4da..995247b4 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/channel/InstrumentedChannel.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/channel/InstrumentedChannel.java @@ -68,4 +68,3 @@ private String getMetricName(String fullMethodName){ return fullMethodName.replace("/", "."); } } - diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/ClientTracingInterceptor.java b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/ClientTracingInterceptor.java index c2646f00..ab1cd933 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/ClientTracingInterceptor.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/ClientTracingInterceptor.java @@ -51,8 +51,8 @@ public class ClientTracingInterceptor implements ClientInterceptor { private final ActiveSpanSource activeSpanSource; /** - * @param tracer to use to trace requests - */ + * @param tracer to use to trace requests + */ public ClientTracingInterceptor(Tracer tracer) { this.tracer = tracer; this.operationNameConstructor = OperationNameConstructor.DEFAULT; @@ -60,10 +60,10 @@ public ClientTracingInterceptor(Tracer tracer) { } /** - * Use this intercepter to trace requests made by this client channel. - * @param channel to be traced - * @return intercepted channel - */ + * Use this intercepter to trace requests made by this client channel. + * @param channel to be traced + * @return intercepted channel + */ public Channel intercept(Channel channel) { return ClientInterceptors.intercept(channel, this); } @@ -74,59 +74,59 @@ public ClientCall interceptCall( Span activeSpan = this.activeSpanSource.getActiveSpan(); if (activeSpan != null) { - final String operationName = operationNameConstructor.constructOperationName(method); - - final Span span = createSpanFromParent(activeSpan, operationName); - - if (callOptions.getDeadline() == null) { - span.setTag("grpc.deadline_millis", "null"); - } else { - span.setTag("grpc.deadline_millis", callOptions.getDeadline().timeRemaining(TimeUnit.MILLISECONDS)); - } - - return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { - - @Override - public void start(Listener responseListener, Metadata headers) { - tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMap() { - @Override - public void put(String key, String value) { - Metadata.Key headerKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); - headers.put(headerKey, value); - } - @Override - public Iterator> iterator() { - throw new UnsupportedOperationException( - "TextMapInjectAdapter should only be used with Tracer.inject()"); - } - }); - Listener tracingResponseListener = new ForwardingClientCallListener.SimpleForwardingClientCallListener( - responseListener) { - @Override - public void onClose(Status status, Metadata trailers) { - span.finish(); - delegate().onClose(status, trailers); - } - }; - delegate().start(tracingResponseListener, headers); - } - - @Override - public void cancel(@Nullable String message, @Nullable Throwable cause) { - String errorMessage; - if (message == null) { - errorMessage = "Error"; - } else { - errorMessage = message; - } - if (cause == null) { - span.log(errorMessage); - } else { - span.log(ImmutableMap.of(errorMessage, cause.getMessage())); - } - delegate().cancel(message, cause); - } - }; + final String operationName = operationNameConstructor.constructOperationName(method); + + final Span span = createSpanFromParent(activeSpan, operationName); + + if (callOptions.getDeadline() == null) { + span.setTag("grpc.deadline_millis", "null"); + } else { + span.setTag("grpc.deadline_millis", callOptions.getDeadline().timeRemaining(TimeUnit.MILLISECONDS)); + } + + return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { + + @Override + public void start(Listener responseListener, Metadata headers) { + tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMap() { + @Override + public void put(String key, String value) { + Metadata.Key headerKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + headers.put(headerKey, value); + } + @Override + public Iterator> iterator() { + throw new UnsupportedOperationException( + "TextMapInjectAdapter should only be used with Tracer.inject()"); + } + }); + Listener tracingResponseListener = new ForwardingClientCallListener.SimpleForwardingClientCallListener( + responseListener) { + @Override + public void onClose(Status status, Metadata trailers) { + span.finish(); + delegate().onClose(status, trailers); + } + }; + delegate().start(tracingResponseListener, headers); + } + + @Override + public void cancel(@Nullable String message, @Nullable Throwable cause) { + String errorMessage; + if (message == null) { + errorMessage = "Error"; + } else { + errorMessage = message; + } + if (cause == null) { + span.log(errorMessage); + } else { + span.log(ImmutableMap.of(errorMessage, cause.getMessage())); + } + delegate().cancel(message, cause); + } + }; } return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)){}; } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/FilterInterceptor.java b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/FilterInterceptor.java index d7506871..7c5d654c 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/FilterInterceptor.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/FilterInterceptor.java @@ -59,8 +59,8 @@ public class FilterInterceptor implements ServerInterceptor, Logging { /** - * Map of Filter instances mapped to Service and its method - */ + * Map of Filter instances mapped to Service and its method + */ @SuppressWarnings("rawtypes") private final Map> filtersMap = new HashMap<>(); @@ -185,8 +185,8 @@ public void onCancel() { } /** - * Helper method to handle RuntimeExceptions and convert it into suitable gRPC message. Closes the ServerCall - */ + * Helper method to handle RuntimeExceptions and convert it into suitable gRPC message. Closes the ServerCall + */ private void handleException(ServerCall call, Exception e) { error("Closing gRPC call due to RuntimeException.", e); Status returnStatus = Status.INTERNAL; diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/StatusMetricInterceptor.java b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/StatusMetricInterceptor.java index 04b8f5b5..99af2c33 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/StatusMetricInterceptor.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/StatusMetricInterceptor.java @@ -43,8 +43,8 @@ public void registerMeteredMethods(List bindableServices) { @Override public ServerCall.Listener interceptCall(ServerCall call, - Metadata metadata, - ServerCallHandler next) { + Metadata metadata, + ServerCallHandler next) { String methodName = call.getMethodDescriptor().getFullMethodName(); if (!meteredMethods.contains(methodName.toLowerCase())) { return next.startCall(new ForwardingServerCall.SimpleForwardingServerCall(call) { diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/TracingInterceptor.java b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/TracingInterceptor.java index a9c41307..d0023b85 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/TracingInterceptor.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/interceptor/TracingInterceptor.java @@ -64,89 +64,89 @@ @Named("TracingInterceptor") public class TracingInterceptor implements ServerInterceptor, Logging { - /** Map of ConfigurableTracingSampler instance mapped to Service and its method*/ - private final TracingSamplerHolder samplerMap; - private final Tracer tracer; + /** Map of ConfigurableTracingSampler instance mapped to Service and its method*/ + private final TracingSamplerHolder samplerMap; + private final Tracer tracer; - @Inject - public TracingInterceptor(@Named("Tracer")Tracer tracer, - @Named("TracingSamplerHolder")TracingSamplerHolder samplerMap) { - this.samplerMap = samplerMap; - this.tracer = tracer; - } + @Inject + public TracingInterceptor(@Named("Tracer")Tracer tracer, + @Named("TracingSamplerHolder")TracingSamplerHolder samplerMap) { + this.samplerMap = samplerMap; + this.tracer = tracer; + } - public void registerTracingSamplers(List samplers, List services) { - Map, TracingSampler> classToInstanceMap = samplers.stream() - .collect(Collectors.toMap(Object::getClass, Function.identity())); - services.forEach(service -> { - List> annotatedMethods = AnnotationUtils.getAnnotatedMethods(service.getClass(), Traced.class); - if(annotatedMethods != null) { - annotatedMethods.forEach(pair -> { - Arrays.asList(pair.getValue().getAnnotation(Traced.class).withTracingSampler()).forEach(samplerClass -> { - // Key is of the form + "/" + - // reflecting the structure followed in the gRPC HandlerRegistry using MethodDescriptor#getFullMethodName() - String samplerComponentName = (service.bindService().getServiceDescriptor().getName() + "/" - + pair.getValue().getName()).toLowerCase(); - if (samplerClass == TracingSampler.class) { - samplerMap.put(samplerComponentName, new ConfigurableTracingSampler()); - } else { - if (!classToInstanceMap.containsKey(samplerClass)) { - throw new RuntimeException("TracingSampler instance not bound for TracingSampler class :" + samplerClass.getName()); - } - samplerMap.put(samplerComponentName, classToInstanceMap.get(samplerClass)); - } - }); - }); - } - }); + public void registerTracingSamplers(List samplers, List services) { + Map, TracingSampler> classToInstanceMap = samplers.stream() + .collect(Collectors.toMap(Object::getClass, Function.identity())); + services.forEach(service -> { + List> annotatedMethods = AnnotationUtils.getAnnotatedMethods(service.getClass(), Traced.class); + if(annotatedMethods != null) { + annotatedMethods.forEach(pair -> { + Arrays.asList(pair.getValue().getAnnotation(Traced.class).withTracingSampler()).forEach(samplerClass -> { + // Key is of the form + "/" + + // reflecting the structure followed in the gRPC HandlerRegistry using MethodDescriptor#getFullMethodName() + String samplerComponentName = (service.bindService().getServiceDescriptor().getName() + "/" + + pair.getValue().getName()).toLowerCase(); + if (samplerClass == TracingSampler.class) { + samplerMap.put(samplerComponentName, new ConfigurableTracingSampler()); + } else { + if (!classToInstanceMap.containsKey(samplerClass)) { + throw new RuntimeException("TracingSampler instance not bound for TracingSampler class :" + samplerClass.getName()); + } + samplerMap.put(samplerComponentName, classToInstanceMap.get(samplerClass)); + } + }); + }); + } + }); - } + } - @Override - public Listener interceptCall(ServerCall call, Metadata headers,ServerCallHandler next) { - TracingSampler tracingSampler = this.samplerMap.get(call.getMethodDescriptor().getFullMethodName().toLowerCase()); - if (tracingSampler == null) { - return new SimpleForwardingServerCallListener(next.startCall( - new SimpleForwardingServerCall(call){},headers)){}; - } - Map headerMap = new HashMap(); - for (String key : headers.keys()) { - if (!key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { - String value = headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)); - headerMap.put(key, value); - } - } + @Override + public Listener interceptCall(ServerCall call, Metadata headers,ServerCallHandler next) { + TracingSampler tracingSampler = this.samplerMap.get(call.getMethodDescriptor().getFullMethodName().toLowerCase()); + if (tracingSampler == null) { + return new SimpleForwardingServerCallListener(next.startCall( + new SimpleForwardingServerCall(call){},headers)){}; + } + Map headerMap = new HashMap(); + for (String key : headers.keys()) { + if (!key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { + String value = headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)); + headerMap.put(key, value); + } + } - final Span span = getSpanFromHeaders(call,headerMap); - /* - * Set the client side initiated Trace and Span in the Context. - * Note : we do not active the Span. This will be done in the TracingModule based on sampling enabled/not-enabled for the service's method - */ - Context ctxWithSpan = Context.current().withValues(GJEXContext.getKeyRoot(), span, // root span and active span are the same - GJEXContext.getKeyActiveSpan(), span, - GJEXContext.getSpanContextKey(), span.context(), - GJEXContext.getTracingSamplerKey(), tracingSampler); // pass on the TracingSampler for use in downstream calls for e.g. in TracingModule + final Span span = getSpanFromHeaders(call,headerMap); + /* + * Set the client side initiated Trace and Span in the Context. + * Note : we do not active the Span. This will be done in the TracingModule based on sampling enabled/not-enabled for the service's method + */ + Context ctxWithSpan = Context.current().withValues(GJEXContext.getKeyRoot(), span, // root span and active span are the same + GJEXContext.getKeyActiveSpan(), span, + GJEXContext.getSpanContextKey(), span.context(), + GJEXContext.getTracingSamplerKey(), tracingSampler); // pass on the TracingSampler for use in downstream calls for e.g. in TracingModule - ServerCall.Listener listenerWithContext = Contexts.interceptCall(ctxWithSpan, call, headers, next); + ServerCall.Listener listenerWithContext = Contexts.interceptCall(ctxWithSpan, call, headers, next); - return new SimpleForwardingServerCallListener(listenerWithContext) {}; - } + return new SimpleForwardingServerCallListener(listenerWithContext) {}; + } - /** - * Creates and returns a Span from gRPC headers, if any. - */ - private Span getSpanFromHeaders(ServerCall call, Map headers) { - String methodInvoked = call.getMethodDescriptor().getFullMethodName(); - Span span = null; - try { - SpanContext parentSpanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapExtractAdapter(headers)); - span = tracer.buildSpan(methodInvoked).asChildOf(parentSpanCtx).start(); - //Service name can be added as a Tag from opentracing-java version v0.31.1 onwards - //Tags.SERVICE.set(span, MethodDescriptor.extractFullServiceName(methodInvoked)); - } catch (IllegalArgumentException iae) { - span = tracer.buildSpan(methodInvoked).withTag("Error", "Extract failed and an IllegalArgumentException was thrown") - .start(); - } - return span; - } + /** + * Creates and returns a Span from gRPC headers, if any. + */ + private Span getSpanFromHeaders(ServerCall call, Map headers) { + String methodInvoked = call.getMethodDescriptor().getFullMethodName(); + Span span = null; + try { + SpanContext parentSpanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapExtractAdapter(headers)); + span = tracer.buildSpan(methodInvoked).asChildOf(parentSpanCtx).start(); + //Service name can be added as a Tag from opentracing-java version v0.31.1 onwards + //Tags.SERVICE.set(span, MethodDescriptor.extractFullServiceName(methodInvoked)); + } catch (IllegalArgumentException iae) { + span = tracer.buildSpan(methodInvoked).withTag("Error", "Extract failed and an IllegalArgumentException was thrown") + .start(); + } + return span; + } } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java b/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java index 70090236..bbf66509 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/service/ApiServer.java @@ -47,59 +47,59 @@ @Named("APIServer") public class ApiServer extends AbstractService implements Logging { - private final Server apiServer; - private final ResourceRegistrar resourceRegistrar; - private final ServletContextHandler context; - private HttpFilterInterceptor httpFilterInterceptor; - private List resourceConfigs = new ArrayList<>(); + private final Server apiServer; + private final ResourceRegistrar resourceRegistrar; + private final ServletContextHandler context; + private HttpFilterInterceptor httpFilterInterceptor; + private List resourceConfigs = new ArrayList<>(); - @Inject - public ApiServer(@Named("APIJettyServer") Server apiServer, - @Named("ApiServletContext") ServletContextHandler context, - @Named("HttpFilterInterceptor") HttpFilterInterceptor httpFilterInterceptor, - ResourceRegistrar resourceRegistrar) { - this.apiServer = apiServer; - this.context = context; - this.httpFilterInterceptor = httpFilterInterceptor; - this.resourceRegistrar = resourceRegistrar; - } + @Inject + public ApiServer(@Named("APIJettyServer") Server apiServer, + @Named("ApiServletContext") ServletContextHandler context, + @Named("HttpFilterInterceptor") HttpFilterInterceptor httpFilterInterceptor, + ResourceRegistrar resourceRegistrar) { + this.apiServer = apiServer; + this.context = context; + this.httpFilterInterceptor = httpFilterInterceptor; + this.resourceRegistrar = resourceRegistrar; + } - public void registerResources(List resourceConfigs) { - this.resourceConfigs.addAll(resourceConfigs); - } + public void registerResources(List resourceConfigs) { + this.resourceConfigs.addAll(resourceConfigs); + } - public void registerFilters(List httpFilterParamsList, + public void registerFilters(List httpFilterParamsList, List javaxFilterParamsList, HttpFilterConfig httpFilterConfig){ - configureAccessLog(httpFilterParamsList, httpFilterConfig); - httpFilterInterceptor.registerFilters(httpFilterParamsList); - context.addFilter(new FilterHolder(httpFilterInterceptor), "/*", EnumSet.of(DispatcherType.REQUEST)); + configureAccessLog(httpFilterParamsList, httpFilterConfig); + httpFilterInterceptor.registerFilters(httpFilterParamsList); + context.addFilter(new FilterHolder(httpFilterInterceptor), "/*", EnumSet.of(DispatcherType.REQUEST)); for (JavaxFilterParams javaxFilterParams: javaxFilterParamsList){ context.addFilter(new FilterHolder(javaxFilterParams.getFilter()), javaxFilterParams.getPathSpec(), EnumSet.of(DispatcherType.REQUEST)); } - } + } - private void configureAccessLog(List httpFilterParamsList, HttpFilterConfig httpFilterConfig){ - if (httpFilterConfig.isEnableAccessLogs()){ - httpFilterParamsList.add(0, HttpFilterParams.builder().filter(new AccessLogHttpFilter()).pathSpec("/*").build()); - } - } + private void configureAccessLog(List httpFilterParamsList, HttpFilterConfig httpFilterConfig){ + if (httpFilterConfig.isEnableAccessLogs()){ + httpFilterParamsList.add(0, HttpFilterParams.builder().filter(new AccessLogHttpFilter()).pathSpec("/*").build()); + } + } - @Override - public void doStart() throws Exception { - this.resourceRegistrar.registerResources(this.resourceConfigs); // register any custom web resources added by the GJEX application - this.apiServer.start(); - info("API Server started and listening on port : " + this.apiServer.getURI().getPort()); - } + @Override + public void doStart() throws Exception { + this.resourceRegistrar.registerResources(this.resourceConfigs); // register any custom web resources added by the GJEX application + this.apiServer.start(); + info("API Server started and listening on port : " + this.apiServer.getURI().getPort()); + } - @Override - public void doStop() { - try { - this.apiServer.stop(); - } catch (Exception e) { - // Just log the error as we are stopping anyway - error("Error stopping API server : " + e.getMessage(), e); - } - } + @Override + public void doStop() { + try { + this.apiServer.stop(); + } catch (Exception e) { + // Just log the error as we are stopping anyway + error("Error stopping API server : " + e.getMessage(), e); + } + } } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/service/DashboardServer.java b/guice/src/main/java/com/flipkart/gjex/grpc/service/DashboardServer.java index 81d74eee..977e49de 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/service/DashboardServer.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/service/DashboardServer.java @@ -37,25 +37,25 @@ public class DashboardServer extends AbstractService implements Logging { private final Server dashboardServer; - @Inject - public DashboardServer(@Named("DashboardJettyServer") Server dashboardServer) { - this.dashboardServer = dashboardServer; - } - - @Override - public void doStart() throws Exception { - this.dashboardServer.start(); - info("Dashboard Server started and listening on port : " + this.dashboardServer.getURI().getPort()); - } - - @Override - public void doStop() { - try { - this.dashboardServer.stop(); - } catch (Exception e) { - // Just log the error as we are stopping anyway - error("Error stopping Dashboard server : " + e.getMessage(), e); - } - } + @Inject + public DashboardServer(@Named("DashboardJettyServer") Server dashboardServer) { + this.dashboardServer = dashboardServer; + } + + @Override + public void doStart() throws Exception { + this.dashboardServer.start(); + info("Dashboard Server started and listening on port : " + this.dashboardServer.getURI().getPort()); + } + + @Override + public void doStop() { + try { + this.dashboardServer.stop(); + } catch (Exception e) { + // Just log the error as we are stopping anyway + error("Error stopping Dashboard server : " + e.getMessage(), e); + } + } } diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/service/GrpcServer.java b/guice/src/main/java/com/flipkart/gjex/grpc/service/GrpcServer.java index 82d8517c..702a0cf1 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/service/GrpcServer.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/service/GrpcServer.java @@ -50,76 +50,76 @@ @Named("GrpcServer") public class GrpcServer extends AbstractService implements Logging { - /** Default port number if none is specified*/ - private int port = 50051; - - /** Default maximum message size allowed to be received on the server*/ - private int maxMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; - - /** The core Grpc Server instance and its builder*/ - private ServerBuilder grpcServerBuilder; - private Server grpcServer; - - /** The ServerInterceptors*/ - private FilterInterceptor filterInterceptor; - private TracingInterceptor tracingInterceptor; - private StatusMetricInterceptor statusMetricInterceptor; - - @Inject - public GrpcServer(GJEXConfiguration configuration, - @Named("FilterInterceptor") FilterInterceptor filterInterceptor, - @Named("TracingInterceptor") TracingInterceptor tracingInterceptor, - @Named("StatusMetricInterceptor") StatusMetricInterceptor statusMetricInterceptor) { - if (configuration.getGrpc().getPort() > 0) { - this.port = configuration.getGrpc().getPort(); - info("Creating GrpcServer listening on port : " + port); - } - - if (configuration.getGrpc().getMaxMessageSize() > 0) { - this.maxMessageSize = configuration.getGrpc().getMaxMessageSize(); - info("Creating GrpcServer with maximum message size allowed : " + maxMessageSize); - } - this.grpcServerBuilder = Grpc.newServerBuilderForPort(this.port, InsecureServerCredentials.create()).maxInboundMessageSize(this.maxMessageSize); - - if (configuration.getGrpc().getExecutorThreads() > 0) { - this.grpcServerBuilder.executor( - Executors.newFixedThreadPool( - configuration.getGrpc().getExecutorThreads(), - GrpcUtil.getThreadFactory("grpc-executor-%d", true))); - } - - - this.filterInterceptor = filterInterceptor; - this.tracingInterceptor = tracingInterceptor; - this.statusMetricInterceptor = statusMetricInterceptor; - } - - @Override - public void doStart() throws Exception { - this.grpcServer = this.grpcServerBuilder.addService(ProtoReflectionService.newInstance()).build().start(); - info("GJEX GrpcServer started.Hosting these services : ****** Start *****"); - this.grpcServer.getServices().forEach(serviceDefinition -> info(serviceDefinition.getServiceDescriptor().getName())); - info("GJEX GrpcServer started.Hosting these services : ****** End *****"); - // Not waiting for termination as this blocks main thread preventing any subsequent startup, like the Jetty Dashboard server - // this.grpcServer.awaitTermination(); - - } - - @Override - public void doStop() { - if (this.grpcServer != null) { - this.grpcServer.shutdown(); - } - info("GJEX GrpcServer stopped."); - } - - public void registerFilters(@SuppressWarnings("rawtypes") List grpcFilters, List services, GrpcFilterConfig grpcFilterConfig) { - this.filterInterceptor.registerFilters(grpcFilters, services, grpcFilterConfig); - } - - public void registerTracingSamplers(List samplers, List services) { - this.tracingInterceptor.registerTracingSamplers(samplers, services); - } + /** Default port number if none is specified*/ + private int port = 50051; + + /** Default maximum message size allowed to be received on the server*/ + private int maxMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; + + /** The core Grpc Server instance and its builder*/ + private ServerBuilder grpcServerBuilder; + private Server grpcServer; + + /** The ServerInterceptors*/ + private FilterInterceptor filterInterceptor; + private TracingInterceptor tracingInterceptor; + private StatusMetricInterceptor statusMetricInterceptor; + + @Inject + public GrpcServer(GJEXConfiguration configuration, + @Named("FilterInterceptor") FilterInterceptor filterInterceptor, + @Named("TracingInterceptor") TracingInterceptor tracingInterceptor, + @Named("StatusMetricInterceptor") StatusMetricInterceptor statusMetricInterceptor) { + if (configuration.getGrpc().getPort() > 0) { + this.port = configuration.getGrpc().getPort(); + info("Creating GrpcServer listening on port : " + port); + } + + if (configuration.getGrpc().getMaxMessageSize() > 0) { + this.maxMessageSize = configuration.getGrpc().getMaxMessageSize(); + info("Creating GrpcServer with maximum message size allowed : " + maxMessageSize); + } + this.grpcServerBuilder = Grpc.newServerBuilderForPort(this.port, InsecureServerCredentials.create()).maxInboundMessageSize(this.maxMessageSize); + + if (configuration.getGrpc().getExecutorThreads() > 0) { + this.grpcServerBuilder.executor( + Executors.newFixedThreadPool( + configuration.getGrpc().getExecutorThreads(), + GrpcUtil.getThreadFactory("grpc-executor-%d", true))); + } + + + this.filterInterceptor = filterInterceptor; + this.tracingInterceptor = tracingInterceptor; + this.statusMetricInterceptor = statusMetricInterceptor; + } + + @Override + public void doStart() throws Exception { + this.grpcServer = this.grpcServerBuilder.addService(ProtoReflectionService.newInstance()).build().start(); + info("GJEX GrpcServer started.Hosting these services : ****** Start *****"); + this.grpcServer.getServices().forEach(serviceDefinition -> info(serviceDefinition.getServiceDescriptor().getName())); + info("GJEX GrpcServer started.Hosting these services : ****** End *****"); + // Not waiting for termination as this blocks main thread preventing any subsequent startup, like the Jetty Dashboard server + // this.grpcServer.awaitTermination(); + + } + + @Override + public void doStop() { + if (this.grpcServer != null) { + this.grpcServer.shutdown(); + } + info("GJEX GrpcServer stopped."); + } + + public void registerFilters(@SuppressWarnings("rawtypes") List grpcFilters, List services, GrpcFilterConfig grpcFilterConfig) { + this.filterInterceptor.registerFilters(grpcFilters, services, grpcFilterConfig); + } + + public void registerTracingSamplers(List samplers, List services) { + this.tracingInterceptor.registerTracingSamplers(samplers, services); + } public void registerResponseMeteredMethods(List services) { this.statusMetricInterceptor.registerMeteredMethods(services); diff --git a/guice/src/main/java/com/flipkart/gjex/grpc/utils/AnnotationUtils.java b/guice/src/main/java/com/flipkart/gjex/grpc/utils/AnnotationUtils.java index 0d9c12a9..2ce23ed5 100644 --- a/guice/src/main/java/com/flipkart/gjex/grpc/utils/AnnotationUtils.java +++ b/guice/src/main/java/com/flipkart/gjex/grpc/utils/AnnotationUtils.java @@ -24,26 +24,26 @@ public class AnnotationUtils { - /** - * Helper method to get annotated methods on a Class. Navigates up the superclass hierarchy to get the methods. This is required when used with DI mechanisms like Guice that - * create a CGLIB proxy sub-type for instances and annotations are not copied to the sub-type. - * Cannot use @Inherited annotation as a workaround because it applies only to Type/Class level annotations and not for Method-level ones. - * @see https://github.com/google/guice/issues/101 - * - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static List> getAnnotatedMethods(Class cls, Class anno) { - List> methods = new ArrayList<>(); - for (Method m : cls.getDeclaredMethods()) { - if (m.getAnnotation(anno) != null) { - methods.add(new Pair(cls,m)); - } - } - if (methods.isEmpty()) { - Class superCls = cls.getSuperclass(); - return (superCls != null) ? getAnnotatedMethods(superCls, anno) : null; - } - return methods; - } + /** + * Helper method to get annotated methods on a Class. Navigates up the superclass hierarchy to get the methods. This is required when used with DI mechanisms like Guice that + * create a CGLIB proxy sub-type for instances and annotations are not copied to the sub-type. + * Cannot use @Inherited annotation as a workaround because it applies only to Type/Class level annotations and not for Method-level ones. + * @see https://github.com/google/guice/issues/101 + * + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static List> getAnnotatedMethods(Class cls, Class anno) { + List> methods = new ArrayList<>(); + for (Method m : cls.getDeclaredMethods()) { + if (m.getAnnotation(anno) != null) { + methods.add(new Pair(cls,m)); + } + } + if (methods.isEmpty()) { + Class superCls = cls.getSuperclass(); + return (superCls != null) ? getAnnotatedMethods(superCls, anno) : null; + } + return methods; + } } diff --git a/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java b/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java index 22bbc6c5..b93858b5 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/GuiceBundle.java @@ -65,172 +65,172 @@ @SuppressWarnings("rawtypes") public class GuiceBundle implements Bundle, Logging { - private List modules; - private Injector baseInjector; - private List services; - private List grpcFilters; - private List healthchecks; - private List tracingSamplers; - private List scheduledJobs; - private List resourceConfigs; - private List httpFilterParamsList; + private List modules; + private Injector baseInjector; + private List services; + private List grpcFilters; + private List healthchecks; + private List tracingSamplers; + private List scheduledJobs; + private List resourceConfigs; + private List httpFilterParamsList; private List javaxFilterParamsList; - private Optional> configurationClass; - private GJEXEnvironmentModule gjexEnvironmentModule; + private Optional> configurationClass; + private GJEXEnvironmentModule gjexEnvironmentModule; - public static class Builder { + public static class Builder { - private List modules = Lists.newArrayList(); - private Optional> configurationClass = Optional.empty(); + private List modules = Lists.newArrayList(); + private Optional> configurationClass = Optional.empty(); - public Builder addModules(Module... moreModules) { - for (Module module : moreModules) { - Preconditions.checkNotNull(module); - modules.add(module); - } - return this; - } + public Builder addModules(Module... moreModules) { + for (Module module : moreModules) { + Preconditions.checkNotNull(module); + modules.add(module); + } + return this; + } - public Builder setConfigClass(Class clazz) { - configurationClass = Optional.ofNullable(clazz); - return this; - } + public Builder setConfigClass(Class clazz) { + configurationClass = Optional.ofNullable(clazz); + return this; + } - public GuiceBundle build() { + public GuiceBundle build() { return new GuiceBundle<>(modules, configurationClass); } - } + } - private GuiceBundle(List modules, Optional> configurationClass) { - Preconditions.checkNotNull(modules); + private GuiceBundle(List modules, Optional> configurationClass) { + Preconditions.checkNotNull(modules); Preconditions.checkArgument(!modules.isEmpty()); this.modules = modules; this.configurationClass = configurationClass; - } - - @SuppressWarnings("unchecked") - @Override - public void initialize(Bootstrap bootstrap) { - // adding config module first - modules.add(new ConfigModule<>(bootstrap)); - if (configurationClass.isPresent()) { - gjexEnvironmentModule = new GJEXEnvironmentModule<>(configurationClass.get()); - } else { - gjexEnvironmentModule = new GJEXEnvironmentModule<>(GJEXConfiguration.class); - } - modules.add(gjexEnvironmentModule); - // add Metrics MetricsInstrumentationModule - modules.add(MetricsInstrumentationModule.builder().withMetricRegistry(bootstrap.getMetricRegistry()).build()); - // add the Validation module - modules.add(new ImplicitValidationModule()); - // add the Api module before Tracing module so that APIs are timed from the start of execution - modules.add(new ApiModule()); - // add the Tracing module before Task module so that even Concurrent tasks can be traced - modules.add(new TracingModule()); - // add the Task module - modules.add(new TaskModule()); - // add the Dashboard module - modules.add(new DashboardModule(bootstrap)); - // add the Grpc Server module - modules.add(new ServerModule()); - baseInjector = Guice.createInjector(this.modules); - } - - @Override - public void run(T configuration, U configMap, Environment environment) { - setEnvironment(configuration, environment); // NOTE - GrpcServer grpcServer = baseInjector.getInstance(GrpcServer.class); - - List bindableServices = new ArrayList<>(); - // Add all Grpc Services to the Grpc Server - bindableServices.addAll(getInstances(baseInjector, BindableService.class)); - - // Add all Grpc Health Check Services to the Grpc Server - bindableServices.addAll(getInstances(baseInjector, HealthGrpc.HealthImplBase.class)); - - grpcServer.registerServices(bindableServices); - - // Add all Grpc Filters to the Grpc Server - grpcFilters = getInstances(baseInjector, GrpcFilter.class); - grpcServer.registerFilters(grpcFilters, bindableServices, configuration.getGrpc().getGrpcFilterConfig()); - - // Add all Grpc Filters to the Grpc Server - tracingSamplers = getInstances(baseInjector, TracingSampler.class); - grpcServer.registerTracingSamplers(tracingSamplers, bindableServices); - - // Register all ResponseMetered methods to publish metrics - grpcServer.registerResponseMeteredMethods(bindableServices); - - ScheduledJobManager scheduledJobManager = baseInjector.getInstance(ScheduledJobManager.class); - // Add all ScheduledJobs to the ScheduleJobManager - scheduledJobs = getInstances(baseInjector, ScheduledJob.class); - scheduledJobManager.registerScheduledJobs(scheduledJobs); - - // Lookup all Service implementations - services = getInstances(baseInjector, Service.class); - // Lookup all HealthCheck implementations - healthchecks = getInstances(baseInjector, HealthCheck.class); - - ApiServer apiServer = baseInjector.getInstance(ApiServer.class); - // Add all custom web resources - resourceConfigs = getInstances(baseInjector, ResourceConfig.class); - apiServer.registerResources(resourceConfigs); - - // Add all custom http filters - httpFilterParamsList = getInstances(baseInjector, HttpFilterParams.class); + } + + @SuppressWarnings("unchecked") + @Override + public void initialize(Bootstrap bootstrap) { + // adding config module first + modules.add(new ConfigModule<>(bootstrap)); + if (configurationClass.isPresent()) { + gjexEnvironmentModule = new GJEXEnvironmentModule<>(configurationClass.get()); + } else { + gjexEnvironmentModule = new GJEXEnvironmentModule<>(GJEXConfiguration.class); + } + modules.add(gjexEnvironmentModule); + // add Metrics MetricsInstrumentationModule + modules.add(MetricsInstrumentationModule.builder().withMetricRegistry(bootstrap.getMetricRegistry()).build()); + // add the Validation module + modules.add(new ImplicitValidationModule()); + // add the Api module before Tracing module so that APIs are timed from the start of execution + modules.add(new ApiModule()); + // add the Tracing module before Task module so that even Concurrent tasks can be traced + modules.add(new TracingModule()); + // add the Task module + modules.add(new TaskModule()); + // add the Dashboard module + modules.add(new DashboardModule(bootstrap)); + // add the Grpc Server module + modules.add(new ServerModule()); + baseInjector = Guice.createInjector(this.modules); + } + + @Override + public void run(T configuration, U configMap, Environment environment) { + setEnvironment(configuration, environment); // NOTE + GrpcServer grpcServer = baseInjector.getInstance(GrpcServer.class); + + List bindableServices = new ArrayList<>(); + // Add all Grpc Services to the Grpc Server + bindableServices.addAll(getInstances(baseInjector, BindableService.class)); + + // Add all Grpc Health Check Services to the Grpc Server + bindableServices.addAll(getInstances(baseInjector, HealthGrpc.HealthImplBase.class)); + + grpcServer.registerServices(bindableServices); + + // Add all Grpc Filters to the Grpc Server + grpcFilters = getInstances(baseInjector, GrpcFilter.class); + grpcServer.registerFilters(grpcFilters, bindableServices, configuration.getGrpc().getGrpcFilterConfig()); + + // Add all Grpc Filters to the Grpc Server + tracingSamplers = getInstances(baseInjector, TracingSampler.class); + grpcServer.registerTracingSamplers(tracingSamplers, bindableServices); + + // Register all ResponseMetered methods to publish metrics + grpcServer.registerResponseMeteredMethods(bindableServices); + + ScheduledJobManager scheduledJobManager = baseInjector.getInstance(ScheduledJobManager.class); + // Add all ScheduledJobs to the ScheduleJobManager + scheduledJobs = getInstances(baseInjector, ScheduledJob.class); + scheduledJobManager.registerScheduledJobs(scheduledJobs); + + // Lookup all Service implementations + services = getInstances(baseInjector, Service.class); + // Lookup all HealthCheck implementations + healthchecks = getInstances(baseInjector, HealthCheck.class); + + ApiServer apiServer = baseInjector.getInstance(ApiServer.class); + // Add all custom web resources + resourceConfigs = getInstances(baseInjector, ResourceConfig.class); + apiServer.registerResources(resourceConfigs); + + // Add all custom http filters + httpFilterParamsList = getInstances(baseInjector, HttpFilterParams.class); javaxFilterParamsList = getInstances(baseInjector, JavaxFilterParams.class); - apiServer.registerFilters(httpFilterParamsList, + apiServer.registerFilters(httpFilterParamsList, javaxFilterParamsList, configuration.getApiService().getHttpFilterConfig()); - } + } - @SuppressWarnings("unchecked") - private void setEnvironment(final T configuration, final Environment environment) { - gjexEnvironmentModule.setEnvironmentData(configuration, environment); - } + @SuppressWarnings("unchecked") + private void setEnvironment(final T configuration, final Environment environment) { + gjexEnvironmentModule.setEnvironmentData(configuration, environment); + } - @Override - public List getServices() { + @Override + public List getServices() { Preconditions.checkState(baseInjector != null, "Service(s) are only available after GuiceBundle.run() is called"); - return this.services; - } + return this.services; + } - @Override - public List getGrpcFilters() { + @Override + public List getGrpcFilters() { Preconditions.checkState(baseInjector != null, "Filter(s) are only available after GuiceBundle.run() is called"); - return this.grpcFilters; - } + return this.grpcFilters; + } - @Override - public List getHealthChecks() { + @Override + public List getHealthChecks() { Preconditions.checkState(baseInjector != null, "HealthCheck(s) are only available after GuiceBundle.run() is called"); - return this.healthchecks; - } + return this.healthchecks; + } - @Override - public List getTracingSamplers() { + @Override + public List getTracingSamplers() { Preconditions.checkState(baseInjector != null, "TracingSampler(s) is only available after GuiceBundle.run() is called"); return this.tracingSamplers; - } + } - @Override - public List getScheduledJobs() { - Preconditions.checkState(baseInjector != null, - "ScheduledJob(s) are only available after GuiceBundle.run() is called"); - return this.scheduledJobs; - } + @Override + public List getScheduledJobs() { + Preconditions.checkState(baseInjector != null, + "ScheduledJob(s) are only available after GuiceBundle.run() is called"); + return this.scheduledJobs; + } - @Override - public List getResourceConfigs() { + @Override + public List getResourceConfigs() { Preconditions.checkState(baseInjector != null, "ResourceConfig(s) is only available after GuiceBundle.run() is called"); return this.resourceConfigs; - } + } - public Injector getInjector() { + public Injector getInjector() { Preconditions.checkState(baseInjector != null, "Injector is only available after GuiceBundle.initialize() is called"); return baseInjector; diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java index 6119b270..91ab3dad 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/ApiModule.java @@ -48,94 +48,94 @@ */ public class ApiModule extends AbstractModule implements Logging { - @Override + @Override protected void configure() { - ApiMethodInterceptor methodInterceptor = new ApiMethodInterceptor(); - requestInjection(methodInterceptor); - bindInterceptor(Matchers.any(), new ApiMethodMatcher(), methodInterceptor); - bind(HealthCheck.class).to(RotationManagementBasedHealthCheck.class); - } + ApiMethodInterceptor methodInterceptor = new ApiMethodInterceptor(); + requestInjection(methodInterceptor); + bindInterceptor(Matchers.any(), new ApiMethodMatcher(), methodInterceptor); + bind(HealthCheck.class).to(RotationManagementBasedHealthCheck.class); + } - @Named("ApiScheduledExecutor") - @Provides - @Singleton - ScheduledExecutorService getScheduledExecutorService(GJEXConfiguration configuration) { - return Executors.newScheduledThreadPool(configuration.getApiService().getScheduledExecutorThreadPoolSize()); - } + @Named("ApiScheduledExecutor") + @Provides + @Singleton + ScheduledExecutorService getScheduledExecutorService(GJEXConfiguration configuration) { + return Executors.newScheduledThreadPool(configuration.getApiService().getScheduledExecutorThreadPoolSize()); + } - class ApiMethodInterceptor implements MethodInterceptor { + class ApiMethodInterceptor implements MethodInterceptor { - @Inject - @Named("GlobalFlattenedConfig") - private Provider globalConfigurationProvider; + @Inject + @Named("GlobalFlattenedConfig") + private Provider globalConfigurationProvider; - @Inject - @Named("ApiScheduledExecutor") - private Provider scheduledExecutorServiceProvider; + @Inject + @Named("ApiScheduledExecutor") + private Provider scheduledExecutorServiceProvider; - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - Context.CancellableContext cancellableContext = null; - Context previous = null; - Api api = invocation.getMethod().getAnnotation(Api.class); - /* - * We explicitly set the deadline only when it is specified on the stub method AND there is none specified by the client. - * Client deadline wins over any server specified deadline - */ - if (api != null) { - String methodInvoked = (invocation.getMethod().getDeclaringClass().getSimpleName() + "." + invocation.getMethod().getName()).toLowerCase(); - // check and warn if Api is used for non BindableService classes - if (!BindableService.class.isAssignableFrom(invocation.getMethod().getDeclaringClass())) { - warn("Api declarations are interpreted only for sub-types of gRPC BindableService. Api declared for : " - + methodInvoked + " will not be interpreted/honored"); - } - int deadline = 0; - if (api.deadlineConfig().length() > 0) { // check if deadline is specified as a config property - deadline = globalConfigurationProvider.get().getInt(api.deadlineConfig()); - } - if (Context.current().getDeadline() == null) { - cancellableContext = Context.current().withDeadlineAfter(deadline, TimeUnit.MILLISECONDS, scheduledExecutorServiceProvider.get()); - previous = cancellableContext.attach(); // attach the CancellableContext and store the previous Context - } else { - info("Not setting API deadline as client has already specified a deadline"); - } - } - Object result = null; - try { - result = invocation.proceed(); - } finally { - if (cancellableContext != null) { - cancellableContext.detach(previous); // Detach the CancellableContext and restore the previous Context - debug("Cancelled a cancellable context"); - cancellableContext.cancel(null); // we cancel the cancellable context with null (no error) - } - } - return result; - } - } + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Context.CancellableContext cancellableContext = null; + Context previous = null; + Api api = invocation.getMethod().getAnnotation(Api.class); + /* + * We explicitly set the deadline only when it is specified on the stub method AND there is none specified by the client. + * Client deadline wins over any server specified deadline + */ + if (api != null) { + String methodInvoked = (invocation.getMethod().getDeclaringClass().getSimpleName() + "." + invocation.getMethod().getName()).toLowerCase(); + // check and warn if Api is used for non BindableService classes + if (!BindableService.class.isAssignableFrom(invocation.getMethod().getDeclaringClass())) { + warn("Api declarations are interpreted only for sub-types of gRPC BindableService. Api declared for : " + + methodInvoked + " will not be interpreted/honored"); + } + int deadline = 0; + if (api.deadlineConfig().length() > 0) { // check if deadline is specified as a config property + deadline = globalConfigurationProvider.get().getInt(api.deadlineConfig()); + } + if (Context.current().getDeadline() == null) { + cancellableContext = Context.current().withDeadlineAfter(deadline, TimeUnit.MILLISECONDS, scheduledExecutorServiceProvider.get()); + previous = cancellableContext.attach(); // attach the CancellableContext and store the previous Context + } else { + info("Not setting API deadline as client has already specified a deadline"); + } + } + Object result = null; + try { + result = invocation.proceed(); + } finally { + if (cancellableContext != null) { + cancellableContext.detach(previous); // Detach the CancellableContext and restore the previous Context + debug("Cancelled a cancellable context"); + cancellableContext.cancel(null); // we cancel the cancellable context with null (no error) + } + } + return result; + } + } - @Named("HttpFilterInterceptor") - @Provides - @Singleton - HttpFilterInterceptor getHttpFilterInterceptor(){ - return new HttpFilterInterceptor(); - } + @Named("HttpFilterInterceptor") + @Provides + @Singleton + HttpFilterInterceptor getHttpFilterInterceptor(){ + return new HttpFilterInterceptor(); + } - /** - * The Matcher that matches methods with the {@link Api} annotation - */ - class ApiMethodMatcher extends AbstractMatcher { - @Override - public boolean matches(final Method method) { - boolean matches = false; - for (Annotation ann : method.getAnnotations()) { - final Class annotationType = ann.annotationType(); - if (Api.class.equals(annotationType)) { - matches = true; - break; - } - } - return matches; - } - } + /** + * The Matcher that matches methods with the {@link Api} annotation + */ + class ApiMethodMatcher extends AbstractMatcher { + @Override + public boolean matches(final Method method) { + boolean matches = false; + for (Annotation ann : method.getAnnotations()) { + final Class annotationType = ann.annotationType(); + if (Api.class.equals(annotationType)) { + matches = true; + break; + } + } + return matches; + } + } } diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/ClientModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/ClientModule.java index e6725851..16b66366 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/ClientModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/ClientModule.java @@ -41,41 +41,41 @@ * @param type of the required Grpc Service's stub eg: GreeterGrpc.GreeterBlockingStub */ public class ClientModule> extends AbstractModule { - private final Class clazz; - private final ChannelConfig channelConfig; + private final Class clazz; + private final ChannelConfig channelConfig; - @Inject @Named("Tracer") - Tracer tracer; + @Inject @Named("Tracer") + Tracer tracer; - public ClientModule(Class clazz, ChannelConfig channelConfig) { - this.clazz = clazz; - this.channelConfig = channelConfig; - } + public ClientModule(Class clazz, ChannelConfig channelConfig) { + this.clazz = clazz; + this.channelConfig = channelConfig; + } - @Override - protected void configure() { - bind(clazz).toProvider(new StubProvider()); - } + @Override + protected void configure() { + bind(clazz).toProvider(new StubProvider()); + } - private class StubProvider implements Provider { - private Channel channel; + private class StubProvider implements Provider { + private Channel channel; - public StubProvider() { - channel = new InstrumentedChannel(channelConfig); - binder().requestInjection(channel); - } + public StubProvider() { + channel = new InstrumentedChannel(channelConfig); + binder().requestInjection(channel); + } - @Override - public T get() { - try { - Constructor constructor = clazz.getDeclaredConstructor(Channel.class); - constructor.setAccessible(true); - return constructor.newInstance(channel).withDeadlineAfter(channelConfig.getDeadlineInMs(), - TimeUnit.MILLISECONDS).withInterceptors(new ClientTracingInterceptor(tracer)); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException| NoSuchMethodException e) { - throw new RuntimeException("Grpc stub class doesn't have a constructor which only takes 'Channel' as parameter", e); - } - } - } + @Override + public T get() { + try { + Constructor constructor = clazz.getDeclaredConstructor(Channel.class); + constructor.setAccessible(true); + return constructor.newInstance(channel).withDeadlineAfter(channelConfig.getDeadlineInMs(), + TimeUnit.MILLISECONDS).withInterceptors(new ClientTracingInterceptor(tracer)); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException| NoSuchMethodException e) { + throw new RuntimeException("Grpc stub class doesn't have a constructor which only takes 'Channel' as parameter", e); + } + } + } } diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/ConfigModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/ConfigModule.java index b5bfac10..f9567e3d 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/ConfigModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/ConfigModule.java @@ -48,7 +48,7 @@ public ConfigModule(Bootstrap bootstrap) { } @SuppressWarnings({"unchecked" }) - @Override + @Override protected void configure() { // bind config map instance bind(Map.class).annotatedWith(Names.named("GlobalMapConfig")).toInstance(configMap); @@ -58,12 +58,12 @@ protected void configure() { flatten(flattenedMap, null, configMap); /** - * Binds individual flattened key-value properties in the configuration yml - * file. So one can directly inject something like this: - * - * @Named("Hibernate.hibernate.jdbcDriver") String jdbcDriver OR - * @Named("Dashboard.service.port") int port - */ + * Binds individual flattened key-value properties in the configuration yml + * file. So one can directly inject something like this: + * + * @Named("Hibernate.hibernate.jdbcDriver") String jdbcDriver OR + * @Named("Dashboard.service.port") int port + */ for (Map.Entry entry: flattenedMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); @@ -87,8 +87,8 @@ private void flatten(Map result, String prefix, Map map) { } /** - * Returns the Global config of all flattened out properties loaded by instance of this class. - */ + * Returns the Global config of all flattened out properties loaded by instance of this class. + */ @Named("GlobalFlattenedConfig") @Provides @Singleton diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/DashboardModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/DashboardModule.java index 3e2766ad..21d172b5 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/DashboardModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/DashboardModule.java @@ -61,210 +61,210 @@ @SuppressWarnings("rawtypes") public class DashboardModule extends AbstractModule implements Logging { - private final Bootstrap bootstrap; + private final Bootstrap bootstrap; - public DashboardModule(Bootstrap bootstrap) { - this.bootstrap = bootstrap; - } + public DashboardModule(Bootstrap bootstrap) { + this.bootstrap = bootstrap; + } - @Override - protected void configure() { - // do nothing - } + @Override + protected void configure() { + // do nothing + } - /** - * Creates the Jetty server instance for the admin Dashboard and configures it with the @Named("DashboardContext"). - * - * @return Jetty Server instance - */ - @Named("DashboardJettyServer") - @Provides - @Singleton - Server getDashboardJettyServer(@Named("Dashboard.service.port") int port, - @Named("DashboardResourceConfig")ResourceConfig resourceConfig, - @Named("DashboardHealthCheckResourceConfig")ResourceConfig dashboardHealthCheckResourceConfig, - @Named("Dashboard.service.acceptors") int acceptorThreads, - @Named("Dashboard.service.selectors") int selectorThreads, - @Named("Dashboard.service.workers") int maxWorkerThreads, - @Named("JSONMarshallingProvider")JacksonJaxbJsonProvider provider) { - resourceConfig.register(provider); - QueuedThreadPool threadPool = new QueuedThreadPool(); - threadPool.setMaxThreads(maxWorkerThreads); - Server server = new Server(threadPool); - ServerConnector http = new ServerConnector(server, acceptorThreads, selectorThreads); - http.setPort(port); - server.addConnector(http); + /** + * Creates the Jetty server instance for the admin Dashboard and configures it with the @Named("DashboardContext"). + * + * @return Jetty Server instance + */ + @Named("DashboardJettyServer") + @Provides + @Singleton + Server getDashboardJettyServer(@Named("Dashboard.service.port") int port, + @Named("DashboardResourceConfig")ResourceConfig resourceConfig, + @Named("DashboardHealthCheckResourceConfig")ResourceConfig dashboardHealthCheckResourceConfig, + @Named("Dashboard.service.acceptors") int acceptorThreads, + @Named("Dashboard.service.selectors") int selectorThreads, + @Named("Dashboard.service.workers") int maxWorkerThreads, + @Named("JSONMarshallingProvider")JacksonJaxbJsonProvider provider) { + resourceConfig.register(provider); + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setMaxThreads(maxWorkerThreads); + Server server = new Server(threadPool); + ServerConnector http = new ServerConnector(server, acceptorThreads, selectorThreads); + http.setPort(port); + server.addConnector(http); - /** Initialize the Context and Servlet for serving static content */ - URL webRootLocation = this.getClass().getResource("/webroot/pages/dashboard.ftl"); - if (webRootLocation == null) { - warn("Webroot location not found! Unable to find root location for Dashboard."); - } - ServletContextHandler context = new ServletContextHandler(); - try { - URI webRootUri = URI - .create(webRootLocation.toURI().toASCIIString().replaceFirst("/pages/dashboard.ftl$", "/")); - context.setContextPath("/"); - context.setBaseResource(Resource.newResource(webRootUri)); - context.addServlet(DefaultServlet.class, "/"); - } catch (Exception e) { - error("Unable to set resource base for Dashboard.", e); - } - context.getMimeTypes().addMimeMapping("txt", "text/plain;charset=utf-8"); - server.setHandler(context); + /** Initialize the Context and Servlet for serving static content */ + URL webRootLocation = this.getClass().getResource("/webroot/pages/dashboard.ftl"); + if (webRootLocation == null) { + warn("Webroot location not found! Unable to find root location for Dashboard."); + } + ServletContextHandler context = new ServletContextHandler(); + try { + URI webRootUri = URI + .create(webRootLocation.toURI().toASCIIString().replaceFirst("/pages/dashboard.ftl$", "/")); + context.setContextPath("/"); + context.setBaseResource(Resource.newResource(webRootUri)); + context.addServlet(DefaultServlet.class, "/"); + } catch (Exception e) { + error("Unable to set resource base for Dashboard.", e); + } + context.getMimeTypes().addMimeMapping("txt", "text/plain;charset=utf-8"); + server.setHandler(context); - context.setAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME, this.bootstrap.getHealthCheckRegistry()); - /** Add the Servlet for serving the HealthCheck resource */ - context.addServlet(new ServletHolder(new ServletContainer(dashboardHealthCheckResourceConfig)), "/healthcheck"); + context.setAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME, this.bootstrap.getHealthCheckRegistry()); + /** Add the Servlet for serving the HealthCheck resource */ + context.addServlet(new ServletHolder(new ServletContainer(dashboardHealthCheckResourceConfig)), "/healthcheck"); - /** Add the Servlet for serving the Dashboard resource */ - context.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/admin/*"); + /** Add the Servlet for serving the Dashboard resource */ + context.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/admin/*"); - /** Add the Hystrix metrics stream servlets */ - context.addServlet(HystrixMetricsStreamServlet.class, "/stream/hystrix.stream.command.local"); - context.addServlet(HystrixMetricsStreamServlet.class, "/stream/hystrix.stream.global"); - context.addServlet(HystrixMetricsStreamServlet.class, "/stream/hystrix.stream.tp.local"); + /** Add the Hystrix metrics stream servlets */ + context.addServlet(HystrixMetricsStreamServlet.class, "/stream/hystrix.stream.command.local"); + context.addServlet(HystrixMetricsStreamServlet.class, "/stream/hystrix.stream.global"); + context.addServlet(HystrixMetricsStreamServlet.class, "/stream/hystrix.stream.tp.local"); - /** Add the promethus servlet */ - context.addServlet(PrometheusMetricsServlet.class,"/metrics"); + /** Add the promethus servlet */ + context.addServlet(PrometheusMetricsServlet.class,"/metrics"); - /** Add the Metrics instrumentation */ - final InstrumentedHandler handler = new InstrumentedHandler(this.bootstrap.getMetricRegistry()); - handler.setName("gjex-dashboard"); - handler.setHandler(context); - server.setHandler(handler); + /** Add the Metrics instrumentation */ + final InstrumentedHandler handler = new InstrumentedHandler(this.bootstrap.getMetricRegistry()); + handler.setName("gjex-dashboard"); + handler.setHandler(context); + server.setHandler(handler); - server.setStopAtShutdown(true); - return server; - } + server.setStopAtShutdown(true); + return server; + } - /** - * Creates the Jetty server instance for the GJEX API endpoint. - * @return Jetty Server instance - */ - @Named("APIJettyServer") - @Provides - @Singleton - Server getAPIJettyServer(@Named("APIVanillaJettyServer")Server server, - @Named("ApiServletContext") ServletContextHandler context, - @Named("HealthCheckResourceConfig") ResourceConfig healthCheckResourceConfig, - @Named("RotationManagementResourceConfig") ResourceConfig rotationManagementResourceConfig, - @Named("TracingResourceConfig")ResourceConfig tracingResourceConfig, - @Named("TracingSamplerHolder")TracingSamplerHolder tracingSamplerHolder, - @Named("JSONMarshallingProvider")JacksonJaxbJsonProvider provider) throws URISyntaxException, UnknownHostException { - healthCheckResourceConfig.register(provider); - ServletHolder healthCheckServlet = - new ServletHolder(new ServletContainer(healthCheckResourceConfig)); - context.addServlet(healthCheckServlet, "/healthcheck"); // registering Health Check servlet under the /healthcheck path + /** + * Creates the Jetty server instance for the GJEX API endpoint. + * @return Jetty Server instance + */ + @Named("APIJettyServer") + @Provides + @Singleton + Server getAPIJettyServer(@Named("APIVanillaJettyServer")Server server, + @Named("ApiServletContext") ServletContextHandler context, + @Named("HealthCheckResourceConfig") ResourceConfig healthCheckResourceConfig, + @Named("RotationManagementResourceConfig") ResourceConfig rotationManagementResourceConfig, + @Named("TracingResourceConfig")ResourceConfig tracingResourceConfig, + @Named("TracingSamplerHolder")TracingSamplerHolder tracingSamplerHolder, + @Named("JSONMarshallingProvider")JacksonJaxbJsonProvider provider) throws URISyntaxException, UnknownHostException { + healthCheckResourceConfig.register(provider); + ServletHolder healthCheckServlet = + new ServletHolder(new ServletContainer(healthCheckResourceConfig)); + context.addServlet(healthCheckServlet, "/healthcheck"); // registering Health Check servlet under the /healthcheck path - rotationManagementResourceConfig.register(provider); - ServletHolder rotationManagementServlet = - new ServletHolder(new ServletContainer(rotationManagementResourceConfig)); - context.addServlet(rotationManagementServlet, "/rotation/*"); // registering Rotation - // Management servlet under the /rotation path + rotationManagementResourceConfig.register(provider); + ServletHolder rotationManagementServlet = + new ServletHolder(new ServletContainer(rotationManagementResourceConfig)); + context.addServlet(rotationManagementServlet, "/rotation/*"); // registering Rotation + // Management servlet under the /rotation path - tracingResourceConfig.register(provider); - ServletHolder tracingServlet = new ServletHolder(new ServletContainer(tracingResourceConfig)); - context.addServlet(tracingServlet, "/tracingconfig"); // registering Tracing config servlet under the /tracingconfig path + tracingResourceConfig.register(provider); + ServletHolder tracingServlet = new ServletHolder(new ServletContainer(tracingResourceConfig)); + context.addServlet(tracingServlet, "/tracingconfig"); // registering Tracing config servlet under the /tracingconfig path - context.setAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME, this.bootstrap.getHealthCheckRegistry()); - context.setAttribute(TracingSamplerHolder.TRACING_SAMPLER_HOLDER_NAME, tracingSamplerHolder); + context.setAttribute(HealthCheckRegistry.HEALTHCHECK_REGISTRY_NAME, this.bootstrap.getHealthCheckRegistry()); + context.setAttribute(TracingSamplerHolder.TRACING_SAMPLER_HOLDER_NAME, tracingSamplerHolder); - final InstrumentedHandler handler = new InstrumentedHandler(this.bootstrap.getMetricRegistry()); - handler.setName("gjex-api"); - handler.setHandler(context); - server.setHandler(handler); + final InstrumentedHandler handler = new InstrumentedHandler(this.bootstrap.getMetricRegistry()); + handler.setName("gjex-api"); + handler.setHandler(context); + server.setHandler(handler); - return server; - } + return server; + } - /** - * Creates the vanilla Jetty server instance for the GJEX API endpoint. - * @return Jetty Server instance - */ - @Named("APIVanillaJettyServer") - @Provides - @Singleton - Server getAPIVanillaJettyServer(@Named("Api.service.port") int port, - @Named("Api.service.acceptors") int acceptorThreads, - @Named("Api.service.selectors") int selectorThreads, - @Named("Api.service.workers") int maxWorkerThreads) throws URISyntaxException, UnknownHostException { - QueuedThreadPool threadPool = new QueuedThreadPool(); - threadPool.setMaxThreads(maxWorkerThreads); - Server server = new Server(threadPool); - ServerConnector http = new ServerConnector(server, acceptorThreads, selectorThreads); - http.setPort(port); - server.addConnector(http); - server.setStopAtShutdown(true); - return server; - } + /** + * Creates the vanilla Jetty server instance for the GJEX API endpoint. + * @return Jetty Server instance + */ + @Named("APIVanillaJettyServer") + @Provides + @Singleton + Server getAPIVanillaJettyServer(@Named("Api.service.port") int port, + @Named("Api.service.acceptors") int acceptorThreads, + @Named("Api.service.selectors") int selectorThreads, + @Named("Api.service.workers") int maxWorkerThreads) throws URISyntaxException, UnknownHostException { + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setMaxThreads(maxWorkerThreads); + Server server = new Server(threadPool); + ServerConnector http = new ServerConnector(server, acceptorThreads, selectorThreads); + http.setPort(port); + server.addConnector(http); + server.setStopAtShutdown(true); + return server; + } - @Named("JSONMarshallingProvider") - @Singleton - @Provides - JacksonJaxbJsonProvider getJSONMarshallingProvider(ObjectMapper objectMapper) { - JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(); - provider.setMapper(objectMapper); - return provider; - } + @Named("JSONMarshallingProvider") + @Singleton + @Provides + JacksonJaxbJsonProvider getJSONMarshallingProvider(ObjectMapper objectMapper) { + JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(); + provider.setMapper(objectMapper); + return provider; + } - @Named("ApiServletContext") - @Singleton - @Provides - public ServletContextHandler getApiServletContext(@Named("APIVanillaJettyServer")Server server) { - return new ServletContextHandler(server, "/"); - } + @Named("ApiServletContext") + @Singleton + @Provides + public ServletContextHandler getApiServletContext(@Named("APIVanillaJettyServer")Server server) { + return new ServletContextHandler(server, "/"); + } - @Named("HealthCheckResourceConfig") - @Singleton - @Provides - ResourceConfig getAPIResourceConfig(HealthCheckResource healthCheckResource) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.register(healthCheckResource); - resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); - return resourceConfig; - } + @Named("HealthCheckResourceConfig") + @Singleton + @Provides + ResourceConfig getAPIResourceConfig(HealthCheckResource healthCheckResource) { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(healthCheckResource); + resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); + return resourceConfig; + } - @Named("DashboardHealthCheckResourceConfig") - @Singleton - @Provides - ResourceConfig getAPIResourceConfig(DashboardHealthCheckResource dashboardHealthCheckResource) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.register(dashboardHealthCheckResource); - resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); - return resourceConfig; - } + @Named("DashboardHealthCheckResourceConfig") + @Singleton + @Provides + ResourceConfig getAPIResourceConfig(DashboardHealthCheckResource dashboardHealthCheckResource) { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(dashboardHealthCheckResource); + resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); + return resourceConfig; + } - @Named("RotationManagementResourceConfig") - @Singleton - @Provides - ResourceConfig getRotationManagementResourceConfig(RotationManagementResource rotationManagementResource) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.register(rotationManagementResource); - resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); - return resourceConfig; - } + @Named("RotationManagementResourceConfig") + @Singleton + @Provides + ResourceConfig getRotationManagementResourceConfig(RotationManagementResource rotationManagementResource) { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(rotationManagementResource); + resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); + return resourceConfig; + } - @Named("TracingResourceConfig") - @Singleton - @Provides - ResourceConfig getTracingResourceConfig(TracingResource tracingResource) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.register(tracingResource); - resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); - return resourceConfig; - } + @Named("TracingResourceConfig") + @Singleton + @Provides + ResourceConfig getTracingResourceConfig(TracingResource tracingResource) { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(tracingResource); + resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); + return resourceConfig; + } - @Named("DashboardResourceConfig") - @Singleton - @Provides - ResourceConfig getDashboardResourceConfig(DashboardResource dashboardResource) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.register(dashboardResource); - resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); - resourceConfig.property(FreemarkerMvcFeature.TEMPLATES_BASE_PATH, "webroot/pages"); - resourceConfig.register(FreemarkerMvcFeature.class); - return resourceConfig; - } + @Named("DashboardResourceConfig") + @Singleton + @Provides + ResourceConfig getDashboardResourceConfig(DashboardResource dashboardResource) { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(dashboardResource); + resourceConfig.setApplicationName(Constants.GJEX_CORE_APPLICATION); + resourceConfig.property(FreemarkerMvcFeature.TEMPLATES_BASE_PATH, "webroot/pages"); + resourceConfig.register(FreemarkerMvcFeature.class); + return resourceConfig; + } } diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/GJEXEnvironmentModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/GJEXEnvironmentModule.java index ba0c0fe4..89d96594 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/GJEXEnvironmentModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/GJEXEnvironmentModule.java @@ -57,10 +57,10 @@ protected void configure() { } /** - * This method is called from GuiceBundle.run() method to set actual generated Configuration instance + * This method is called from GuiceBundle.run() method to set actual generated Configuration instance (constructed using either yml file or other sources like config-service). This instance will be returned when we try to inject GJEXConfiguration or its subclass class - */ + */ public void setEnvironmentData(T configuration, Environment environment) { this.configuration = configuration; this.environment = environment; diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/ServerModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/ServerModule.java index 3085e24e..ef001a58 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/ServerModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/ServerModule.java @@ -38,15 +38,15 @@ */ public class ServerModule extends AbstractModule { - @Override + @Override protected void configure() { - bind(FilterInterceptor.class).annotatedWith(Names.named("FilterInterceptor")).to(FilterInterceptor.class); - bind(TracingInterceptor.class).annotatedWith(Names.named("TracingInterceptor")).to(TracingInterceptor.class); - bind(StatusMetricInterceptor.class).annotatedWith(Names.named("StatusMetricInterceptor")).to(StatusMetricInterceptor.class); - bind(Service.class).annotatedWith(Names.named("GrpcServer")).to(GrpcServer.class); - bind(HealthGrpc.HealthImplBase.class).annotatedWith(Names.named("GrpcHealthCheckService")).to(GrpcHealthCheckService.class); - bind(Service.class).annotatedWith(Names.named("DashboardServer")).to(DashboardServer.class); - bind(Service.class).annotatedWith(Names.named("APIServer")).to(ApiServer.class); - bind(Service.class).annotatedWith(Names.named("ScheduleJobManager")).to(ScheduledJobManager.class); - } + bind(FilterInterceptor.class).annotatedWith(Names.named("FilterInterceptor")).to(FilterInterceptor.class); + bind(TracingInterceptor.class).annotatedWith(Names.named("TracingInterceptor")).to(TracingInterceptor.class); + bind(StatusMetricInterceptor.class).annotatedWith(Names.named("StatusMetricInterceptor")).to(StatusMetricInterceptor.class); + bind(Service.class).annotatedWith(Names.named("GrpcServer")).to(GrpcServer.class); + bind(HealthGrpc.HealthImplBase.class).annotatedWith(Names.named("GrpcHealthCheckService")).to(GrpcHealthCheckService.class); + bind(Service.class).annotatedWith(Names.named("DashboardServer")).to(DashboardServer.class); + bind(Service.class).annotatedWith(Names.named("APIServer")).to(ApiServer.class); + bind(Service.class).annotatedWith(Names.named("ScheduleJobManager")).to(ScheduledJobManager.class); + } } diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/TaskModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/TaskModule.java index 59b3e492..3de222a7 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/TaskModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/TaskModule.java @@ -39,58 +39,58 @@ */ public class TaskModule extends AbstractModule implements Logging { - @Override + @Override protected void configure() { - TaskMethodInterceptor methodInterceptor = new TaskMethodInterceptor(); - requestInjection(methodInterceptor); - bindInterceptor(Matchers.any(), new TaskMethodMatcher(), methodInterceptor); - } + TaskMethodInterceptor methodInterceptor = new TaskMethodInterceptor(); + requestInjection(methodInterceptor); + bindInterceptor(Matchers.any(), new TaskMethodMatcher(), methodInterceptor); + } - class TaskMethodInterceptor implements MethodInterceptor { + class TaskMethodInterceptor implements MethodInterceptor { - @Inject - @Named("GlobalFlattenedConfig") - private Provider globalConfigurationProvider; + @Inject + @Named("GlobalFlattenedConfig") + private Provider globalConfigurationProvider; - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - ConcurrentTask task = invocation.getMethod().getAnnotation(ConcurrentTask.class); - Configuration globalConfig = globalConfigurationProvider.get(); - int timeout = 0; - if (task.timeoutConfig().length() > 0) { // check if timeout is specified as a config property - timeout = globalConfig.getInt(task.timeoutConfig()); - } - if (task.timeout() > 0) { // we take the method level annotation value as the final override - timeout = task.timeout(); - } - int concurrency = 0; - if (task.concurrencyConfig().length() > 0) { // check if concurrency is specified as a config property - concurrency = globalConfig.getInt(task.concurrencyConfig()); - } - if (task.concurrency() > 0) { // we take the method level annotation value as the final override - concurrency = task.concurrency(); - } - return new FutureDecorator(new TaskExecutor(invocation, - invocation.getMethod().getDeclaringClass().getSimpleName(), - invocation.getMethod().getName(), concurrency, timeout, task.withRequestHedging()),task.completion()) ; // we return the FutureDecorator and not wait for its completion. This enables responses to be composed in a reactive manner - } - } + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + ConcurrentTask task = invocation.getMethod().getAnnotation(ConcurrentTask.class); + Configuration globalConfig = globalConfigurationProvider.get(); + int timeout = 0; + if (task.timeoutConfig().length() > 0) { // check if timeout is specified as a config property + timeout = globalConfig.getInt(task.timeoutConfig()); + } + if (task.timeout() > 0) { // we take the method level annotation value as the final override + timeout = task.timeout(); + } + int concurrency = 0; + if (task.concurrencyConfig().length() > 0) { // check if concurrency is specified as a config property + concurrency = globalConfig.getInt(task.concurrencyConfig()); + } + if (task.concurrency() > 0) { // we take the method level annotation value as the final override + concurrency = task.concurrency(); + } + return new FutureDecorator(new TaskExecutor(invocation, + invocation.getMethod().getDeclaringClass().getSimpleName(), + invocation.getMethod().getName(), concurrency, timeout, task.withRequestHedging()),task.completion()) ; // we return the FutureDecorator and not wait for its completion. This enables responses to be composed in a reactive manner + } + } - /** - * The Matcher that matches methods with the {@link ConcurrentTask} annotation - */ - class TaskMethodMatcher extends AbstractMatcher { - @Override - public boolean matches(final Method method) { - boolean matches = false; - for (Annotation ann : method.getAnnotations()) { - final Class annotationType = ann.annotationType(); - if (ConcurrentTask.class.equals(annotationType)) { - matches = true; - break; - } - } - return matches; - } - } + /** + * The Matcher that matches methods with the {@link ConcurrentTask} annotation + */ + class TaskMethodMatcher extends AbstractMatcher { + @Override + public boolean matches(final Method method) { + boolean matches = false; + for (Annotation ann : method.getAnnotations()) { + final Class annotationType = ann.annotationType(); + if (ConcurrentTask.class.equals(annotationType)) { + matches = true; + break; + } + } + return matches; + } + } } diff --git a/guice/src/main/java/com/flipkart/gjex/guice/module/TracingModule.java b/guice/src/main/java/com/flipkart/gjex/guice/module/TracingModule.java index 0adc0ff0..cfa9ccaa 100644 --- a/guice/src/main/java/com/flipkart/gjex/guice/module/TracingModule.java +++ b/guice/src/main/java/com/flipkart/gjex/guice/module/TracingModule.java @@ -63,173 +63,173 @@ */ public class TracingModule extends AbstractModule implements Logging { - @Override + @Override protected void configure() { - TracedMethodInterceptor methodInterceptor = new TracedMethodInterceptor(); - requestInjection(methodInterceptor); - bindInterceptor(Matchers.any(), new TracedMethodMatcher(), methodInterceptor); - bind(TracingSamplerHolder.class).annotatedWith(Names.named("TracingSamplerHolder")).to(TracingSamplerHolder.class); - } - - /** - * Creates an OpenTracing Tracer over the Openzipkin-Brave tracer - */ - @Named("Tracer") - @Provides - @Singleton - Tracer getTracer(GJEXConfiguration configuration) { - String endPoint = configuration.getTracing().getCollectorEndpoint(); - AsyncReporter spanReporter = AsyncReporter.create(OkHttpSender.create(endPoint)); - Tracing tracing = Tracing.newBuilder() - .localServiceName("GJEX") - .spanReporter(spanReporter) - .build(); - return BraveTracer.create(tracing); - } - - /** - * The Tracing method interceptor - */ - class TracedMethodInterceptor implements MethodInterceptor { - - /** The OpenTracing Tracer instance*/ - @Inject - @Named("Tracer") - private Provider tracerProvider; - - /** - * Starts a Trace(implicitly) or adds a Span for every method annotated with {@link Traced}. Nesting of spans is implicit - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - Scope parentScope = null; - Scope scope = null; - /* - * Initializing method invocation span as null means the current active span may get unset if there is no parent active span or the Tracing sampler returns - * negative for sampling the request - */ - io.opentracing.Span methodInvocationSpan = null; - Callable methodCallable = new MethodCallable(invocation); - if (GJEXContext.activeSpan() != null) { - String methodInvoked = (invocation.getMethod().getDeclaringClass().getSimpleName() + "." + invocation.getMethod().getName()).toLowerCase(); - // check and warn if TracingSampler is used for non BindableService classes - if (!BindableService.class.isAssignableFrom(invocation.getMethod().getDeclaringClass()) && invocation.getMethod().getAnnotation(Traced.class).withTracingSampler() != TracingSampler.class) { - warn("TracingSampler declarations are interpreted only for sub-types of gRPC BindableService. TracingSampler declared for : " - + methodInvoked + " will not be interpreted/honored"); - } - TracingSampler tracingSampler = GJEXContext.activeTracingSampler(); - tracingSampler.initializeSamplerFor(methodInvoked, invocation.getMethod().getAnnotation(Traced.class).withSamplingRate()); - Tracer tracer = tracerProvider.get(); - if (tracingSampler.isSampled(methodInvoked)) { - /* - * We check and activate the parent span - cases where the parent span has been defined (say in the gRPC ServerInterceptor like TracingInterceptor) - * but not activated because it has to be sampled here. - */ - if (tracer.scopeManager().active() == null || (tracer.scopeManager().active().span() != GJEXContext.activeSpan())) { - parentScope = tracer.scopeManager().activate(GJEXContext.activeSpan(), true); - } - methodInvocationSpan = tracer.buildSpan(methodInvoked) - .asChildOf(GJEXContext.activeSpan()) - .start(); - scope = tracer.scopeManager().activate(methodInvocationSpan, true); - } - // Set the Method invocation Span as the current span - may be null too and this means subsequent methods will not get traced - methodCallable = Context.current().withValue(GJEXContext.getKeyActiveSpan(), methodInvocationSpan).wrap(methodCallable); - } - Object result = null; - try { - result = methodCallable.call(); - if (result != null && FutureDecorator.class.isAssignableFrom(result.getClass())) { - ((FutureDecorator)result).whenComplete(new AsyncScopeCloserConsumer(scope, parentScope)); // scopes will be closed when the callback executes - return result; - } - } catch(Exception ex) { // we want to log errors to the trace only once - TaskException tex = null; - if (TaskException.class.isAssignableFrom(ex.getClass())) { - tex = (TaskException)ex; - if (tex.isTraced()) { - logErrorToSpan(methodInvocationSpan, ex); - tex.setTraced(false); - } - } else { - logErrorToSpan(methodInvocationSpan, ex); - tex = new TaskException(ex,false); - } - closeScopes(scope, parentScope); // close any open scopes - throw tex; - } - closeScopes(scope, parentScope); - return result; - } - } - - /** Convenience class to extract Scope closing in {@link FutureDecorator#whenComplete(BiConsumer)}*/ - class AsyncScopeCloserConsumer implements BiConsumer { - Scope scope; - Scope parentScope; - AsyncScopeCloserConsumer(Scope scope, Scope parentScope) { - this.scope = scope; - this.parentScope = parentScope; - } - @Override - public void accept(T t, Throwable u) { - closeScopes(scope, parentScope); - } - } - - /** - * The Matcher that matches methods with the {@link Traced} annotation - */ - class TracedMethodMatcher extends AbstractMatcher { - @Override - public boolean matches(final Method method) { - boolean matches = false; - for (Annotation ann : method.getAnnotations()) { - final Class annotationType = ann.annotationType(); - if (Traced.class.equals(annotationType)) { - matches = true; - break; - } - } - return matches; - } - } - - /** Wraps a MethodInvocation as a Callable for use with gRPC Context*/ - class MethodCallable implements Callable { - MethodInvocation invocation ; - MethodCallable(MethodInvocation invocation) { - this.invocation = invocation; - } - public Object call() throws Exception { - try { - return this.invocation.proceed(); - } catch (Throwable e) { - if (Exception.class.isAssignableFrom(e.getClass())) { - throw (Exception)e; - } - throw new RuntimeException(e); - } - } - } - - /** Helper method to close Scope instances*/ - private void closeScopes(Scope scope, Scope parentScope) { - if (scope != null) { - scope.close(); - } - if (parentScope != null && parentScope.span() == GJEXContext.activeRootSpan()) { // close the parent span only if it is the root span - parentScope.close(); - } - } - - /** Helper to log error to the traced Span*/ - private void logErrorToSpan(io.opentracing.Span methodInvocationSpan, Exception ex) { - if (methodInvocationSpan != null) { - Tags.ERROR.set(methodInvocationSpan, true); - methodInvocationSpan.log(ImmutableMap.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage())); - } - } + TracedMethodInterceptor methodInterceptor = new TracedMethodInterceptor(); + requestInjection(methodInterceptor); + bindInterceptor(Matchers.any(), new TracedMethodMatcher(), methodInterceptor); + bind(TracingSamplerHolder.class).annotatedWith(Names.named("TracingSamplerHolder")).to(TracingSamplerHolder.class); + } + + /** + * Creates an OpenTracing Tracer over the Openzipkin-Brave tracer + */ + @Named("Tracer") + @Provides + @Singleton + Tracer getTracer(GJEXConfiguration configuration) { + String endPoint = configuration.getTracing().getCollectorEndpoint(); + AsyncReporter spanReporter = AsyncReporter.create(OkHttpSender.create(endPoint)); + Tracing tracing = Tracing.newBuilder() + .localServiceName("GJEX") + .spanReporter(spanReporter) + .build(); + return BraveTracer.create(tracing); + } + + /** + * The Tracing method interceptor + */ + class TracedMethodInterceptor implements MethodInterceptor { + + /** The OpenTracing Tracer instance*/ + @Inject + @Named("Tracer") + private Provider tracerProvider; + + /** + * Starts a Trace(implicitly) or adds a Span for every method annotated with {@link Traced}. Nesting of spans is implicit + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Scope parentScope = null; + Scope scope = null; + /* + * Initializing method invocation span as null means the current active span may get unset if there is no parent active span or the Tracing sampler returns + * negative for sampling the request + */ + io.opentracing.Span methodInvocationSpan = null; + Callable methodCallable = new MethodCallable(invocation); + if (GJEXContext.activeSpan() != null) { + String methodInvoked = (invocation.getMethod().getDeclaringClass().getSimpleName() + "." + invocation.getMethod().getName()).toLowerCase(); + // check and warn if TracingSampler is used for non BindableService classes + if (!BindableService.class.isAssignableFrom(invocation.getMethod().getDeclaringClass()) && invocation.getMethod().getAnnotation(Traced.class).withTracingSampler() != TracingSampler.class) { + warn("TracingSampler declarations are interpreted only for sub-types of gRPC BindableService. TracingSampler declared for : " + + methodInvoked + " will not be interpreted/honored"); + } + TracingSampler tracingSampler = GJEXContext.activeTracingSampler(); + tracingSampler.initializeSamplerFor(methodInvoked, invocation.getMethod().getAnnotation(Traced.class).withSamplingRate()); + Tracer tracer = tracerProvider.get(); + if (tracingSampler.isSampled(methodInvoked)) { + /* + * We check and activate the parent span - cases where the parent span has been defined (say in the gRPC ServerInterceptor like TracingInterceptor) + * but not activated because it has to be sampled here. + */ + if (tracer.scopeManager().active() == null || (tracer.scopeManager().active().span() != GJEXContext.activeSpan())) { + parentScope = tracer.scopeManager().activate(GJEXContext.activeSpan(), true); + } + methodInvocationSpan = tracer.buildSpan(methodInvoked) + .asChildOf(GJEXContext.activeSpan()) + .start(); + scope = tracer.scopeManager().activate(methodInvocationSpan, true); + } + // Set the Method invocation Span as the current span - may be null too and this means subsequent methods will not get traced + methodCallable = Context.current().withValue(GJEXContext.getKeyActiveSpan(), methodInvocationSpan).wrap(methodCallable); + } + Object result = null; + try { + result = methodCallable.call(); + if (result != null && FutureDecorator.class.isAssignableFrom(result.getClass())) { + ((FutureDecorator)result).whenComplete(new AsyncScopeCloserConsumer(scope, parentScope)); // scopes will be closed when the callback executes + return result; + } + } catch(Exception ex) { // we want to log errors to the trace only once + TaskException tex = null; + if (TaskException.class.isAssignableFrom(ex.getClass())) { + tex = (TaskException)ex; + if (tex.isTraced()) { + logErrorToSpan(methodInvocationSpan, ex); + tex.setTraced(false); + } + } else { + logErrorToSpan(methodInvocationSpan, ex); + tex = new TaskException(ex,false); + } + closeScopes(scope, parentScope); // close any open scopes + throw tex; + } + closeScopes(scope, parentScope); + return result; + } + } + + /** Convenience class to extract Scope closing in {@link FutureDecorator#whenComplete(BiConsumer)}*/ + class AsyncScopeCloserConsumer implements BiConsumer { + Scope scope; + Scope parentScope; + AsyncScopeCloserConsumer(Scope scope, Scope parentScope) { + this.scope = scope; + this.parentScope = parentScope; + } + @Override + public void accept(T t, Throwable u) { + closeScopes(scope, parentScope); + } + } + + /** + * The Matcher that matches methods with the {@link Traced} annotation + */ + class TracedMethodMatcher extends AbstractMatcher { + @Override + public boolean matches(final Method method) { + boolean matches = false; + for (Annotation ann : method.getAnnotations()) { + final Class annotationType = ann.annotationType(); + if (Traced.class.equals(annotationType)) { + matches = true; + break; + } + } + return matches; + } + } + + /** Wraps a MethodInvocation as a Callable for use with gRPC Context*/ + class MethodCallable implements Callable { + MethodInvocation invocation ; + MethodCallable(MethodInvocation invocation) { + this.invocation = invocation; + } + public Object call() throws Exception { + try { + return this.invocation.proceed(); + } catch (Throwable e) { + if (Exception.class.isAssignableFrom(e.getClass())) { + throw (Exception)e; + } + throw new RuntimeException(e); + } + } + } + + /** Helper method to close Scope instances*/ + private void closeScopes(Scope scope, Scope parentScope) { + if (scope != null) { + scope.close(); + } + if (parentScope != null && parentScope.span() == GJEXContext.activeRootSpan()) { // close the parent span only if it is the root span + parentScope.close(); + } + } + + /** Helper to log error to the traced Span*/ + private void logErrorToSpan(io.opentracing.Span methodInvocationSpan, Exception ex) { + if (methodInvocationSpan != null) { + Tags.ERROR.set(methodInvocationSpan, true); + methodInvocationSpan.log(ImmutableMap.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage())); + } + } } diff --git a/guice/src/main/java/com/flipkart/gjex/http/interceptor/HttpFilterInterceptor.java b/guice/src/main/java/com/flipkart/gjex/http/interceptor/HttpFilterInterceptor.java index 2bfd6560..e0ae4a8a 100644 --- a/guice/src/main/java/com/flipkart/gjex/http/interceptor/HttpFilterInterceptor.java +++ b/guice/src/main/java/com/flipkart/gjex/http/interceptor/HttpFilterInterceptor.java @@ -37,8 +37,8 @@ public ServletPathFiltersHolder(ServletPathSpec spec, HttpFilter filter) { } /** - * Map of Filter instances mapped to Service and its method - */ + * Map of Filter instances mapped to Service and its method + */ private final List pathFiltersHolders = new ArrayList<>(); public void registerFilters(List httpFilterParamsList) { @@ -51,20 +51,20 @@ public void registerFilters(List httpFilterParamsList) { public void init(FilterConfig filterConfig) throws ServletException {} /** - * The core method that processes incoming requests and responses. It captures the request and response objects, - * extracts and builds request parameters including client IP and request headers, and invokes the - * {@link HttpFilter#doProcessRequest(ServletRequest, RequestParams)} method for further processing. - * Finally, it ensures that the response is processed by invoking {@link HttpFilter#doProcessResponse(ServletResponse)}. - * - * @param request The incoming ServletRequest - * @param response The outgoing ServletResponse - * @param chain The filter chain to which the request and response should be passed for further processing - * @throws IOException if an I/O error occurs during the filter chain execution - * @throws ServletException if the request could not be handled - */ + * The core method that processes incoming requests and responses. It captures the request and response objects, + * extracts and builds request parameters including client IP and request headers, and invokes the + * {@link HttpFilter#doProcessRequest(ServletRequest, RequestParams)} method for further processing. + * Finally, it ensures that the response is processed by invoking {@link HttpFilter#doProcessResponse(ServletResponse)}. + * + * @param request The incoming ServletRequest + * @param response The outgoing ServletResponse + * @param chain The filter chain to which the request and response should be passed for further processing + * @throws IOException if an I/O error occurs during the filter chain execution + * @throws ServletException if the request could not be handled + */ @Override public final void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { + FilterChain chain) throws IOException, ServletException { List filters = new ArrayList<>(); RequestParams.RequestParamsBuilder> requestParamsBuilder = RequestParams.builder(); @@ -90,12 +90,12 @@ public final void doFilter(ServletRequest request, ServletResponse response, } /** - * Utility method to extract the real client IP address from the ServletRequest. It checks for the - * "X-Forwarded-For" header to support clients connecting through a proxy. - * - * @param request The ServletRequest object containing the client's request - * @return The real IP address of the client - */ + * Utility method to extract the real client IP address from the ServletRequest. It checks for the + * "X-Forwarded-For" header to support clients connecting through a proxy. + * + * @param request The ServletRequest object containing the client's request + * @return The real IP address of the client + */ protected String getClientIp(ServletRequest request) { String remoteAddr = request.getRemoteAddr(); String xForwardedFor = ((HttpServletRequest) request).getHeader("X-Forwarded-For"); diff --git a/guice/src/main/java/com/flipkart/gjex/web/ResourceRegistrar.java b/guice/src/main/java/com/flipkart/gjex/web/ResourceRegistrar.java index d4fe3603..2a90012f 100644 --- a/guice/src/main/java/com/flipkart/gjex/web/ResourceRegistrar.java +++ b/guice/src/main/java/com/flipkart/gjex/web/ResourceRegistrar.java @@ -42,35 +42,35 @@ @Singleton public class ResourceRegistrar implements Logging { - private final ServletContextHandler context; - private final JacksonJaxbJsonProvider jaxbProvider; + private final ServletContextHandler context; + private final JacksonJaxbJsonProvider jaxbProvider; - @Inject - public ResourceRegistrar(@Named("ApiServletContext")ServletContextHandler context, - @Named("JSONMarshallingProvider")JacksonJaxbJsonProvider jaxbProvider) { - this.context = context; - this.jaxbProvider = jaxbProvider; - } + @Inject + public ResourceRegistrar(@Named("ApiServletContext")ServletContextHandler context, + @Named("JSONMarshallingProvider")JacksonJaxbJsonProvider jaxbProvider) { + this.context = context; + this.jaxbProvider = jaxbProvider; + } - public void registerResources(List resourceConfigs) throws Exception { - ResourceConfig uniqueResourceConfig = null; - for (ResourceConfig resourceConfig : resourceConfigs) { - // we check to ensure we are not adding GJEX core application resources again - if (resourceConfig.getApplicationName() == null || - !resourceConfig.getApplicationName().equalsIgnoreCase(Constants.GJEX_CORE_APPLICATION)) { - if (uniqueResourceConfig == null) { - uniqueResourceConfig = resourceConfig; - } else { - throw new ResourceException("Multiple ResourceConfig instances configured for this GJEX application. Only one may be configured."); - } - } - } - if (uniqueResourceConfig == null) { - return; - } - uniqueResourceConfig.register(jaxbProvider); - ServletHolder servlet = new ServletHolder(new ServletContainer(uniqueResourceConfig)); - context.addServlet(servlet, "/*"); - } + public void registerResources(List resourceConfigs) throws Exception { + ResourceConfig uniqueResourceConfig = null; + for (ResourceConfig resourceConfig : resourceConfigs) { + // we check to ensure we are not adding GJEX core application resources again + if (resourceConfig.getApplicationName() == null || + !resourceConfig.getApplicationName().equalsIgnoreCase(Constants.GJEX_CORE_APPLICATION)) { + if (uniqueResourceConfig == null) { + uniqueResourceConfig = resourceConfig; + } else { + throw new ResourceException("Multiple ResourceConfig instances configured for this GJEX application. Only one may be configured."); + } + } + } + if (uniqueResourceConfig == null) { + return; + } + uniqueResourceConfig.register(jaxbProvider); + ServletHolder servlet = new ServletHolder(new ServletContainer(uniqueResourceConfig)); + context.addServlet(servlet, "/*"); + } } diff --git a/runtime/build.gradle b/runtime/build.gradle index af31ae69..1b0c0ac6 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'java-library' apply plugin: 'maven-publish' tasks.withType(GenerateModuleMetadata) { - enabled = false + enabled = false } publishing { @@ -12,10 +12,10 @@ publishing { from components.java pom { licenses { - license { + license { name = "The Apache License, Version 2.0" url = "http://www.apache.org/licenses/LICENSE-2.0.txt" - } + } } } } diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.css b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.css index 93a11042..bcbd20c7 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.css +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.css @@ -1,161 +1,161 @@ .dependencies .spacer { - width: 100%; - margin: 0 auto; - padding-top:4px; - clear:both; + width: 100%; + margin: 0 auto; + padding-top:4px; + clear:both; } .dependencies .last { - margin-right: 0px; + margin-right: 0px; } .dependencies span.loading { - display: block; - padding-top: 6%; - padding-bottom: 6%; - color: gray; - text-align: center; + display: block; + padding-top: 6%; + padding-bottom: 6%; + color: gray; + text-align: center; } .dependencies span.loading.failed { - color: red; + color: red; } .dependencies div.monitor { - float: left; - margin-right:5px; - margin-top:5px; + float: left; + margin-right:5px; + margin-top:5px; } .dependencies div.monitor p.name { - font-weight:bold; - font-size: 10pt; - text-align: right; - padding-bottom: 5px; - color: black; + font-weight:bold; + font-size: 10pt; + text-align: right; + padding-bottom: 5px; + color: black; } .dependencies div.monitor_data { - margin: 0 auto; + margin: 0 auto; } /* override the HREF when we have specified it as a tooltip to not act like a link */ .dependencies div.monitor_data a.tooltip { - text-decoration: none; - cursor: default; + text-decoration: none; + cursor: default; } .dependencies div.monitor_data div.counters { - text-align: right; - padding-bottom: 10px; - font-size: 10pt; - clear: both; + text-align: right; + padding-bottom: 10px; + font-size: 10pt; + clear: both; } .dependencies div.monitor_data div.counters div.cell { - display: inline; - float: right; + display: inline; + float: right; } .dependencies .borderRight { - border-right: 1px solid grey; - padding-right: 6px; - padding-left: 8px; + border-right: 1px solid grey; + padding-right: 6px; + padding-left: 8px; } .dependencies div.cell .line { - display: block; + display: block; } .dependencies div.monitor_data a, .dependencies span.rate_value { - font-weight:bold; + font-weight:bold; } .dependencies span.smaller { - font-size: 8pt; - color: grey; + font-size: 8pt; + color: grey; } .dependencies div.tableRow { - width:100%; - white-space: nowrap; - font-size: 8pt; - margin: 0 auto; - clear:both; - padding-left:26%; + width:100%; + white-space: nowrap; + font-size: 8pt; + margin: 0 auto; + clear:both; + padding-left:26%; } .dependencies div.tableRow .cell { - float:left; + float:left; } .dependencies div.tableRow .header { - width:18%; - text-align:right; - padding-right:2%; + width:18%; + text-align:right; + padding-right:2%; } .dependencies div.tableRow .data { - width:17%; - font-weight: bold; - text-align:right; + width:17%; + font-weight: bold; + text-align:right; } .dependencies div.monitor { - width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */ - height: 150px; + width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */ + height: 150px; } .dependencies .success { - color: green; + color: green; } .dependencies .shortCircuited { - color: blue; + color: blue; } .dependencies .latent { - color: #FFCC00; /* shade of dark yellow */ + color: #FFCC00; /* shade of dark yellow */ } .dependencies .timeout { - color: #FF9900; /* shade of orange */ + color: #FF9900; /* shade of orange */ } .dependencies .failure { - color: red; + color: red; } .dependencies .rejected { - color: purple; + color: purple; } .dependencies .exceptionsThrown { - color: brown; + color: brown; } .dependencies div.monitor_data a.rate { - color: black; - font-size: 11pt; + color: black; + font-size: 11pt; } .dependencies div.rate { - padding-top: 1px; - clear:both; - text-align:right; + padding-top: 1px; + clear:both; + text-align:right; } .dependencies .errorPercentage { - color: grey; + color: grey; } .dependencies div.cell .errorPercentage { - padding-left:5px; - font-size: 12pt !important; + padding-left:5px; + font-size: 12pt !important; } @@ -166,18 +166,18 @@ } .dependencies div.monitor div.chart svg text { - fill: white; + fill: white; } .dependencies div.circuitStatus { - width:100%; - white-space: nowrap; - font-size: 9pt; - margin: 0 auto; - clear:both; - text-align:right; - padding-top: 4px; + width:100%; + white-space: nowrap; + font-size: 9pt; + margin: 0 auto; + clear:both; + text-align:right; + padding-top: 4px; } .dependencies #hidden { @@ -191,9 +191,9 @@ /* sparkline */ .dependencies path { - stroke: steelblue; - stroke-width: 1; - fill: none; + stroke: steelblue; + stroke-width: 1; + fill: none; } diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.js b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.js index 74b0a9f0..50a86eeb 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.js +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/hystrixCommand.js @@ -1,144 +1,144 @@ (function(window) { - // cache the templates we use on this page as global variables (asynchronously) - jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuit.html"), function(data) { - hystrixTemplateCircuit = data; - }); - jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitContainer.html"), function(data) { - hystrixTemplateCircuitContainer = data; - }); - - function getRelativePath(path) { - var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1); - return p + path; - } - - /** - * Object containing functions for displaying and updating the UI with streaming data. - * - * Publish this externally as "HystrixCommandMonitor" - */ - window.HystrixCommandMonitor = function(containerId, args) { - - var self = this; // keep scope under control - self.args = args; - if(self.args == undefined) { - self.args = {}; - } - - this.containerId = containerId; - - /** - * Initialization on construction - */ - // intialize various variables we use for visualization - var maxXaxisForCircle="40%"; - var maxYaxisForCircle="40%"; - var maxRadiusForCircle="125"; - - // CIRCUIT_BREAKER circle visualization settings - self.circuitCircleRadius = d3.scale.pow().exponent(0.5).domain([0, 400]).range(["5", maxRadiusForCircle]); // requests per second per host - self.circuitCircleYaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxXaxisForCircle]); - self.circuitCircleXaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxYaxisForCircle]); - self.circuitColorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]); - self.circuitErrorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]); - - /** - * We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds - * to maintain whatever sort the user (or default) has chosen. - * - * In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing. - */ - setInterval(function() { - // sort since we have added a new one - self.sortSameAsLast(); - }, 10000); - - - /** - * END of Initialization on construction - */ - - /** - * Event listener to handle new messages from EventSource as streamed from the server. - */ - /* public */ self.eventSourceMessageListener = function(e) { - var data = JSON.parse(e.data); - if(data) { - // check for reportingHosts (if not there, set it to 1 for singleHost vs cluster) - if(!data.reportingHosts) { - data.reportingHosts = 1; - } - - if(data && data.type == 'HystrixCommand') { - if (data.deleteData == 'true') { - deleteCircuit(data.name); - } else { - displayCircuit(data); - } - } - } - }; - - /** - * Pre process the data before displying in the UI. - * e.g Get Averages from sums, do rate calculation etc. - */ - function preProcessData(data) { - validateData(data); - // clean up the 'name' field so it doesn't have invalid characters - data.name = data.name.replace(/[.:-]/g,'_'); - // do math - converAllAvg(data); - calcRatePerSecond(data); - } - - /** - * Since the stream of data can be aggregated from multiple hosts in a tiered manner - * the aggregation just sums everything together and provides us the denominator (reportingHosts) - * so we must divide by it to get an average per instance value. - * - * We want to do this on any numerical values where we want per instance rather than cluster-wide sum. - */ - function converAllAvg(data) { - convertAvg(data, "errorPercentage", true); - convertAvg(data, "latencyExecute_mean", false); - convertAvg(data, "latencyTotal_mean", false); - - // the following will break when it becomes a compound string if the property is dynamically changed - convertAvg(data, "propertyValue_metricsRollingStatisticalWindowInMilliseconds", false); - } - - function convertAvg(data, key, decimal) { - if (decimal) { - data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal); - } else { - data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal); - } - } - - function getInstanceAverage(value, reportingHosts, decimal) { - if (decimal) { - return roundNumber(value/reportingHosts); - } else { - return Math.floor(value/reportingHosts); - } - } - - function calcRatePerSecond(data) { - var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000; - - var totalRequests = data["requestCount"]; - if (totalRequests < 0) { - totalRequests = 0; - } - data["ratePerSecond"] = roundNumber(totalRequests / numberSeconds); - data["ratePerSecondPerHost"] = roundNumber(totalRequests / numberSeconds / data["reportingHosts"]) ; - } - - function validateData(data) { - assertNotNull(data,"reportingHosts"); + // cache the templates we use on this page as global variables (asynchronously) + jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuit.html"), function(data) { + hystrixTemplateCircuit = data; + }); + jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitContainer.html"), function(data) { + hystrixTemplateCircuitContainer = data; + }); + + function getRelativePath(path) { + var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1); + return p + path; + } + + /** + * Object containing functions for displaying and updating the UI with streaming data. + * + * Publish this externally as "HystrixCommandMonitor" + */ + window.HystrixCommandMonitor = function(containerId, args) { + + var self = this; // keep scope under control + self.args = args; + if(self.args == undefined) { + self.args = {}; + } + + this.containerId = containerId; + + /** + * Initialization on construction + */ + // intialize various variables we use for visualization + var maxXaxisForCircle="40%"; + var maxYaxisForCircle="40%"; + var maxRadiusForCircle="125"; + + // CIRCUIT_BREAKER circle visualization settings + self.circuitCircleRadius = d3.scale.pow().exponent(0.5).domain([0, 400]).range(["5", maxRadiusForCircle]); // requests per second per host + self.circuitCircleYaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxXaxisForCircle]); + self.circuitCircleXaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxYaxisForCircle]); + self.circuitColorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]); + self.circuitErrorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]); + + /** + * We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds + * to maintain whatever sort the user (or default) has chosen. + * + * In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing. + */ + setInterval(function() { + // sort since we have added a new one + self.sortSameAsLast(); + }, 10000); + + + /** + * END of Initialization on construction + */ + + /** + * Event listener to handle new messages from EventSource as streamed from the server. + */ + /* public */ self.eventSourceMessageListener = function(e) { + var data = JSON.parse(e.data); + if(data) { + // check for reportingHosts (if not there, set it to 1 for singleHost vs cluster) + if(!data.reportingHosts) { + data.reportingHosts = 1; + } + + if(data && data.type == 'HystrixCommand') { + if (data.deleteData == 'true') { + deleteCircuit(data.name); + } else { + displayCircuit(data); + } + } + } + }; + + /** + * Pre process the data before displying in the UI. + * e.g Get Averages from sums, do rate calculation etc. + */ + function preProcessData(data) { + validateData(data); + // clean up the 'name' field so it doesn't have invalid characters + data.name = data.name.replace(/[.:-]/g,'_'); + // do math + converAllAvg(data); + calcRatePerSecond(data); + } + + /** + * Since the stream of data can be aggregated from multiple hosts in a tiered manner + * the aggregation just sums everything together and provides us the denominator (reportingHosts) + * so we must divide by it to get an average per instance value. + * + * We want to do this on any numerical values where we want per instance rather than cluster-wide sum. + */ + function converAllAvg(data) { + convertAvg(data, "errorPercentage", true); + convertAvg(data, "latencyExecute_mean", false); + convertAvg(data, "latencyTotal_mean", false); + + // the following will break when it becomes a compound string if the property is dynamically changed + convertAvg(data, "propertyValue_metricsRollingStatisticalWindowInMilliseconds", false); + } + + function convertAvg(data, key, decimal) { + if (decimal) { + data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal); + } else { + data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal); + } + } + + function getInstanceAverage(value, reportingHosts, decimal) { + if (decimal) { + return roundNumber(value/reportingHosts); + } else { + return Math.floor(value/reportingHosts); + } + } + + function calcRatePerSecond(data) { + var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000; + + var totalRequests = data["requestCount"]; + if (totalRequests < 0) { + totalRequests = 0; + } + data["ratePerSecond"] = roundNumber(totalRequests / numberSeconds); + data["ratePerSecondPerHost"] = roundNumber(totalRequests / numberSeconds / data["reportingHosts"]) ; + } + + function validateData(data) { + assertNotNull(data,"reportingHosts"); assertNotNull(data,"type"); assertNotNull(data,"name"); assertNotNull(data,"group"); @@ -177,360 +177,358 @@ assertNotNull(data,"propertyValue_requestCacheEnabled"); assertNotNull(data,"propertyValue_requestLogEnabled"); assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds"); - } - - function assertNotNull(data, key) { - if(data[key] == undefined) { - throw new Error("Key Missing: " + key + " for " + data.name); - } - } - - /** - * Method to display the CIRCUIT data - * - * @param data - */ - /* private */ function displayCircuit(data) { - - try { - preProcessData(data); - } catch (err) { - log("Failed preProcessData: " + err.message); - return; - } - - // add the 'addCommas' function to the 'data' object so the HTML templates can use it - data.addCommas = addCommas; - // add the 'roundNumber' function to the 'data' object so the HTML templates can use it - data.roundNumber = roundNumber; - // add the 'getInstanceAverage' function to the 'data' object so the HTML templates can use it - data.getInstanceAverage = getInstanceAverage; - - var addNew = false; - // check if we need to create the container - if(!$('#CIRCUIT_' + data.name).length) { - // args for display - if(self.args.includeDetailIcon != undefined && self.args.includeDetailIcon) { - data.includeDetailIcon = true; - }else { - data.includeDetailIcon = false; - } - - // it doesn't exist so add it - var html = tmpl(hystrixTemplateCircuitContainer, data); - // remove the loading thing first - $('#' + containerId + ' span.loading').remove(); - // now create the new data and add it - $('#' + containerId + '').append(html); - - // add the default sparkline graph - d3.selectAll('#graph_CIRCUIT_' + data.name + ' svg').append("svg:path"); - - // remember this is new so we can trigger a sort after setting data - addNew = true; - } - - - // now update/insert the data - $('#CIRCUIT_' + data.name + ' div.monitor_data').html(tmpl(hystrixTemplateCircuit, data)); - - var ratePerSecond = data.ratePerSecond; - var ratePerSecondPerHost = data.ratePerSecondPerHost; - var ratePerSecondPerHostDisplay = ratePerSecondPerHost; - var errorThenVolume = (data.errorPercentage * 100000000) + ratePerSecond; - - // set the rates on the div element so it's available for sorting - $('#CIRCUIT_' + data.name).attr('rate_value', ratePerSecond); - $('#CIRCUIT_' + data.name).attr('error_then_volume', errorThenVolume); - - // update errorPercentage color on page - $('#CIRCUIT_' + data.name + ' a.errorPercentage').css('color', self.circuitErrorPercentageColorRange(data.errorPercentage)); - - updateCircle('circuit', '#CIRCUIT_' + data.name + ' circle', ratePerSecondPerHostDisplay, data.errorPercentage); - - if(data.graphValues) { - // we have a set of values to initialize with - updateSparkline('circuit', '#CIRCUIT_' + data.name + ' path', data.graphValues); - } else { - updateSparkline('circuit', '#CIRCUIT_' + data.name + ' path', ratePerSecond); - } - - if(addNew) { - // sort since we added a new circuit - self.sortSameAsLast(); - } - } - - /* round a number to X digits: num => the number to round, dec => the number of decimals */ - /* private */ function roundNumber(num) { - var dec=1; - var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); - var resultAsString = result.toString(); - if(resultAsString.indexOf('.') == -1) { - resultAsString = resultAsString + '.0'; - } - return resultAsString; - }; - - - - - /* private */ function updateCircle(variablePrefix, cssTarget, rate, errorPercentage) { - var newXaxisForCircle = self[variablePrefix + 'CircleXaxis'](rate); - if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) { - newXaxisForCircle = maxXaxisForCircle; - } - var newYaxisForCircle = self[variablePrefix + 'CircleYaxis'](rate); - if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) { - newYaxisForCircle = maxYaxisForCircle; - } - var newRadiusForCircle = self[variablePrefix + 'CircleRadius'](rate); - if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) { - newRadiusForCircle = maxRadiusForCircle; - } - - d3.selectAll(cssTarget) - .transition() - .duration(400) - .attr("cy", newYaxisForCircle) - .attr("cx", newXaxisForCircle) - .attr("r", newRadiusForCircle) - .style("fill", self[variablePrefix + 'ColorRange'](errorPercentage)); - } - - /* private */ function updateSparkline(variablePrefix, cssTarget, newDataPoint) { - var currentTimeMilliseconds = new Date().getTime(); - var data = self[variablePrefix + cssTarget + '_data']; - if(typeof data == 'undefined') { - // else it's new - if(typeof newDataPoint == 'object') { - // we received an array of values, so initialize with it - data = newDataPoint; - } else { - // v: VALUE, t: TIME_IN_MILLISECONDS - data = [{"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}]; - } - self[variablePrefix + cssTarget + '_data'] = data; - } else { - if(typeof newDataPoint == 'object') { - /* if an array is passed in we'll replace the cached one */ - data = newDataPoint; - } else { - // else we just add to the existing one - data.push({"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}); - } - } - - while(data.length > 200) { // 400 should be plenty for the 2 minutes we have the scale set to below even with a very low update latency - // remove data so we don't keep increasing forever - data.shift(); - } - - if(data.length == 1 && data[0].v == 0) { - //console.log("we have a single 0 so skipping"); - // don't show if we have a single 0 - return; - } - - if(data.length > 1 && data[0].v == 0 && data[1].v != 0) { - //console.log("we have a leading 0 so removing it"); - // get rid of a leading 0 if the following number is not a 0 - data.shift(); - } - - var xScale = d3.time.scale().domain([new Date(currentTimeMilliseconds-(60*1000*2)), new Date(currentTimeMilliseconds)]).range([0, 140]); - - var yMin = d3.min(data, function(d) { return d.v; }); - var yMax = d3.max(data, function(d) { return d.v; }); - var yScale = d3.scale.linear().domain([yMin, yMax]).nice().range([60, 0]); // y goes DOWN, so 60 is the "lowest" - - sparkline = d3.svg.line() - // assign the X function to plot our line as we wish - .x(function(d,i) { - // return the X coordinate where we want to plot this datapoint based on the time - return xScale(new Date(d.t)); - }) - .y(function(d) { - return yScale(d.v); - }) - .interpolate("basis"); - - d3.selectAll(cssTarget).attr("d", sparkline(data)); - } - - /* private */ function deleteCircuit(circuitName) { - $('#CIRCUIT_' + circuitName).remove(); - } - - }; - - // public methods for sorting - HystrixCommandMonitor.prototype.sortByVolume = function() { - var direction = "desc"; - if(this.sortedBy == 'rate_desc') { - direction = 'asc'; - } - this.sortByVolumeInDirection(direction); - }; - - HystrixCommandMonitor.prototype.sortByVolumeInDirection = function(direction) { - this.sortedBy = 'rate_' + direction; - $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'}); - }; - - HystrixCommandMonitor.prototype.sortAlphabetically = function() { - var direction = "asc"; - if(this.sortedBy == 'alph_asc') { - direction = 'desc'; - } - this.sortAlphabeticalInDirection(direction); - }; - - HystrixCommandMonitor.prototype.sortAlphabeticalInDirection = function(direction) { - this.sortedBy = 'alph_' + direction; - $('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction}); - }; - - - HystrixCommandMonitor.prototype.sortByError = function() { - var direction = "desc"; - if(this.sortedBy == 'error_desc') { - direction = 'asc'; - } - this.sortByErrorInDirection(direction); - }; - - HystrixCommandMonitor.prototype.sortByErrorInDirection = function(direction) { - this.sortedBy = 'error_' + direction; - $('#' + this.containerId + ' div.monitor').tsort(".errorPercentage .value", {order: direction}); - }; - - HystrixCommandMonitor.prototype.sortByErrorThenVolume = function() { - var direction = "desc"; - if(this.sortedBy == 'error_then_volume_desc') { - direction = 'asc'; - } - this.sortByErrorThenVolumeInDirection(direction); - }; - - HystrixCommandMonitor.prototype.sortByErrorThenVolumeInDirection = function(direction) { - this.sortedBy = 'error_then_volume_' + direction; - $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'error_then_volume'}); - }; - - HystrixCommandMonitor.prototype.sortByLatency90 = function() { - var direction = "desc"; - if(this.sortedBy == 'lat90_desc') { - direction = 'asc'; - } - this.sortedBy = 'lat90_' + direction; - this.sortByMetricInDirection(direction, ".latency90 .value"); - }; - - HystrixCommandMonitor.prototype.sortByLatency99 = function() { - var direction = "desc"; - if(this.sortedBy == 'lat99_desc') { - direction = 'asc'; - } - this.sortedBy = 'lat99_' + direction; - this.sortByMetricInDirection(direction, ".latency99 .value"); - }; - - HystrixCommandMonitor.prototype.sortByLatency995 = function() { - var direction = "desc"; - if(this.sortedBy == 'lat995_desc') { - direction = 'asc'; - } - this.sortedBy = 'lat995_' + direction; - this.sortByMetricInDirection(direction, ".latency995 .value"); - }; - - HystrixCommandMonitor.prototype.sortByLatencyMean = function() { - var direction = "desc"; - if(this.sortedBy == 'latMean_desc') { - direction = 'asc'; - } - this.sortedBy = 'latMean_' + direction; - this.sortByMetricInDirection(direction, ".latencyMean .value"); - }; - - HystrixCommandMonitor.prototype.sortByLatencyMedian = function() { - var direction = "desc"; - if(this.sortedBy == 'latMedian_desc') { - direction = 'asc'; - } - this.sortedBy = 'latMedian_' + direction; - this.sortByMetricInDirection(direction, ".latencyMedian .value"); - }; - - HystrixCommandMonitor.prototype.sortByMetricInDirection = function(direction, metric) { - $('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction}); - }; - - // this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose - HystrixCommandMonitor.prototype.sortSameAsLast = function() { - if(this.sortedBy == 'alph_asc') { - this.sortAlphabeticalInDirection('asc'); - } else if(this.sortedBy == 'alph_desc') { - this.sortAlphabeticalInDirection('desc'); - } else if(this.sortedBy == 'rate_asc') { - this.sortByVolumeInDirection('asc'); - } else if(this.sortedBy == 'rate_desc') { - this.sortByVolumeInDirection('desc'); - } else if(this.sortedBy == 'error_asc') { - this.sortByErrorInDirection('asc'); - } else if(this.sortedBy == 'error_desc') { - this.sortByErrorInDirection('desc'); - } else if(this.sortedBy == 'error_then_volume_asc') { - this.sortByErrorThenVolumeInDirection('asc'); - } else if(this.sortedBy == 'error_then_volume_desc') { - this.sortByErrorThenVolumeInDirection('desc'); - } else if(this.sortedBy == 'lat90_asc') { - this.sortByMetricInDirection('asc', '.latency90 .value'); - } else if(this.sortedBy == 'lat90_desc') { - this.sortByMetricInDirection('desc', '.latency90 .value'); - } else if(this.sortedBy == 'lat99_asc') { - this.sortByMetricInDirection('asc', '.latency99 .value'); - } else if(this.sortedBy == 'lat99_desc') { - this.sortByMetricInDirection('desc', '.latency99 .value'); - } else if(this.sortedBy == 'lat995_asc') { - this.sortByMetricInDirection('asc', '.latency995 .value'); - } else if(this.sortedBy == 'lat995_desc') { - this.sortByMetricInDirection('desc', '.latency995 .value'); - } else if(this.sortedBy == 'latMean_asc') { - this.sortByMetricInDirection('asc', '.latencyMean .value'); - } else if(this.sortedBy == 'latMean_desc') { - this.sortByMetricInDirection('desc', '.latencyMean .value'); - } else if(this.sortedBy == 'latMedian_asc') { - this.sortByMetricInDirection('asc', '.latencyMedian .value'); - } else if(this.sortedBy == 'latMedian_desc') { - this.sortByMetricInDirection('desc', '.latencyMedian .value'); - } - }; - - // default sort type and direction - this.sortedBy = 'alph_asc'; - - - // a temporary home for the logger until we become more sophisticated - function log(message) { - console.log(message); - }; - - function addCommas(nStr){ - nStr += ''; - if(nStr.length <=3) { - return nStr; //shortcut if we don't need commas - } - x = nStr.split('.'); - x1 = x[0]; - x2 = x.length > 1 ? '.' + x[1] : ''; - var rgx = /(\d+)(\d{3})/; - while (rgx.test(x1)) { - x1 = x1.replace(rgx, '$1' + ',' + '$2'); - } - return x1 + x2; - } + } + + function assertNotNull(data, key) { + if(data[key] == undefined) { + throw new Error("Key Missing: " + key + " for " + data.name); + } + } + + /** + * Method to display the CIRCUIT data + * + * @param data + */ + /* private */ function displayCircuit(data) { + + try { + preProcessData(data); + } catch (err) { + log("Failed preProcessData: " + err.message); + return; + } + + // add the 'addCommas' function to the 'data' object so the HTML templates can use it + data.addCommas = addCommas; + // add the 'roundNumber' function to the 'data' object so the HTML templates can use it + data.roundNumber = roundNumber; + // add the 'getInstanceAverage' function to the 'data' object so the HTML templates can use it + data.getInstanceAverage = getInstanceAverage; + + var addNew = false; + // check if we need to create the container + if(!$('#CIRCUIT_' + data.name).length) { + // args for display + if(self.args.includeDetailIcon != undefined && self.args.includeDetailIcon) { + data.includeDetailIcon = true; + }else { + data.includeDetailIcon = false; + } + + // it doesn't exist so add it + var html = tmpl(hystrixTemplateCircuitContainer, data); + // remove the loading thing first + $('#' + containerId + ' span.loading').remove(); + // now create the new data and add it + $('#' + containerId + '').append(html); + + // add the default sparkline graph + d3.selectAll('#graph_CIRCUIT_' + data.name + ' svg').append("svg:path"); + + // remember this is new so we can trigger a sort after setting data + addNew = true; + } + + + // now update/insert the data + $('#CIRCUIT_' + data.name + ' div.monitor_data').html(tmpl(hystrixTemplateCircuit, data)); + + var ratePerSecond = data.ratePerSecond; + var ratePerSecondPerHost = data.ratePerSecondPerHost; + var ratePerSecondPerHostDisplay = ratePerSecondPerHost; + var errorThenVolume = (data.errorPercentage * 100000000) + ratePerSecond; + + // set the rates on the div element so it's available for sorting + $('#CIRCUIT_' + data.name).attr('rate_value', ratePerSecond); + $('#CIRCUIT_' + data.name).attr('error_then_volume', errorThenVolume); + + // update errorPercentage color on page + $('#CIRCUIT_' + data.name + ' a.errorPercentage').css('color', self.circuitErrorPercentageColorRange(data.errorPercentage)); + + updateCircle('circuit', '#CIRCUIT_' + data.name + ' circle', ratePerSecondPerHostDisplay, data.errorPercentage); + + if(data.graphValues) { + // we have a set of values to initialize with + updateSparkline('circuit', '#CIRCUIT_' + data.name + ' path', data.graphValues); + } else { + updateSparkline('circuit', '#CIRCUIT_' + data.name + ' path', ratePerSecond); + } + + if(addNew) { + // sort since we added a new circuit + self.sortSameAsLast(); + } + } + + /* round a number to X digits: num => the number to round, dec => the number of decimals */ + /* private */ function roundNumber(num) { + var dec=1; + var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); + var resultAsString = result.toString(); + if(resultAsString.indexOf('.') == -1) { + resultAsString = resultAsString + '.0'; + } + return resultAsString; + }; + + + + + /* private */ function updateCircle(variablePrefix, cssTarget, rate, errorPercentage) { + var newXaxisForCircle = self[variablePrefix + 'CircleXaxis'](rate); + if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) { + newXaxisForCircle = maxXaxisForCircle; + } + var newYaxisForCircle = self[variablePrefix + 'CircleYaxis'](rate); + if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) { + newYaxisForCircle = maxYaxisForCircle; + } + var newRadiusForCircle = self[variablePrefix + 'CircleRadius'](rate); + if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) { + newRadiusForCircle = maxRadiusForCircle; + } + + d3.selectAll(cssTarget) + .transition() + .duration(400) + .attr("cy", newYaxisForCircle) + .attr("cx", newXaxisForCircle) + .attr("r", newRadiusForCircle) + .style("fill", self[variablePrefix + 'ColorRange'](errorPercentage)); + } + + /* private */ function updateSparkline(variablePrefix, cssTarget, newDataPoint) { + var currentTimeMilliseconds = new Date().getTime(); + var data = self[variablePrefix + cssTarget + '_data']; + if(typeof data == 'undefined') { + // else it's new + if(typeof newDataPoint == 'object') { + // we received an array of values, so initialize with it + data = newDataPoint; + } else { + // v: VALUE, t: TIME_IN_MILLISECONDS + data = [{"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}]; + } + self[variablePrefix + cssTarget + '_data'] = data; + } else { + if(typeof newDataPoint == 'object') { + /* if an array is passed in we'll replace the cached one */ + data = newDataPoint; + } else { + // else we just add to the existing one + data.push({"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}); + } + } + + while(data.length > 200) { // 400 should be plenty for the 2 minutes we have the scale set to below even with a very low update latency + // remove data so we don't keep increasing forever + data.shift(); + } + + if(data.length == 1 && data[0].v == 0) { + //console.log("we have a single 0 so skipping"); + // don't show if we have a single 0 + return; + } + + if(data.length > 1 && data[0].v == 0 && data[1].v != 0) { + //console.log("we have a leading 0 so removing it"); + // get rid of a leading 0 if the following number is not a 0 + data.shift(); + } + + var xScale = d3.time.scale().domain([new Date(currentTimeMilliseconds-(60*1000*2)), new Date(currentTimeMilliseconds)]).range([0, 140]); + + var yMin = d3.min(data, function(d) { return d.v; }); + var yMax = d3.max(data, function(d) { return d.v; }); + var yScale = d3.scale.linear().domain([yMin, yMax]).nice().range([60, 0]); // y goes DOWN, so 60 is the "lowest" + + sparkline = d3.svg.line() + // assign the X function to plot our line as we wish + .x(function(d,i) { + // return the X coordinate where we want to plot this datapoint based on the time + return xScale(new Date(d.t)); + }) + .y(function(d) { + return yScale(d.v); + }) + .interpolate("basis"); + + d3.selectAll(cssTarget).attr("d", sparkline(data)); + } + + /* private */ function deleteCircuit(circuitName) { + $('#CIRCUIT_' + circuitName).remove(); + } + + }; + + // public methods for sorting + HystrixCommandMonitor.prototype.sortByVolume = function() { + var direction = "desc"; + if(this.sortedBy == 'rate_desc') { + direction = 'asc'; + } + this.sortByVolumeInDirection(direction); + }; + + HystrixCommandMonitor.prototype.sortByVolumeInDirection = function(direction) { + this.sortedBy = 'rate_' + direction; + $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'}); + }; + + HystrixCommandMonitor.prototype.sortAlphabetically = function() { + var direction = "asc"; + if(this.sortedBy == 'alph_asc') { + direction = 'desc'; + } + this.sortAlphabeticalInDirection(direction); + }; + + HystrixCommandMonitor.prototype.sortAlphabeticalInDirection = function(direction) { + this.sortedBy = 'alph_' + direction; + $('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction}); + }; + + + HystrixCommandMonitor.prototype.sortByError = function() { + var direction = "desc"; + if(this.sortedBy == 'error_desc') { + direction = 'asc'; + } + this.sortByErrorInDirection(direction); + }; + + HystrixCommandMonitor.prototype.sortByErrorInDirection = function(direction) { + this.sortedBy = 'error_' + direction; + $('#' + this.containerId + ' div.monitor').tsort(".errorPercentage .value", {order: direction}); + }; + + HystrixCommandMonitor.prototype.sortByErrorThenVolume = function() { + var direction = "desc"; + if(this.sortedBy == 'error_then_volume_desc') { + direction = 'asc'; + } + this.sortByErrorThenVolumeInDirection(direction); + }; + + HystrixCommandMonitor.prototype.sortByErrorThenVolumeInDirection = function(direction) { + this.sortedBy = 'error_then_volume_' + direction; + $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'error_then_volume'}); + }; + + HystrixCommandMonitor.prototype.sortByLatency90 = function() { + var direction = "desc"; + if(this.sortedBy == 'lat90_desc') { + direction = 'asc'; + } + this.sortedBy = 'lat90_' + direction; + this.sortByMetricInDirection(direction, ".latency90 .value"); + }; + + HystrixCommandMonitor.prototype.sortByLatency99 = function() { + var direction = "desc"; + if(this.sortedBy == 'lat99_desc') { + direction = 'asc'; + } + this.sortedBy = 'lat99_' + direction; + this.sortByMetricInDirection(direction, ".latency99 .value"); + }; + + HystrixCommandMonitor.prototype.sortByLatency995 = function() { + var direction = "desc"; + if(this.sortedBy == 'lat995_desc') { + direction = 'asc'; + } + this.sortedBy = 'lat995_' + direction; + this.sortByMetricInDirection(direction, ".latency995 .value"); + }; + + HystrixCommandMonitor.prototype.sortByLatencyMean = function() { + var direction = "desc"; + if(this.sortedBy == 'latMean_desc') { + direction = 'asc'; + } + this.sortedBy = 'latMean_' + direction; + this.sortByMetricInDirection(direction, ".latencyMean .value"); + }; + + HystrixCommandMonitor.prototype.sortByLatencyMedian = function() { + var direction = "desc"; + if(this.sortedBy == 'latMedian_desc') { + direction = 'asc'; + } + this.sortedBy = 'latMedian_' + direction; + this.sortByMetricInDirection(direction, ".latencyMedian .value"); + }; + + HystrixCommandMonitor.prototype.sortByMetricInDirection = function(direction, metric) { + $('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction}); + }; + + // this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose + HystrixCommandMonitor.prototype.sortSameAsLast = function() { + if(this.sortedBy == 'alph_asc') { + this.sortAlphabeticalInDirection('asc'); + } else if(this.sortedBy == 'alph_desc') { + this.sortAlphabeticalInDirection('desc'); + } else if(this.sortedBy == 'rate_asc') { + this.sortByVolumeInDirection('asc'); + } else if(this.sortedBy == 'rate_desc') { + this.sortByVolumeInDirection('desc'); + } else if(this.sortedBy == 'error_asc') { + this.sortByErrorInDirection('asc'); + } else if(this.sortedBy == 'error_desc') { + this.sortByErrorInDirection('desc'); + } else if(this.sortedBy == 'error_then_volume_asc') { + this.sortByErrorThenVolumeInDirection('asc'); + } else if(this.sortedBy == 'error_then_volume_desc') { + this.sortByErrorThenVolumeInDirection('desc'); + } else if(this.sortedBy == 'lat90_asc') { + this.sortByMetricInDirection('asc', '.latency90 .value'); + } else if(this.sortedBy == 'lat90_desc') { + this.sortByMetricInDirection('desc', '.latency90 .value'); + } else if(this.sortedBy == 'lat99_asc') { + this.sortByMetricInDirection('asc', '.latency99 .value'); + } else if(this.sortedBy == 'lat99_desc') { + this.sortByMetricInDirection('desc', '.latency99 .value'); + } else if(this.sortedBy == 'lat995_asc') { + this.sortByMetricInDirection('asc', '.latency995 .value'); + } else if(this.sortedBy == 'lat995_desc') { + this.sortByMetricInDirection('desc', '.latency995 .value'); + } else if(this.sortedBy == 'latMean_asc') { + this.sortByMetricInDirection('asc', '.latencyMean .value'); + } else if(this.sortedBy == 'latMean_desc') { + this.sortByMetricInDirection('desc', '.latencyMean .value'); + } else if(this.sortedBy == 'latMedian_asc') { + this.sortByMetricInDirection('asc', '.latencyMedian .value'); + } else if(this.sortedBy == 'latMedian_desc') { + this.sortByMetricInDirection('desc', '.latencyMedian .value'); + } + }; + + // default sort type and direction + this.sortedBy = 'alph_asc'; + + + // a temporary home for the logger until we become more sophisticated + function log(message) { + console.log(message); + }; + + function addCommas(nStr){ + nStr += ''; + if(nStr.length <=3) { + return nStr; //shortcut if we don't need commas + } + x = nStr.split('.'); + x1 = x[0]; + x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; + } })(window); - - diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuit.html b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuit.html index 2bc2ffb2..8b6532d6 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuit.html +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuit.html @@ -1,73 +1,73 @@ - - - + + -
- <% if(propertyValue_circuitBreakerForceClosed) { %> - [ Forced Closed ] - <% } %> - <% if(propertyValue_circuitBreakerForceOpen) { %> - Circuit Forced Open - <% } else { %> - <% if(isCircuitBreakerOpen == reportingHosts) { %> - Circuit Open - <% } else if(isCircuitBreakerOpen == 0) { %> - Circuit Closed - <% } else { - /* We have some circuits that are open */ - %> - Circuit <%= isCircuitBreakerOpen.replace("true", "Open").replace("false", "Closed") %>) - <% } %> - <% } %> -
+
+ <% if(propertyValue_circuitBreakerForceClosed) { %> + [ Forced Closed ] + <% } %> + <% if(propertyValue_circuitBreakerForceOpen) { %> + Circuit Forced Open + <% } else { %> + <% if(isCircuitBreakerOpen == reportingHosts) { %> + Circuit Open + <% } else if(isCircuitBreakerOpen == 0) { %> + Circuit Closed + <% } else { + /* We have some circuits that are open */ + %> + Circuit <%= isCircuitBreakerOpen.replace("true", "Open").replace("false", "Closed") %>) + <% } %> + <% } %> +
-
+
-
- <% if(typeof reportingHosts != 'undefined') { %> -
Hosts
-
<%= reportingHosts %>
- <% } else { %> -
Host
-
Single
- <% } %> -
90th
-
<%= getInstanceAverage(latencyExecute['90'], reportingHosts, false) %>ms
-
-
-
Median
-
<%= getInstanceAverage(latencyExecute['50'], reportingHosts, false) %>ms
-
99th
-
<%= getInstanceAverage(latencyExecute['99'], reportingHosts, false) %>ms
-
-
-
Mean
-
<%= latencyExecute_mean %>ms
-
99.5th
-
<%= getInstanceAverage(latencyExecute['99.5'], reportingHosts, false) %>ms
-
+
+ <% if(typeof reportingHosts != 'undefined') { %> +
Hosts
+
<%= reportingHosts %>
+ <% } else { %> +
Host
+
Single
+ <% } %> +
90th
+
<%= getInstanceAverage(latencyExecute['90'], reportingHosts, false) %>ms
+
+
+
Median
+
<%= getInstanceAverage(latencyExecute['50'], reportingHosts, false) %>ms
+
99th
+
<%= getInstanceAverage(latencyExecute['99'], reportingHosts, false) %>ms
+
+
+
Mean
+
<%= latencyExecute_mean %>ms
+
99.5th
+
<%= getInstanceAverage(latencyExecute['99.5'], reportingHosts, false) %>ms
+
diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitContainer.html b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitContainer.html index 6b849939..420b091b 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitContainer.html +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitContainer.html @@ -1,38 +1,38 @@
- <% - var displayName = name; - var toolTip = ""; - if(displayName.length > 32) { - displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length); - toolTip = "title=\"" + name + "\""; - } - %> + <% + var displayName = name; + var toolTip = ""; + if(displayName.length > 32) { + displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length); + toolTip = "title=\"" + name + "\""; + } + %> -
+
- <% if(includeDetailIcon) { %> -

style="padding-right:16px"> - <%= displayName %> - -

- <% } else { %> -

><%= displayName %>

- <% } %> + <% if(includeDetailIcon) { %> +

style="padding-right:16px"> + <%= displayName %> + +

+ <% } else { %> +

><%= displayName %>

+ <% } %>
-
-
-
-
+
+
+
+
- + /* add the line graph - it will be populated by javascript, no default to show here */ + var graph = d3.select("#graph_CIRCUIT_<%= name %>").append("svg:svg").attr("width", "100%").attr("height", "100%"); +
diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitProperties.html b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitProperties.html index 5b8c0fae..19c1dae1 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitProperties.html +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixCommand/templates/hystrixCircuitProperties.html @@ -1,6 +1,6 @@ -
-
Median
-
<%= sla_medianLastMinute %>ms
-
99th
-
<%= sla_percentile99LastMinute %>ms
-
+
+
Median
+
<%= sla_medianLastMinute %>ms
+
99th
+
<%= sla_percentile99LastMinute %>ms
+
diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.css b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.css index 2104f1e0..8f0502f9 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.css +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.css @@ -1,92 +1,92 @@ .dependencyThreadPools .spacer { - width: 100%; - margin: 0 auto; - padding-top:4px; - clear:both; + width: 100%; + margin: 0 auto; + padding-top:4px; + clear:both; } .dependencyThreadPools .last { - margin-right: 0px; + margin-right: 0px; } .dependencyThreadPools span.loading { - display: block; - padding-top: 6%; - padding-bottom: 6%; - color: gray; - text-align: center; + display: block; + padding-top: 6%; + padding-bottom: 6%; + color: gray; + text-align: center; } .dependencyThreadPools span.loading.failed { - color: red; + color: red; } .dependencyThreadPools div.monitor { - float: left; - margin-right:5px; /* these are tweaked to look good on desktop and iPad portrait, and fit things densely */ - margin-top:5px; + float: left; + margin-right:5px; /* these are tweaked to look good on desktop and iPad portrait, and fit things densely */ + margin-top:5px; } .dependencyThreadPools div.monitor p.name { - font-weight:bold; - font-size: 10pt; - text-align: right; - padding-bottom: 5px; + font-weight:bold; + font-size: 10pt; + text-align: right; + padding-bottom: 5px; } .dependencyThreadPools div.monitor_data { - margin: 0 auto; + margin: 0 auto; } .dependencyThreadPools span.smaller { - font-size: 8pt; - color: grey; + font-size: 8pt; + color: grey; } .dependencyThreadPools div.tableRow { - width:100%; - white-space: nowrap; - font-size: 8pt; - margin: 0 auto; - clear:both; + width:100%; + white-space: nowrap; + font-size: 8pt; + margin: 0 auto; + clear:both; } .dependencyThreadPools div.tableRow .cell { - float:left; + float:left; } .dependencyThreadPools div.tableRow .header { - text-align:right; - padding-right:5px; + text-align:right; + padding-right:5px; } .dependencyThreadPools div.tableRow .header.left { - width:85px; + width:85px; } .dependencyThreadPools div.tableRow .header.right { - width:75px; + width:75px; } .dependencyThreadPools div.tableRow .data { - font-weight: bold; - text-align:right; + font-weight: bold; + text-align:right; } .dependencyThreadPools div.tableRow .data.left { - width:30px; + width:30px; } .dependencyThreadPools div.tableRow .data.right { - width:45px; + width:45px; } .dependencyThreadPools div.monitor { - width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */ - height: 110px; + width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */ + height: 110px; } @@ -95,24 +95,24 @@ /* override the HREF when we have specified it as a tooltip to not act like a link */ .dependencyThreadPools div.monitor_data a.tooltip { - text-decoration: none; - cursor: default; + text-decoration: none; + cursor: default; } .dependencyThreadPools div.monitor_data a.rate { - font-weight:bold; - color: black; - font-size: 11pt; + font-weight:bold; + color: black; + font-size: 11pt; } .dependencyThreadPools div.rate { - padding-top: 1px; - clear:both; - text-align:right; + padding-top: 1px; + clear:both; + text-align:right; } .dependencyThreadPools span.rate_value { - font-weight:bold; + font-weight:bold; } @@ -128,7 +128,7 @@ } .dependencyThreadPools div.monitor div.chart svg text { - fill: white; + fill: white; } .dependencyThreadPools #hidden { @@ -137,5 +137,3 @@ background: lightgrey; display: none; } - - diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.js b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.js index 30bffe3b..db3aa474 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.js +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/hystrixThreadPool.js @@ -1,339 +1,337 @@ (function(window) { - // cache the templates we use on this page as global variables (asynchronously) - jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPool.html"), function(data) { - htmlTemplate = data; - }); - jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html"), function(data) { - htmlTemplateContainer = data; - }); - - function getRelativePath(path) { - var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1); - return p + path; - } - - /** - * Object containing functions for displaying and updating the UI with streaming data. - * - * Publish this externally as "HystrixThreadPoolMonitor" - */ - window.HystrixThreadPoolMonitor = function(containerId) { - - var self = this; // keep scope under control - - this.containerId = containerId; - - /** - * Initialization on construction - */ - // intialize various variables we use for visualization - var maxXaxisForCircle="40%"; - var maxYaxisForCircle="40%"; - var maxRadiusForCircle="125"; - var maxDomain = 2000; - - self.circleRadius = d3.scale.pow().exponent(0.5).domain([0, maxDomain]).range(["5", maxRadiusForCircle]); // requests per second per host - self.circleYaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxXaxisForCircle]); - self.circleXaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxYaxisForCircle]); - self.colorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]); - self.errorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]); - - /** - * We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds - * to maintain whatever sort the user (or default) has chosen. - * - * In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing. - */ - setInterval(function() { - // sort since we have added a new one - self.sortSameAsLast(); - }, 1000) - - /** - * END of Initialization on construction - */ - - /** - * Event listener to handle new messages from EventSource as streamed from the server. - */ - /* public */ self.eventSourceMessageListener = function(e) { - var data = JSON.parse(e.data); - if(data) { - // check for reportingHosts (if not there, set it to 1 for singleHost vs cluster) - if(!data.reportingHosts) { - data.reportingHosts = 1; - } - - if(data && data.type == 'HystrixThreadPool') { - if (data.deleteData == 'true') { - deleteThreadPool(data.name); - } else { - displayThreadPool(data); - } - } - } - } - - /** - * Pre process the data before displying in the UI. - * e.g Get Averages from sums, do rate calculation etc. - */ - function preProcessData(data) { - validateData(data); - // clean up the 'name' field so it doesn't have invalid characters - data.name = data.name.replace(/[.:-]/g,'_'); - // do math - converAllAvg(data); - } - - function converAllAvg(data) { - convertAvg(data, "propertyValue_corePoolSize", false); - convertAvg(data, "propertyValue_queueSizeRejectionThreshold", false); - convertAvg(data, "rollingThreadExecutions", false); - } - - function convertAvg(data, key, decimal) { - if (decimal) { - data[key] = roundNumber(data[key]/data["reportingHosts"]); - } else { - data[key] = Math.floor(data[key]/data["reportingHosts"]); - } - } - - function validateData(data) { - - assertNotNull(data,"type"); - assertNotNull(data,"name"); - // assertNotNull(data,"currentTime"); - assertNotNull(data,"currentActiveCount"); - assertNotNull(data,"currentCompletedTaskCount"); - assertNotNull(data,"currentCorePoolSize"); - assertNotNull(data,"currentLargestPoolSize"); - assertNotNull(data,"currentMaximumPoolSize"); - assertNotNull(data,"currentPoolSize"); - assertNotNull(data,"currentQueueSize"); - assertNotNull(data,"currentTaskCount"); - assertNotNull(data,"rollingCountThreadsExecuted"); - assertNotNull(data,"rollingMaxActiveThreads"); - assertNotNull(data,"reportingHosts"); + // cache the templates we use on this page as global variables (asynchronously) + jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPool.html"), function(data) { + htmlTemplate = data; + }); + jQuery.get(getRelativePath("../hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html"), function(data) { + htmlTemplateContainer = data; + }); + + function getRelativePath(path) { + var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1); + return p + path; + } + + /** + * Object containing functions for displaying and updating the UI with streaming data. + * + * Publish this externally as "HystrixThreadPoolMonitor" + */ + window.HystrixThreadPoolMonitor = function(containerId) { + + var self = this; // keep scope under control + + this.containerId = containerId; + + /** + * Initialization on construction + */ + // intialize various variables we use for visualization + var maxXaxisForCircle="40%"; + var maxYaxisForCircle="40%"; + var maxRadiusForCircle="125"; + var maxDomain = 2000; + + self.circleRadius = d3.scale.pow().exponent(0.5).domain([0, maxDomain]).range(["5", maxRadiusForCircle]); // requests per second per host + self.circleYaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxXaxisForCircle]); + self.circleXaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxYaxisForCircle]); + self.colorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]); + self.errorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]); + + /** + * We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds + * to maintain whatever sort the user (or default) has chosen. + * + * In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing. + */ + setInterval(function() { + // sort since we have added a new one + self.sortSameAsLast(); + }, 1000) + + /** + * END of Initialization on construction + */ + + /** + * Event listener to handle new messages from EventSource as streamed from the server. + */ + /* public */ self.eventSourceMessageListener = function(e) { + var data = JSON.parse(e.data); + if(data) { + // check for reportingHosts (if not there, set it to 1 for singleHost vs cluster) + if(!data.reportingHosts) { + data.reportingHosts = 1; + } + + if(data && data.type == 'HystrixThreadPool') { + if (data.deleteData == 'true') { + deleteThreadPool(data.name); + } else { + displayThreadPool(data); + } + } + } + } + + /** + * Pre process the data before displying in the UI. + * e.g Get Averages from sums, do rate calculation etc. + */ + function preProcessData(data) { + validateData(data); + // clean up the 'name' field so it doesn't have invalid characters + data.name = data.name.replace(/[.:-]/g,'_'); + // do math + converAllAvg(data); + } + + function converAllAvg(data) { + convertAvg(data, "propertyValue_corePoolSize", false); + convertAvg(data, "propertyValue_queueSizeRejectionThreshold", false); + convertAvg(data, "rollingThreadExecutions", false); + } + + function convertAvg(data, key, decimal) { + if (decimal) { + data[key] = roundNumber(data[key]/data["reportingHosts"]); + } else { + data[key] = Math.floor(data[key]/data["reportingHosts"]); + } + } + + function validateData(data) { + + assertNotNull(data,"type"); + assertNotNull(data,"name"); + // assertNotNull(data,"currentTime"); + assertNotNull(data,"currentActiveCount"); + assertNotNull(data,"currentCompletedTaskCount"); + assertNotNull(data,"currentCorePoolSize"); + assertNotNull(data,"currentLargestPoolSize"); + assertNotNull(data,"currentMaximumPoolSize"); + assertNotNull(data,"currentPoolSize"); + assertNotNull(data,"currentQueueSize"); + assertNotNull(data,"currentTaskCount"); + assertNotNull(data,"rollingCountThreadsExecuted"); + assertNotNull(data,"rollingMaxActiveThreads"); + assertNotNull(data,"reportingHosts"); assertNotNull(data,"propertyValue_queueSizeRejectionThreshold"); - assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds"); - } - - function assertNotNull(data, key) { - if(data[key] == undefined) { - if (key == "dependencyOwner") { - data["dependencyOwner"] = data.name; - } else { - throw new Error("Key Missing: " + key + " for " + data.name) - } - } - } - - /** - * Method to display the THREAD_POOL data - * - * @param data - */ - /* private */ function displayThreadPool(data) { - - try { - preProcessData(data); - } catch (err) { - log("Failed preProcessData: " + err.message); - return; - } - - // add the 'addCommas' function to the 'data' object so the HTML templates can use it - data.addCommas = addCommas; - // add the 'roundNumber' function to the 'data' object so the HTML templates can use it - data.roundNumber = roundNumber; - - var addNew = false; - // check if we need to create the container - if(!$('#THREAD_POOL_' + data.name).length) { - // it doesn't exist so add it - var html = tmpl(htmlTemplateContainer, data); - // remove the loading thing first - $('#' + containerId + ' span.loading').remove(); - // get the current last column and remove the 'last' class from it - $('#' + containerId + ' div.last').removeClass('last'); - // now create the new data and add it - $('#' + containerId + '').append(html); - // add the 'last' class to the column we just added - $('#' + containerId + ' div.monitor').last().addClass('last'); - - // add the default sparkline graph - d3.selectAll('#graph_THREAD_POOL_' + data.name + ' svg').append("svg:path"); - - // remember this is new so we can trigger a sort after setting data - addNew = true; - } - - var ratePerSecondPerHost = roundNumber(data.rollingCountThreadsExecuted/data.reportingHosts); - - data.ratePerSecond = data.rollingCountThreadsExecuted; - data.ratePerSecondPerHost = ratePerSecondPerHost; - - // set the rate on the div element so it's available for sorting - $('#THREAD_POOL_' + data.name).attr('rate_value', data.ratePerSecondPerHost); - - // now update/insert the data - $('#THREAD_POOL_' + data.name + ' div.monitor_data').html(tmpl(htmlTemplate, data)); - - - // set variables for circle visualization - var rate = ratePerSecondPerHost; - // we will treat each item in queue as 1% of an error visualization - // ie. 5 threads in queue per instance == 5% error percentage - var errorPercentage = data.currentQueueSize / data.reportingHosts; - - updateCircle('#THREAD_POOL_' + data.name + ' circle', rate, errorPercentage); - - if(addNew) { - // sort since we added a new circuit - self.sortSameAsLast(); - } - } - - /* round a number to X digits: num => the number to round, dec => the number of decimals */ - /* private */ function roundNumber(num) { - var dec=1; // we are hardcoding to support only 1 decimal so that our padding logic at the end is simple - var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); - var resultAsString = result.toString(); - if(resultAsString.indexOf('.') == -1) { - resultAsString = resultAsString + '.'; - for(var i=0; i parseInt(maxXaxisForCircle)) { - newXaxisForCircle = maxXaxisForCircle; - } - var newYaxisForCircle = self.circleYaxis(rate); - if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) { - newYaxisForCircle = maxYaxisForCircle; - } - var newRadiusForCircle = self.circleRadius(rate); - if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) { - newRadiusForCircle = maxRadiusForCircle; - } - - d3.selectAll(cssTarget) - .transition() - .duration(400) - .attr("cy", newYaxisForCircle) - .attr("cx", newXaxisForCircle) - .attr("r", newRadiusForCircle) - .style("fill", self.colorRange(errorPercentage)); - } - - /* private */ function deleteThreadPool(circuitName) { - $('#THREAD_POOL_' + circuitName).remove(); - } - - } - - // public methods for sorting - HystrixThreadPoolMonitor.prototype.sortByVolume = function() { - var direction = "desc"; - if(this.sortedBy == 'rate_desc') { - direction = 'asc'; - } - this.sortByVolumeInDirection(direction); - } - - HystrixThreadPoolMonitor.prototype.sortByVolumeInDirection = function(direction) { - this.sortedBy = 'rate_' + direction; - $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'}); - } - - HystrixThreadPoolMonitor.prototype.sortAlphabetically = function() { - var direction = "asc"; - if(this.sortedBy == 'alph_asc') { - direction = 'desc'; - } - this.sortAlphabeticalInDirection(direction); - } - - HystrixThreadPoolMonitor.prototype.sortAlphabeticalInDirection = function(direction) { - this.sortedBy = 'alph_' + direction; - $('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction}); - } - - HystrixThreadPoolMonitor.prototype.sortByMetricInDirection = function(direction, metric) { - $('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction}); - } - - // this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose - HystrixThreadPoolMonitor.prototype.sortSameAsLast = function() { - if(this.sortedBy == 'alph_asc') { - this.sortAlphabeticalInDirection('asc'); - } else if(this.sortedBy == 'alph_desc') { - this.sortAlphabeticalInDirection('desc'); - } else if(this.sortedBy == 'rate_asc') { - this.sortByVolumeInDirection('asc'); - } else if(this.sortedBy == 'rate_desc') { - this.sortByVolumeInDirection('desc'); - } else if(this.sortedBy == 'error_asc') { - this.sortByErrorInDirection('asc'); - } else if(this.sortedBy == 'error_desc') { - this.sortByErrorInDirection('desc'); - } else if(this.sortedBy == 'lat90_asc') { - this.sortByMetricInDirection('asc', 'p90'); - } else if(this.sortedBy == 'lat90_desc') { - this.sortByMetricInDirection('desc', 'p90'); - } else if(this.sortedBy == 'lat99_asc') { - this.sortByMetricInDirection('asc', 'p99'); - } else if(this.sortedBy == 'lat99_desc') { - this.sortByMetricInDirection('desc', 'p99'); - } else if(this.sortedBy == 'lat995_asc') { - this.sortByMetricInDirection('asc', 'p995'); - } else if(this.sortedBy == 'lat995_desc') { - this.sortByMetricInDirection('desc', 'p995'); - } else if(this.sortedBy == 'latMean_asc') { - this.sortByMetricInDirection('asc', 'pMean'); - } else if(this.sortedBy == 'latMean_desc') { - this.sortByMetricInDirection('desc', 'pMean'); - } else if(this.sortedBy == 'latMedian_asc') { - this.sortByMetricInDirection('asc', 'pMedian'); - } else if(this.sortedBy == 'latMedian_desc') { - this.sortByMetricInDirection('desc', 'pMedian'); - } - } - - // default sort type and direction - this.sortedBy = 'alph_asc'; - - - // a temporary home for the logger until we become more sophisticated - function log(message) { - console.log(message); - }; - - function addCommas(nStr){ - nStr += ''; - if(nStr.length <=3) { - return nStr; //shortcut if we don't need commas - } - x = nStr.split('.'); - x1 = x[0]; - x2 = x.length > 1 ? '.' + x[1] : ''; - var rgx = /(\d+)(\d{3})/; - while (rgx.test(x1)) { - x1 = x1.replace(rgx, '$1' + ',' + '$2'); - } - return x1 + x2; - } + assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds"); + } + + function assertNotNull(data, key) { + if(data[key] == undefined) { + if (key == "dependencyOwner") { + data["dependencyOwner"] = data.name; + } else { + throw new Error("Key Missing: " + key + " for " + data.name) + } + } + } + + /** + * Method to display the THREAD_POOL data + * + * @param data + */ + /* private */ function displayThreadPool(data) { + + try { + preProcessData(data); + } catch (err) { + log("Failed preProcessData: " + err.message); + return; + } + + // add the 'addCommas' function to the 'data' object so the HTML templates can use it + data.addCommas = addCommas; + // add the 'roundNumber' function to the 'data' object so the HTML templates can use it + data.roundNumber = roundNumber; + + var addNew = false; + // check if we need to create the container + if(!$('#THREAD_POOL_' + data.name).length) { + // it doesn't exist so add it + var html = tmpl(htmlTemplateContainer, data); + // remove the loading thing first + $('#' + containerId + ' span.loading').remove(); + // get the current last column and remove the 'last' class from it + $('#' + containerId + ' div.last').removeClass('last'); + // now create the new data and add it + $('#' + containerId + '').append(html); + // add the 'last' class to the column we just added + $('#' + containerId + ' div.monitor').last().addClass('last'); + + // add the default sparkline graph + d3.selectAll('#graph_THREAD_POOL_' + data.name + ' svg').append("svg:path"); + + // remember this is new so we can trigger a sort after setting data + addNew = true; + } + + var ratePerSecondPerHost = roundNumber(data.rollingCountThreadsExecuted/data.reportingHosts); + + data.ratePerSecond = data.rollingCountThreadsExecuted; + data.ratePerSecondPerHost = ratePerSecondPerHost; + + // set the rate on the div element so it's available for sorting + $('#THREAD_POOL_' + data.name).attr('rate_value', data.ratePerSecondPerHost); + + // now update/insert the data + $('#THREAD_POOL_' + data.name + ' div.monitor_data').html(tmpl(htmlTemplate, data)); + + + // set variables for circle visualization + var rate = ratePerSecondPerHost; + // we will treat each item in queue as 1% of an error visualization + // ie. 5 threads in queue per instance == 5% error percentage + var errorPercentage = data.currentQueueSize / data.reportingHosts; + + updateCircle('#THREAD_POOL_' + data.name + ' circle', rate, errorPercentage); + + if(addNew) { + // sort since we added a new circuit + self.sortSameAsLast(); + } + } + + /* round a number to X digits: num => the number to round, dec => the number of decimals */ + /* private */ function roundNumber(num) { + var dec=1; // we are hardcoding to support only 1 decimal so that our padding logic at the end is simple + var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); + var resultAsString = result.toString(); + if(resultAsString.indexOf('.') == -1) { + resultAsString = resultAsString + '.'; + for(var i=0; i parseInt(maxXaxisForCircle)) { + newXaxisForCircle = maxXaxisForCircle; + } + var newYaxisForCircle = self.circleYaxis(rate); + if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) { + newYaxisForCircle = maxYaxisForCircle; + } + var newRadiusForCircle = self.circleRadius(rate); + if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) { + newRadiusForCircle = maxRadiusForCircle; + } + + d3.selectAll(cssTarget) + .transition() + .duration(400) + .attr("cy", newYaxisForCircle) + .attr("cx", newXaxisForCircle) + .attr("r", newRadiusForCircle) + .style("fill", self.colorRange(errorPercentage)); + } + + /* private */ function deleteThreadPool(circuitName) { + $('#THREAD_POOL_' + circuitName).remove(); + } + + } + + // public methods for sorting + HystrixThreadPoolMonitor.prototype.sortByVolume = function() { + var direction = "desc"; + if(this.sortedBy == 'rate_desc') { + direction = 'asc'; + } + this.sortByVolumeInDirection(direction); + } + + HystrixThreadPoolMonitor.prototype.sortByVolumeInDirection = function(direction) { + this.sortedBy = 'rate_' + direction; + $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'}); + } + + HystrixThreadPoolMonitor.prototype.sortAlphabetically = function() { + var direction = "asc"; + if(this.sortedBy == 'alph_asc') { + direction = 'desc'; + } + this.sortAlphabeticalInDirection(direction); + } + + HystrixThreadPoolMonitor.prototype.sortAlphabeticalInDirection = function(direction) { + this.sortedBy = 'alph_' + direction; + $('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction}); + } + + HystrixThreadPoolMonitor.prototype.sortByMetricInDirection = function(direction, metric) { + $('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction}); + } + + // this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose + HystrixThreadPoolMonitor.prototype.sortSameAsLast = function() { + if(this.sortedBy == 'alph_asc') { + this.sortAlphabeticalInDirection('asc'); + } else if(this.sortedBy == 'alph_desc') { + this.sortAlphabeticalInDirection('desc'); + } else if(this.sortedBy == 'rate_asc') { + this.sortByVolumeInDirection('asc'); + } else if(this.sortedBy == 'rate_desc') { + this.sortByVolumeInDirection('desc'); + } else if(this.sortedBy == 'error_asc') { + this.sortByErrorInDirection('asc'); + } else if(this.sortedBy == 'error_desc') { + this.sortByErrorInDirection('desc'); + } else if(this.sortedBy == 'lat90_asc') { + this.sortByMetricInDirection('asc', 'p90'); + } else if(this.sortedBy == 'lat90_desc') { + this.sortByMetricInDirection('desc', 'p90'); + } else if(this.sortedBy == 'lat99_asc') { + this.sortByMetricInDirection('asc', 'p99'); + } else if(this.sortedBy == 'lat99_desc') { + this.sortByMetricInDirection('desc', 'p99'); + } else if(this.sortedBy == 'lat995_asc') { + this.sortByMetricInDirection('asc', 'p995'); + } else if(this.sortedBy == 'lat995_desc') { + this.sortByMetricInDirection('desc', 'p995'); + } else if(this.sortedBy == 'latMean_asc') { + this.sortByMetricInDirection('asc', 'pMean'); + } else if(this.sortedBy == 'latMean_desc') { + this.sortByMetricInDirection('desc', 'pMean'); + } else if(this.sortedBy == 'latMedian_asc') { + this.sortByMetricInDirection('asc', 'pMedian'); + } else if(this.sortedBy == 'latMedian_desc') { + this.sortByMetricInDirection('desc', 'pMedian'); + } + } + + // default sort type and direction + this.sortedBy = 'alph_asc'; + + + // a temporary home for the logger until we become more sophisticated + function log(message) { + console.log(message); + }; + + function addCommas(nStr){ + nStr += ''; + if(nStr.length <=3) { + return nStr; //shortcut if we don't need commas + } + x = nStr.split('.'); + x1 = x[0]; + x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; + } })(window) - - diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPool.html b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPool.html index 5253e9e8..bfb41076 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPool.html +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPool.html @@ -1,33 +1,32 @@ -
+
- - + + -
+
-
-
Active
-
<%= currentActiveCount%>
+
+
Active
+
<%= currentActiveCount%>
-
Max Active
-
<%= addCommas(rollingMaxActiveThreads)%>
-
- -
-
Queued
-
<%= currentQueueSize %>
-
Executions
-
<%= addCommas(rollingCountThreadsExecuted)%>
-
-
-
Pool Size
-
<%= currentPoolSize %>
-
Queue Size
-
<%= propertyValue_queueSizeRejectionThreshold %>
-
+
Max Active
+
<%= addCommas(rollingMaxActiveThreads)%>
+
+
+
Queued
+
<%= currentQueueSize %>
+
Executions
+
<%= addCommas(rollingCountThreadsExecuted)%>
+
+
+
Pool Size
+
<%= currentPoolSize %>
+
Queue Size
+
<%= propertyValue_queueSizeRejectionThreshold %>
+
diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html index 5a923476..fa75109f 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html @@ -1,33 +1,33 @@
- <% - var displayName = name; - var toolTip = ""; - if(displayName.length > 32) { - displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length); - toolTip = "title=\"" + name + "\""; - } - %> + <% + var displayName = name; + var toolTip = ""; + if(displayName.length > 32) { + displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length); + toolTip = "title=\"" + name + "\""; + } + %> -
-

><%= displayName %>

-
-
-
+
+

><%= displayName %>

+
+
+
- +
diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/css/global.css b/runtime/src/main/resources/webroot/hystrix-dashboard/css/global.css index bb4c35c2..a61fa760 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/css/global.css +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/css/global.css @@ -1,69 +1,69 @@ @IMPORT url("/hystrix-dashboard/css/resets.css"); body { - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } img, object, embed { - max-width: 100%; + max-width: 100%; } img { - height: auto; + height: auto; } #header { - height: 36px; - margin-bottom: 0px; + height: 36px; + margin-bottom: 0px; } #header h2 { - float:left; - color: black; - position:relative; - padding-left: 20px; - top: 26px; - font-size: 20px; - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + float:left; + color: black; + position:relative; + padding-left: 20px; + top: 26px; + font-size: 20px; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } #header .header_nav { - position:absolute; - top:48px; - right:15px; + position:absolute; + top:48px; + right:15px; } #header .header_links { - float:left; - color: lightgray; - font-size: 18px; - top: 3px; - padding-left: 10px; - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + float:left; + color: lightgray; + font-size: 18px; + top: 3px; + padding-left: 10px; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } #header .header_links a { - color: white; + color: white; } #header .header_clusters { - float:left; - position:relative; - padding-left: 10px; - top: -1px; - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + float:left; + position:relative; + padding-left: 10px; + top: -1px; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } @media screen and (min-width: 1500px) { - #header .header_nav { - top:13px; - right:130px; - } + #header .header_nav { + top:13px; + right:130px; + } - #header { - height: 65px; - } + #header { + height: 65px; + } } diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/js/tmpl.js b/runtime/src/main/resources/webroot/hystrix-dashboard/js/tmpl.js index 3824fc83..b2f31759 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/js/tmpl.js +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/js/tmpl.js @@ -6,38 +6,38 @@ var cache = {}; window.tmpl = function tmpl(str, data) { - try { - // Figure out if we're getting a template, or if we need to - // load the template - and be sure to cache the result. - var fn = !/\W/.test(str) ? - cache[str] = cache[str] || - tmpl(document.getElementById(str).innerHTML) : + try { + // Figure out if we're getting a template, or if we need to + // load the template - and be sure to cache the result. + var fn = !/\W/.test(str) ? + cache[str] = cache[str] || + tmpl(document.getElementById(str).innerHTML) : - // Generate a reusable function that will serve as a template - // generator (and which will be cached). - new Function("obj", - "var p=[],print=function(){p.push.apply(p,arguments);};" + + // Generate a reusable function that will serve as a template + // generator (and which will be cached). + new Function("obj", + "var p=[],print=function(){p.push.apply(p,arguments);};" + - // Introduce the data as local variables using with(){} - "with(obj){p.push('" + + // Introduce the data as local variables using with(){} + "with(obj){p.push('" + - // Convert the template into pure JavaScript - str - .replace(/[\r\t\n]/g, " ") - .split("<%").join("\t") - .replace(/((^|%>)[^\t]*)'/g, "$1\r") - .replace(/\t=(.*?)%>/g, "',$1,'") - .split("\t").join("');") - .split("%>").join("p.push('") - .split("\r").join("\\'") - + "');}return p.join('');"); + // Convert the template into pure JavaScript + str + .replace(/[\r\t\n]/g, " ") + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)'/g, "$1\r") + .replace(/\t=(.*?)%>/g, "',$1,'") + .split("\t").join("');") + .split("%>").join("p.push('") + .split("\r").join("\\'") + + "');}return p.join('');"); - //console.log(fn); + //console.log(fn); - // Provide some basic currying to the user - return data ? fn(data) : fn; - }catch(e) { - console.log(e); - } + // Provide some basic currying to the user + return data ? fn(data) : fn; + }catch(e) { + console.log(e); + } }; })(window); diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.css b/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.css index b7f55518..5e538fec 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.css +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.css @@ -1,106 +1,106 @@ @IMPORT url("/hystrix-dashboard/css/resets.css"); .container { - padding-left: 0px; - padding-right: 20px; + padding-left: 0px; + padding-right: 20px; } .row { - width: 100%; - margin: 0 auto; - overflow: visible; + width: 100%; + margin: 0 auto; + overflow: visible; } .spacer { - width: 100%; - margin: 0 auto; - padding-top:4px; - clear:both; + width: 100%; + margin: 0 auto; + padding-top:4px; + clear:both; } .last { - margin-right: 0px; + margin-right: 0px; } .menubar { - overflow: hidden; - border-bottom: 1px solid black; + overflow: hidden; + border-bottom: 1px solid black; } .menubar div { - padding-bottom:5px; + padding-bottom:5px; - margin: 0 auto; - overflow: hidden; + margin: 0 auto; + overflow: hidden; - font-size: 80%; - font-family:'Bookman Old Style',Bookman,'URW Bookman L','Palatino Linotype',serif; + font-size: 80%; + font-family:'Bookman Old Style',Bookman,'URW Bookman L','Palatino Linotype',serif; - float:left; + float:left; } .menubar .title { - float: left; - padding-right: 20px; + float: left; + padding-right: 20px; - font-size: 110%; - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; - font-weight: bold; + font-size: 110%; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + font-weight: bold; - vertical-align: bottom; + vertical-align: bottom; } .menubar .menu_actions { - float: left; - position:relative; - top: 4px; + float: left; + position:relative; + top: 4px; } .menubar .menu_legend { - float: right; - position:relative; - top: 4px; + float: right; + position:relative; + top: 4px; } h3.sectionHeader { - color: black; - font-size: 110%; - padding-top: 4px; - padding-bottom: 4px; - padding-left: 8px; - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; - background: lightgrey; + color: black; + font-size: 110%; + padding-top: 4px; + padding-bottom: 4px; + padding-left: 8px; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + background: lightgrey; } .success { - color: green; + color: green; } .shortCircuited { - color: blue; + color: blue; } .latent { - color: #FFCC00; /* shade of dark yellow */ + color: #FFCC00; /* shade of dark yellow */ } .timeout { - color: #FF9900; /* shade of orange */ + color: #FF9900; /* shade of orange */ } .failure { - color: red; + color: red; } .rejected { - color: purple; + color: purple; } .exceptionsThrown { - color: brown; + color: brown; } @media screen and (max-width: 1100px) { - .container { - padding-left: 5px; - padding-right: 5px; - } + .container { + padding-left: 5px; + padding-right: 5px; + } } diff --git a/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.html b/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.html index 380a19be..a0bcbce1 100644 --- a/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.html +++ b/runtime/src/main/resources/webroot/hystrix-dashboard/monitor/monitor.html @@ -1,65 +1,65 @@ - - Hystrix Monitor - + + Hystrix Monitor + - - + + - - + + - - + + - - - - + + + + - - - + + + - - - + + + - - -
-
- -
-
Loading ...
- -
-
+ + +
+
+ +
+
Loading ...
+ +
+
@@ -73,121 +73,121 @@

-
+
+ /** + * Queue up the monitor to start once the page has finished loading. + * + * This is an inline script and expects to execute once on page load. + */ + + // commands + var hystrixMonitor = new HystrixCommandMonitor('dependencies', {includeDetailIcon:false}); + + var stream = "../../hystrix.stream"; + + if(stream != undefined) { + if(getUrlVars()["delay"] != undefined) { + stream = stream + "&delay=" + getUrlVars()["delay"]; + } + + var commandStream = stream; + var poolStream = stream; + + if(getUrlVars()["title"] != undefined) { + $('#title_name').html("Hystrix Stream: " + decodeURIComponent(getUrlVars()["title"])) + } else { + $('#title_name').html("Hystrix Stream: " + decodeURIComponent(stream)) + } + } + + $(window).load(function() { // within load with a setTimeout to prevent the infinite spinner + setTimeout(function() { + if(commandStream == undefined) { + console.log("commandStream is undefined") + $("#dependencies .loading").html("The 'stream' argument was not provided."); + $("#dependencies .loading").addClass("failed"); + } else { + // sort by error+volume by default + hystrixMonitor.sortByErrorThenVolume(); + + // start the EventSource which will open a streaming connection to the server + var source = new EventSource(commandStream); + + // add the listener that will process incoming events + source.addEventListener('message', hystrixMonitor.eventSourceMessageListener, false); + + // source.addEventListener('open', function(e) { + // console.console.log(">>> opened connection, phase: " + e.eventPhase); + // // Connection was opened. + // }, false); + + source.addEventListener('error', function(e) { + if (e.eventPhase == EventSource.CLOSED) { + // Connection was closed. + console.log("Connection was closed on error: " + e); + } else { + console.log("Error occurred while streaming: " + e); + } + }, false); + } + },0); + }); + + // thread pool + var dependencyThreadPoolMonitor = new HystrixThreadPoolMonitor('dependencyThreadPools'); + + $(window).load(function() { // within load with a setTimeout to prevent the infinite spinner + setTimeout(function() { + if(poolStream == undefined) { + console.log("poolStream is undefined") + $("#dependencyThreadPools .loading").html("The 'stream' argument was not provided."); + $("#dependencyThreadPools .loading").addClass("failed"); + } else { + dependencyThreadPoolMonitor.sortByVolume(); + + // start the EventSource which will open a streaming connection to the server + var source = new EventSource(poolStream); + + // add the listener that will process incoming events + source.addEventListener('message', dependencyThreadPoolMonitor.eventSourceMessageListener, false); + + // source.addEventListener('open', function(e) { + // console.console.log(">>> opened connection, phase: " + e.eventPhase); + // // Connection was opened. + // }, false); + + source.addEventListener('error', function(e) { + if (e.eventPhase == EventSource.CLOSED) { + // Connection was closed. + console.log("Connection was closed on error: " + e); + } else { + console.log("Error occurred while streaming: " + e); + } + }, false); + } + },0); + }); + + //Read a page's GET URL variables and return them as an associative array. + // from: http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html + function getUrlVars() + { + var vars = [], hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for(var i = 0; i < hashes.length; i++) + { + hash = hashes[i].split('='); + vars.push(hash[0]); + vars[hash[0]] = hash[1]; + } + return vars; + } + + diff --git a/runtime/src/main/resources/webroot/pages/dashboard.ftl b/runtime/src/main/resources/webroot/pages/dashboard.ftl index 2ea58ba0..152dc3b3 100644 --- a/runtime/src/main/resources/webroot/pages/dashboard.ftl +++ b/runtime/src/main/resources/webroot/pages/dashboard.ftl @@ -1,174 +1,174 @@ <#include "./../header.ftl"> - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
Loading ...
- -
-
- -
- -
-
Loading ...
-
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
Loading ...
+ +
+
+ +
+ +
+
Loading ...
+
+ /** + * Queue up the monitor to start once the page has finished loading. + * + * This is an inline script and expects to execute once on page load. + */ + + // commands + var hystrixMonitor = new HystrixCommandMonitor('dependencies', {includeDetailIcon:false}); + + var commandStream = "/stream/hystrix.stream.command.local"; + var poolStream = "/stream/hystrix.stream.tp.local"; + + $(window).load(function() { // within load with a setTimeout to prevent the infinite spinner + setTimeout(function() { + if(commandStream == undefined) { + console.log("commandStream is undefined") + $("#dependencies .loading").html("The 'stream' argument was not provided."); + $("#dependencies .loading").addClass("failed"); + } else { + // sort by error+volume by default + hystrixMonitor.sortByErrorThenVolume(); + + // start the EventSource which will open a streaming connection to the server + var source = new EventSource(commandStream); + + // add the listener that will process incoming events + source.addEventListener('message', hystrixMonitor.eventSourceMessageListener, false); + + source.addEventListener('open', function(e) { + console.log(">>> opened connection, phase: " + e.eventPhase); + // Connection was opened. + }, false); + + source.addEventListener('error', function(e) { + if (e.eventPhase == EventSource.CLOSED) { + // Connection was closed. + console.log("Connection was closed on error: " + e); + } else { + console.log("Error occurred while streaming: " + e); + } + }, false); + } + },0); + }); + + + // thread pool + var dependencyThreadPoolMonitor = new HystrixThreadPoolMonitor('dependencyThreadPools'); + $(window).load(function() { // within load with a setTimeout to prevent the infinite spinner + + setTimeout(function() { + if(poolStream == undefined) { + + alert("poolStream is undefined"); + console.log("poolStream is undefined") + $("#dependencyThreadPools .loading").html("The 'stream' argument was not provided."); + $("#dependencyThreadPools .loading").addClass("failed"); + } else { + + dependencyThreadPoolMonitor.sortByVolume(); + + // start the EventSource which will open a streaming connection to the server + var source = new EventSource(poolStream); + + // add the listener that will process incoming events + source.addEventListener('message', dependencyThreadPoolMonitor.eventSourceMessageListener, false); + + source.addEventListener('open', function(e) { + console.log(">>> opened connection, phase: " + e.eventPhase); + // Connection was opened. + }, false); + + source.addEventListener('error', function(e) { + if (e.eventPhase == EventSource.CLOSED) { + // Connection was closed. + console.log("Connection was closed on error: " + e); + } else { + console.log("Error occurred while streaming: " + e); + } + }, false); + + } + },0); + }); + + //Read a page's GET URL variables and return them as an associative array. + // from: http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html + function getUrlVars() + { + var vars = [], hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for(var i = 0; i < hashes.length; i++) + { + hash = hashes[i].split('='); + vars.push(hash[0]); + vars[hash[0]] = hash[1]; + } + return vars; + } + + <#include "./../footer.ftl"> diff --git a/runtime/src/main/resources/webroot/pages/footer.ftl b/runtime/src/main/resources/webroot/pages/footer.ftl index 2f2e879c..33fa46d3 100644 --- a/runtime/src/main/resources/webroot/pages/footer.ftl +++ b/runtime/src/main/resources/webroot/pages/footer.ftl @@ -1,13 +1,13 @@ - - - - - - - - - + + + + + + + + + diff --git a/runtime/src/main/resources/webroot/pages/header.ftl b/runtime/src/main/resources/webroot/pages/header.ftl index f2880d67..64d8a227 100644 --- a/runtime/src/main/resources/webroot/pages/header.ftl +++ b/runtime/src/main/resources/webroot/pages/header.ftl @@ -2,42 +2,42 @@ - <#if appName?? > ${appName} <#else> GJEX </#if> Admin + <#if appName?? > ${appName} <#else> GJEX </#if> Admin - - - - - + + + + + - - - + + + - - - - - + + + + + - - -
-