├── .gitattributes ├── .github └── workflows │ ├── gradle-basic-build.yml │ ├── gradle-basic.yml │ ├── gradle-build.yml │ ├── gradle-publish.yml │ └── gradle-wrapper-validation.yml ├── .gitignore ├── LICENSE ├── README.adoc ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── dev │ └── jacomet │ └── build │ └── FunctionalTestPlugin.kt ├── config └── HEADER.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── functionalTest └── groovy │ └── dev │ └── jacomet │ └── gradle │ └── plugins │ └── logging │ ├── AbstractLoggingCapabilitiesPluginFunctionalTest.groovy │ ├── LoggingCapabilitiesPluginDetectionFunctionalTest.groovy │ └── LoggingCapabilitiesPluginSelectionFunctionalTest.groovy ├── main └── java │ └── dev │ └── jacomet │ └── gradle │ └── plugins │ └── logging │ ├── LoggingCapabilitiesPlugin.java │ ├── LoggingModuleIdentifiers.java │ ├── actions │ ├── Slf4JEnforcementSubstitutionsUsing.java │ └── Slf4JEnforcementSubstitutionsWith.java │ ├── extension │ └── LoggingCapabilitiesExtension.java │ └── rules │ ├── CommonsLoggingImplementationRule.java │ ├── FixedCapabilityRule.java │ ├── Log4J2Alignment.java │ ├── Log4J2Implementation.java │ ├── Log4J2vsSlf4J.java │ ├── Slf4JAlignment.java │ ├── Slf4JImplementation.java │ ├── Slf4JVsJCL.java │ ├── Slf4JVsLog4J2ForJCL.java │ ├── Slf4JvsJUL.java │ ├── Slf4JvsLog4J.java │ ├── Slf4JvsLog4J2ForJUL.java │ ├── Slf4JvsLog4J2ForLog4J.java │ └── VersionedCapabilityRule.java └── test └── groovy └── dev └── jacomet └── gradle └── plugins └── logging ├── LoggingCapabilitiesPluginTest.groovy └── rules └── LoggingCapabilitiesRulesTest.groovy /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior to LF, because checkstyle enforces it 2 | * text eol=lf 3 | 4 | *.bat eol=crlf 5 | 6 | *.jar binary 7 | *.jpg binary 8 | *.png binary 9 | *.ttf binary 10 | -------------------------------------------------------------------------------- /.github/workflows/gradle-basic-build.yml: -------------------------------------------------------------------------------- 1 | name: Basic with Gradle action 2 | on: workflow_dispatch 3 | jobs: 4 | gradle: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: actions/setup-java@v3 9 | with: 10 | java-version: 8 11 | distribution: temurin 12 | 13 | - name: Setup Gradle 14 | uses: gradle/gradle-build-action@v2 15 | with: 16 | # Only write to the cache for builds on the 'master' branch. 17 | # Builds on other branches will only read existing entries from the cache. 18 | cache-read-only: ${{ github.ref != 'refs/heads/main' }} 19 | 20 | - name: Execute Gradle build 21 | run: ./gradlew build -------------------------------------------------------------------------------- /.github/workflows/gradle-basic.yml: -------------------------------------------------------------------------------- 1 | name: Basic 2 | on: workflow_dispatch 3 | jobs: 4 | gradle: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: actions/setup-java@v3 9 | with: 10 | java-version: 8 11 | distribution: temurin 12 | - name: Execute Gradle build 13 | run: ./gradlew build 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/gradle-build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [pull_request, push] 3 | jobs: 4 | gradle: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: actions/setup-java@v3 9 | with: 10 | java-version: 8 11 | distribution: temurin 12 | 13 | - name: Setup Gradle 14 | uses: gradle/gradle-build-action@v2 15 | with: 16 | # Only write to the cache for builds on the 'master' branch. 17 | # Builds on other branches will only read existing entries from the cache. 18 | cache-read-only: ${{ github.ref != 'refs/heads/main' }} 19 | 20 | - name: Execute Gradle build 21 | run: ./gradlew build 22 | 23 | test-matrix: 24 | needs: [gradle] 25 | runs-on: ubuntu-latest 26 | strategy: 27 | matrix: 28 | # Test around 6.6, 6.2, 6.0 and 5.2 breaking points, and the lower and upper boundaries 29 | # Version 6.6 is omitted as it's already tested above 30 | gradle: ["8.0.1", "7.4.2", "6.3", "6.2.2", "6.1.1", "6.0", "5.6.4", "5.3.1", "5.2.1", "5.1.1", "5.0"] 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: actions/setup-java@v3 34 | with: 35 | java-version: 8 36 | distribution: temurin 37 | 38 | - name: Setup Gradle 39 | uses: gradle/gradle-build-action@v2 40 | with: 41 | # Only write to the cache for builds on the 'main' branch. 42 | # Builds on other branches will only read existing entries from the cache. 43 | cache-read-only: ${{ github.ref != 'refs/heads/master' }} 44 | 45 | - name: Execute Gradle build 46 | run: ./gradlew functionalTest -Ptest.gradle-version=${{ matrix.gradle }} 47 | -------------------------------------------------------------------------------- /.github/workflows/gradle-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: workflow_dispatch 3 | jobs: 4 | gradle: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: actions/setup-java@v3 9 | with: 10 | java-version: 11 11 | distribution: temurin 12 | 13 | - name: Setup Gradle 14 | uses: gradle/gradle-build-action@v2 15 | with: 16 | # Only write to the cache for builds on the 'master' branch. 17 | # Builds on other branches will only read existing entries from the cache. 18 | cache-read-only: ${{ github.ref != 'refs/heads/master' }} 19 | 20 | - name: Execute Gradle build 21 | run: ./gradlew publish 22 | 23 | - name: Archive publication 24 | uses: actions/upload-artifact@v3 25 | with: 26 | name: plugin-publication 27 | path: build/repo/dev/jacomet/gradle/plugins/logging-capabilities 28 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | validation: 6 | name: "Validation" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: gradle/wrapper-validation-action@v1 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | 4 | # Ignore Gradle build output directory 5 | build/ 6 | 7 | # Ignore IJ files 8 | /.idea/ 9 | *.iml 10 | out/ 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2019 Louis Jacomet 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Logging Capabilities Gradle Plugin 2 | 3 | :tip-caption: :bulb: 4 | :note-caption: :information_source: 5 | :important-caption: :heavy_exclamation_mark: 6 | :caution-caption: :fire: 7 | :warning-caption: :warning: 8 | 9 | WARNING: This plugin has been merged with the GradleX https://github.com/gradlex-org/jvm-dependency-conflict-resolution[jvm-dependency-conflict-resolution] plugin. 10 | Please update to that other plugin and report issues there. 11 | I will follow up on the existing issues here and move the relevant ones to the new plugin. 12 | 13 | Ever seen this infamous Slf4J warning? 14 | 15 | [source] 16 | ---- 17 | SLF4J: Class path contains multiple SLF4J bindings. 18 | SLF4J: Found binding in [jar:file:.../slf4j-log4j12-1.7.29.jar!/org/slf4j/impl/StaticLoggerBinder.class] 19 | SLF4J: Found binding in [jar:file:.../logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class] 20 | SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. 21 | SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory] 22 | ---- 23 | 24 | Ever wondered how to make sure _all_ your dependencies' logging ends up in your Log4J 2 configuration? 25 | 26 | Then this plugin is for you! 27 | 28 | It is built on the https://docs.gradle.org/6.0.1/userguide/component_capabilities.html[Gradle capabilities feature], to detect and optionally select a Java logger implementation so that it is enforced at build time! 29 | 30 | == Plugin application and compatibility 31 | 32 | [source,kotlin] 33 | ---- 34 | plugins { 35 | id("dev.jacomet.logging-capabilities") version "0.11.0" 36 | } 37 | ---- 38 | 39 | Have a look at https://plugins.gradle.org/plugin/dev.jacomet.logging-capabilities[the documentation on the plugin portal] for alternate or DSL specific syntax. 40 | 41 | |=== 42 | | Gradle version | Available feature 43 | 44 | | Gradle 5.0+ 45 | | The detection part is available 46 | 47 | | Gradle 6.0+ 48 | | The configuration part is available 49 | 50 | | Gradle 8.0+ 51 | | All features available, requires version 0.11.0 or above of the plugin 52 | 53 | |=== 54 | 55 | == Detection of invalid logging configurations 56 | 57 | Upon application, this plugin defines a set of https://docs.gradle.org/6.0.1/userguide/component_metadata_rules.html#basics_of_writing_a_component_metadata_rule[component metadata rules] that add capabilities to relevant logging related dependencies. 58 | The added capabilities and impacted modules can be found in the table below: 59 | 60 | |=== 61 | | Capability | Impacted modules | Comment 62 | 63 | | `slf4j-impl` 64 | | `org.slf4j:slf4j-simple`, `org.slf4j:slf4j-log4j12`, `org.slf4j:slf4j-jcl`, `org.slf4j:slf4j-jdk14`, `ch.qos.logback:logback-classic`, `org.apache.logging.log4j:log4j-slf4j-impl`, `org.apache.logging.log4j:log4j-slf4j2-impl` 65 | | Represents an Slf4J binding 66 | 67 | | `log4j2-impl` 68 | | `org.apache.logging.log4j:log4j-slf4j-impl`, `org.apache.logging.log4j:log4j-slf4j2-impl`, `org.apache.logging.log4j:log4j-core` 69 | | Represents the native Log4J 2 implementation or delegation to Slf4J 70 | 71 | | `log4j2-vs-slf4j` 72 | | `org.apache.logging.log4j:log4j-slf4j-impl`, `org.apache.logging.log4j:log4j-to-slf4j` 73 | | Represents the Slf4J / Log4J 2 relationship: which one delegates to the other 74 | 75 | | `slf4j-vs-log4j` 76 | | `org.slf4j:log4j-over-slf4j`, `org.slf4j:slf4j-log4j12` 77 | | Represents the Slf4J / Log4J 1.2 relationship: either Slf4J intercepts or binds to Log4J 78 | 79 | | `slf4j-vs-log4j2-log4j` 80 | | `org.slf4j:log4j-over-slf4j`, `org.apache.logging.log4j:log4j-1.2-api`, `log4j:log4j` 81 | | Represents the available Log4J implementation: native, with Slf4J or with Log4J 2 82 | 83 | | `slf4j-vs-jul` 84 | | `org.slf4j:jul-to-slf4j`, `org.slf4j:slf4j-jdk14` 85 | | Represents the Slf4J / `java.util.logging` relationship: either Slf4 intercepts or binds to JUL 86 | 87 | | `slf4j-vs-log4j2-jul` 88 | | `org.slf4j:jul-to-slf4j`, `org.apache.logging.log4j:log4j-jul` 89 | | Represents JUL replacement: either with Slf4J or with Log4J 2 90 | 91 | | `commons-logging-impl` 92 | | `commons-logging:commons-logging`, `org.slf4j:jcl-over-slf4j`, `org.springframework:spring-jcl` 93 | | Represents Apache Commons Logging implementation: native or Slf4J 94 | 95 | | `slf4j-vs-jcl` 96 | | `org.slf4j:jcl-over-slf4j`, `org.slf4j:slf4j-jcl` 97 | | Represents the Slf4J / Apache Commons Logging relationship: either Slf4J intercepts or binds to `commons-logging` 98 | 99 | | `slf4j-vs-log4j2-jcl` 100 | | `org.slf4j:jcl-over-slf4j`, `org.apache.logging.log4j:log4j-jcl` 101 | | Represents the Slf4J or Log4J 2 interception of `commons-logging` 102 | 103 | |=== 104 | 105 | TIP: All capabilities are in the group `dev.jacomet.logging` 106 | 107 | With the set of capabilities defined above, all configuration resolutions in Gradle will fail if conflicting modules are found in the graph. 108 | 109 | == Alignment of logging libraries 110 | 111 | In addition to the capability setting and conflict detection, the plugin also registers https://docs.gradle.org/6.0.1/userguide/dependency_version_alignment.html#sec:align-versions-virtual[alignment rules] for Slf4J and Log4J 2 modules. 112 | 113 | Due to a bug in Gradle versions `[5.2, 6.2[`, alignment is disabled by default for these versions. 114 | Users with Gradle `[6.0, 6.2[` can opt back in with `loggingCapabilities.enableAlignment()`. 115 | Note that enabling alignment for these versions may cause some capabilities conflict to remain undetected. 116 | See https://github.com/ljacomet/logging-capabilities/issues/4[this issue] for details. 117 | 118 | == Expressing preference over a logging solution 119 | 120 | The plugin also contributes a project extension that allows to configure which logging solution to use in a declarative fashion. 121 | This solution is https://docs.gradle.org/6.0.1/userguide/dependency_capability_conflict.html#sub:selecting-between-candidates[based on APIs] introduced in Gradle 6.0. 122 | 123 | The extension is accessed as follows: 124 | 125 | [source,kotlin] 126 | ---- 127 | // Assuming the plugin has been applied 128 | loggingCapabilities { 129 | // Configuration goes here 130 | } 131 | ---- 132 | 133 | TIP: The different configuration options documented below do not _add_ dependencies. 134 | Make sure to have the expected dependency in your graph, either as a direct or transitive one. 135 | 136 | The plugin first provides a number of high-level, one stop solutions, for selecting a logging solution: 137 | 138 | |=== 139 | | Method | Documentation | Required dependency 140 | 141 | | `enforceLogback()` + 142 | `enforceLogback(String configurationName)` 143 | | This will configure all capabilities to resolve in favour of http://logback.qos.ch/[LOGBack] and route all alternative logging solutions through Slf4J. 144 | | `ch.qos.logback:logback-classic` 145 | 146 | | `enforceLog4J2()` + 147 | `enforceLog4J2(String configurationName)` 148 | | This will configure all capabilities to resolve in favour of http://logging.apache.org/log4j/2.x/[Log4J 2] and route all alternative logging solutions through Log4J 2. 149 | | `org.apache.logging.log4j:log4j-slf4j-impl` 150 | 151 | | `enforceSlf4JSimple()` + 152 | `enforceSlf4JSimple(String configurationName)` 153 | | This will configure all capabilities to resolve in favour of Slf4J simple and route all alternative logging solutions through Slf4J. 154 | | `org.slf4j:slf4j-simple` 155 | 156 | |=== 157 | 158 | TIP: The method without parameter will apply the setup to all dependency configuration, while the other one will limit the setup to the specified dependency configuration. 159 | 160 | If you want a finer grained control, the plugin provides lower level entry points for solving the different logging capability conflicts: 161 | |=== 162 | | Method | Accepted parameter values | Documentation 163 | 164 | | `selectSlf4JBinding(Object notation)` 165 | | Value must be an Slf4J binding implementation known by the plugin: `org.slf4j:slf4j-simple`, `org.slf4j:slf4j-log4j12`, `org.slf4j:slf4j-jcl`, `org.slf4j:slf4j-jdk14`, `ch.qos.logback:logback-classic` or `org.apache.logging.log4j:log4j-slf4j-impl` 166 | | Configures the provided Slf4J binding for selection, configuring related capabilities if needed 167 | 168 | | `selectSlf4JBinding(String configurationName, Object notation)` 169 | | A dependency configuration name, that `canBeResolved=true` + 170 | A notation as above 171 | | Configures the provided Slf4J binding for selection, configuring related capabilities if needed, only for the provided dependency configuration 172 | 173 | | `selectLog4J12Implementation(Object notation)` 174 | | Value must be a Log4J 1.2 implementation known by the plugin: `org.slf4j:log4j-over-slf4j`, `org.apache.logging.log4j:log4j-1.2-api`, `log4:log4j` or `org.slf4j:slf4j-log4j12` 175 | | Configures the provided Log4J 1.2 implementation for selection, configuring related capabilities if needed 176 | 177 | | `selectLog4J12Implementation(String configurationName, Object notation)` 178 | | A dependency configuration name, that `canBeResolved=true` + 179 | A notation as above 180 | | Configures the provided Log4J 1.2 implementation for selection, configuring related capabilities if needed, only for the provided dependency configuration 181 | 182 | | `selectJulDelegation(Object notation)` 183 | | Value must be a `java.util.logging` interceptor or binding known by the plugin: `org.slf4j:jul-to-slf4j`, `org.slf4j:slf4j-jdk14` or `org.apache.logging.log4j:log4j-jul` 184 | | Configures the provided JUL integration of binding for selection, configuring related capabilities if needed 185 | 186 | | `selectJulDelegation(String configurationName, Object notation)` 187 | | A dependency configuration name, that `canBeResolved=true` + 188 | A notation as above 189 | | Configures the provided JUL integration for selection, configuring related capabilities if needed, only for the provided dependency configuration 190 | 191 | | `selectJCLImplementation(Object notation)` 192 | | Value must be a Apache Commons Logging interceptor or binding known by the plugin: `org.slf4j:jcl-over-slf4j`, `commons-logging:commons-logging`, `org.slf4j:slf4j-jcl` or `org.apache.logging.log4j:log4j-jcl` 193 | | Configures the provided commons logging interceptor or binding for selection, configuring related capabilities if needed 194 | 195 | | `selectJCLImplementation(String configurationName, Object notation)` 196 | | A dependency configuration name, that `canBeResolved=true` + 197 | A notation as above 198 | | Configures the provided commons logging interceptor or binding for selection, configuring related capabilities if needed, only for the provided dependency configuration 199 | 200 | | `selectSlf4JLog4J2Interaction(Object notation)` 201 | | Value must be a Log4J 2 module for Slf4J interaction known by the plugin: `org.apache.logging.log4j:log4j-to-slf4j` or `org.apache.logging.log4j:log4j-slf4j-impl` 202 | | Configures the Log4J 2 / Slf4J integration, configuring related capabilities if needed 203 | 204 | | `selectSlf4JLog4J2Interaction(Sting configurationName, Object notation)` 205 | | A dependency configuration name, that `canBeResolved=true` + 206 | A notation as above 207 | | Configures the Log4J 2 / Slf4J integration, configuring related capabilities if needed, only for the provided dependency configuration 208 | 209 | |=== 210 | 211 | TIP: Notations above are those accepted by https://docs.gradle.org/6.0.1/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.html#org.gradle.api.artifacts.dsl.DependencyHandler:create(java.lang.Object)[`DependencyHandler.create(notation)`] in Gradle that resolves to an `ExternalDependency`. 212 | Most often this is a `group:name:version` `String`. 213 | 214 | == Building and reporting issues 215 | 216 | You will need a JDK 8+ to build this project. 217 | 218 | WARNING: This build is configured to publish build scans always. 219 | 220 | Use the GitHub issue tracker for reporting bugs and feature requests. 221 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import org.gradle.util.GradleVersion 18 | 19 | plugins { 20 | groovy 21 | id("com.gradle.plugin-publish") version "1.1.0" 22 | dev.jacomet.build.functional 23 | id("com.github.hierynomus.license") version "0.15.0" 24 | signing 25 | } 26 | 27 | repositories { 28 | mavenCentral() 29 | } 30 | 31 | group = "dev.jacomet.gradle.plugins" 32 | version = "0.12.0-dev" 33 | 34 | java { 35 | targetCompatibility = JavaVersion.VERSION_1_8 36 | sourceCompatibility = JavaVersion.VERSION_1_8 37 | withSourcesJar() 38 | withJavadocJar() 39 | } 40 | 41 | dependencies { 42 | implementation(gradleApi()) 43 | 44 | testImplementation("org.spockframework:spock-core:2.0-groovy-2.5") 45 | testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") 46 | } 47 | 48 | gradlePlugin { 49 | plugins { 50 | create("logging-capabilities") { 51 | id = "dev.jacomet.logging-capabilities" 52 | implementationClass = "dev.jacomet.gradle.plugins.logging.LoggingCapabilitiesPlugin" 53 | displayName = "Logging libraries capabilities" 54 | description = """Release notes: 55 | |* Fix issue with spring-jcl in enforcement (#36) 56 | """.trimMargin() 57 | } 58 | } 59 | } 60 | 61 | pluginBundle { 62 | website = "https://github.com/ljacomet/logging-capabilities" 63 | vcsUrl = "https://github.com/ljacomet/logging-capabilities.git" 64 | tags = listOf("dependency", "dependencies", "dependency-management", "logging", "slf4j", "log4j2") 65 | } 66 | 67 | publishing { 68 | repositories { 69 | maven { 70 | name = "local" 71 | url = uri(layout.buildDirectory.dir("repo")) 72 | } 73 | } 74 | } 75 | 76 | signing { 77 | useGpgCmd() 78 | } 79 | 80 | license { 81 | header = rootProject.file("config/HEADER.txt") 82 | strictCheck = true 83 | ignoreFailures = true 84 | mapping(mapOf( 85 | "java" to "SLASHSTAR_STYLE", 86 | "kt" to "SLASHSTAR_STYLE", 87 | "groovy" to "SLASHSTAR_STYLE", 88 | "kts" to "SLASHSTAR_STYLE" 89 | )) 90 | ext.set("year", "2019") 91 | exclude("**/build/*") 92 | exclude("**/.gradle/*") 93 | } 94 | 95 | tasks { 96 | withType().configureEach { 97 | useJUnitPlatform() 98 | } 99 | functionalTest { 100 | systemProperty("test.gradle-version", project.findProperty("test.gradle-version") ?: GradleVersion.current().version) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | plugins { 18 | `kotlin-dsl` 19 | `java-gradle-plugin` 20 | } 21 | 22 | repositories { 23 | jcenter() 24 | } 25 | 26 | dependencies { 27 | implementation(kotlin("gradle-plugin")) 28 | } 29 | 30 | gradlePlugin { 31 | plugins { 32 | register("functional") { 33 | id = "dev.jacomet.build.functional" 34 | implementationClass = "dev.jacomet.build.FunctionalTestPlugin" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/dev/jacomet/build/FunctionalTestPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.build 17 | 18 | import org.gradle.api.Plugin 19 | import org.gradle.api.Project 20 | import org.gradle.api.Task 21 | import org.gradle.api.plugins.JavaPluginConvention 22 | import org.gradle.api.tasks.testing.Test 23 | import org.gradle.kotlin.dsl.creating 24 | import org.gradle.kotlin.dsl.getValue 25 | import org.gradle.kotlin.dsl.getting 26 | import org.gradle.kotlin.dsl.the 27 | import org.gradle.plugin.devel.GradlePluginDevelopmentExtension 28 | 29 | class FunctionalTestPlugin : Plugin { 30 | override 31 | fun apply(project: Project) { 32 | val functionalTestSourceSet = project.the().sourceSets.create("functionalTest") { 33 | 34 | } 35 | project.the().testSourceSets(functionalTestSourceSet) 36 | project.configurations.getByName("functionalTestImplementation").extendsFrom(project.configurations.getByName("testImplementation")) 37 | 38 | val test by project.tasks.getting 39 | 40 | val functionalTest by project.tasks.creating(Test::class) { 41 | testClassesDirs = functionalTestSourceSet.output.classesDirs 42 | classpath = functionalTestSourceSet.runtimeClasspath 43 | shouldRunAfter(test) 44 | } 45 | 46 | val check by project.tasks.getting(Task::class) { 47 | // Run the functional tests as part of `check` 48 | dependsOn(functionalTest) 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /config/HEADER.txt: -------------------------------------------------------------------------------- 1 | Copyright ${year} the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.parallel=true 2 | org.gradle.caching=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljacomet/logging-capabilities/86a2d9003460aeb4c0385bc6a36104c556621966/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | plugins { 18 | id("com.gradle.enterprise").version("3.7.2") 19 | } 20 | 21 | rootProject.name = "logging-capabilities" 22 | 23 | gradleEnterprise { 24 | buildScan { 25 | termsOfServiceUrl = "https://gradle.com/terms-of-service" 26 | termsOfServiceAgree = "yes" 27 | 28 | publishAlways() 29 | } 30 | } -------------------------------------------------------------------------------- /src/functionalTest/groovy/dev/jacomet/gradle/plugins/logging/AbstractLoggingCapabilitiesPluginFunctionalTest.groovy: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging 2 | 3 | import org.gradle.testkit.runner.BuildResult 4 | import org.gradle.testkit.runner.GradleRunner 5 | import org.gradle.testkit.runner.TaskOutcome 6 | import org.gradle.util.GradleVersion 7 | import spock.lang.Specification 8 | import spock.lang.TempDir 9 | 10 | import java.nio.file.Path 11 | 12 | abstract class AbstractLoggingCapabilitiesPluginFunctionalTest extends Specification { 13 | 14 | static GradleVersion testGradleVersion = System.getProperty("test.gradle-version")?.with { GradleVersion.version(it) } ?: GradleVersion.current() 15 | 16 | @TempDir 17 | Path testFolder 18 | File buildFile 19 | 20 | def setup() { 21 | buildFile = testFolder.resolve('build.gradle.kts').toFile() 22 | testFolder.resolve('settings.gradle.kts').toFile() << 'rootProject.name = "test-project"' 23 | } 24 | 25 | TaskOutcome outcomeOf(BuildResult result, String path) { 26 | result.task(path)?.outcome 27 | } 28 | 29 | BuildResult build(List args) { 30 | gradleRunnerFor(args).build() 31 | } 32 | 33 | BuildResult buildAndFail(List args) { 34 | gradleRunnerFor(args).buildAndFail() 35 | } 36 | 37 | GradleRunner gradleRunnerFor(List args) { 38 | GradleRunner.create() 39 | .forwardOutput() 40 | .withGradleVersion(testGradleVersion.version) 41 | .withPluginClasspath() 42 | .withProjectDir(testFolder.toFile()) 43 | .withArguments(args + ["-s"]) 44 | } 45 | 46 | void withBuildScript(String content) { 47 | buildFile << content 48 | } 49 | 50 | void withBuildScriptWithDependencies(String... dependencies) { 51 | buildFile << """ 52 | plugins { 53 | `java-library` 54 | id("dev.jacomet.logging-capabilities") 55 | } 56 | 57 | repositories { 58 | mavenCentral() 59 | } 60 | 61 | dependencies { 62 | ${dependencies.collect { " implementation(\"$it\")" }.join("\n")} 63 | } 64 | 65 | tasks.register("doIt") { 66 | doLast { 67 | println(configurations["compileClasspath"].files) 68 | } 69 | } 70 | """ 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/functionalTest/groovy/dev/jacomet/gradle/plugins/logging/LoggingCapabilitiesPluginDetectionFunctionalTest.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging 17 | 18 | import org.gradle.util.GradleVersion 19 | import spock.lang.Requires 20 | import spock.lang.Unroll 21 | 22 | import static org.gradle.testkit.runner.TaskOutcome.FAILED 23 | 24 | class LoggingCapabilitiesPluginDetectionFunctionalTest extends AbstractLoggingCapabilitiesPluginFunctionalTest { 25 | 26 | boolean conflictOnCapability(String output, String capability) { 27 | // Error became lenient in Gradle 5.3, with a different error message: 28 | // https://github.com/gradle/gradle/commit/0c10062f9c86192b2568b0035ec8885c75b024cc 29 | return output.contains("conflict on capability '$capability'") || // Gradle >= 5.3 30 | output.contains("provide the same capability: $capability") // Gradle <= 5.2 31 | } 32 | 33 | @Unroll 34 | def "can detect Slf4J logger implementation conflicts with #first and #second"() { 35 | given: 36 | withBuildScriptWithDependencies(first, second) 37 | 38 | when: 39 | def result = buildAndFail(['doIt']) 40 | 41 | then: 42 | outcomeOf(result, ':doIt') == FAILED 43 | conflictOnCapability(result.output, "dev.jacomet.logging:slf4j-impl:1.0") 44 | 45 | where: 46 | first | second 47 | 'org.slf4j:slf4j-simple:1.7.27' | 'ch.qos.logback:logback-classic:1.2.3' 48 | 'org.slf4j:slf4j-simple:1.7.27' | 'org.slf4j:slf4j-log4j12:1.7.27' 49 | 'org.slf4j:slf4j-simple:1.7.27' | 'org.slf4j:slf4j-jcl:1.7.27' 50 | 'org.slf4j:slf4j-simple:1.7.27' | 'org.slf4j:slf4j-jdk14:1.7.27' 51 | 'org.slf4j:slf4j-simple:1.7.27' | 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.0' 52 | 'org.slf4j:slf4j-simple:1.7.27' | 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0' 53 | 54 | } 55 | 56 | @Unroll 57 | def "can detect Slf4J logger implementation / bridge implementation conflicts with #first and #second"() { 58 | given: 59 | withBuildScriptWithDependencies(first, second) 60 | 61 | when: 62 | def result = buildAndFail(['doIt']) 63 | 64 | then: 65 | outcomeOf(result, ':doIt') == FAILED 66 | conflictOnCapability(result.output, "dev.jacomet.logging:$capability:1.7.27") 67 | 68 | where: 69 | first | second | capability 70 | 'org.slf4j:jcl-over-slf4j:1.7.27' | 'org.slf4j:slf4j-jcl:1.7.27' | 'slf4j-vs-jcl' 71 | 'org.slf4j:jul-to-slf4j:1.7.27' | 'org.slf4j:slf4j-jdk14:1.7.27' | 'slf4j-vs-jul' 72 | 'org.slf4j:log4j-over-slf4j:1.7.27' | 'org.slf4j:slf4j-log4j12:1.7.27' | 'slf4j-vs-log4j' 73 | } 74 | 75 | @Unroll 76 | def "can detect Slf4J bridge implementations vs native logger implementations with #first and #second"() { 77 | given: 78 | withBuildScriptWithDependencies(first, second) 79 | 80 | when: 81 | def result = buildAndFail(['doIt']) 82 | 83 | then: 84 | outcomeOf(result, ':doIt') == FAILED 85 | conflictOnCapability(result.output, "dev.jacomet.logging:$capability:1.0") 86 | 87 | where: 88 | first | second | capability 89 | 'org.slf4j:jcl-over-slf4j:1.7.27' | 'commons-logging:commons-logging:1.2' | 'commons-logging-impl' 90 | 'org.springframework:spring-jcl:5.3.9' | 'commons-logging:commons-logging:1.2' | 'commons-logging-impl' 91 | 'org.slf4j:log4j-over-slf4j:1.7.27' | 'log4j:log4j:1.2.9' | 'slf4j-vs-log4j2-log4j' 92 | 'org.slf4j:log4j-over-slf4j:1.7.27' | 'org.apache.logging.log4j:log4j-1.2-api:2.17.0' | 'slf4j-vs-log4j2-log4j' 93 | 'org.slf4j:slf4j-log4j12:1.7.27' | 'org.apache.logging.log4j:log4j-1.2-api:2.17.0' | 'slf4j-vs-log4j2-log4j' 94 | 'org.apache.logging.log4j:log4j-1.2-api:2.17.0' | 'org.slf4j:slf4j-log4j12:1.7.27' | 'slf4j-vs-log4j2-log4j' 95 | } 96 | 97 | @Unroll 98 | def "can detect Log4J2 logger implementation / bridge implementation conflict with #bridge"() { 99 | given: 100 | withBuildScriptWithDependencies(bridge, 'org.apache.logging.log4j:log4j-to-slf4j:2.20.0') 101 | 102 | when: 103 | def result = buildAndFail(['doIt']) 104 | 105 | then: 106 | outcomeOf(result, ':doIt') == FAILED 107 | conflictOnCapability(result.output, "dev.jacomet.logging:log4j2-vs-slf4j:2.20.0") 108 | 109 | where: 110 | bridge << ['org.apache.logging.log4j:log4j-slf4j-impl:2.20.0', 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0'] 111 | } 112 | 113 | def "can detect Log4J2 logger implementation conflict"() { 114 | given: 115 | withBuildScriptWithDependencies('org.apache.logging.log4j:log4j-core:2.17.0', 'org.apache.logging.log4j:log4j-to-slf4j:2.17.0') 116 | 117 | when: 118 | def result = buildAndFail(['doIt']) 119 | 120 | then: 121 | outcomeOf(result, ':doIt') == FAILED 122 | conflictOnCapability(result.output, "dev.jacomet.logging:log4j2-impl:2.17.0") 123 | } 124 | 125 | @Unroll 126 | def "can detect conflicting bridge implementations from Slf4J and Log4J2 with #first and #second"() { 127 | given: 128 | withBuildScriptWithDependencies(first, second) 129 | 130 | when: 131 | def result = buildAndFail(['doIt']) 132 | 133 | then: 134 | outcomeOf(result, ':doIt') == FAILED 135 | conflictOnCapability(result.output, "dev.jacomet.logging:$capability:1.0") 136 | 137 | where: 138 | first | second | capability 139 | 'org.slf4j:jul-to-slf4j:1.7.27' | 'org.apache.logging.log4j:log4j-jul:2.17.0' | 'slf4j-vs-log4j2-jul' 140 | 'org.slf4j:jcl-over-slf4j:1.7.27' | 'org.apache.logging.log4j:log4j-jcl:2.17.0' | 'slf4j-vs-log4j2-jcl' 141 | } 142 | 143 | @Requires({ instance.testGradleVersion >= GradleVersion.version("6.0") }) 144 | def "provides alignment on Slf4J"() { 145 | given: 146 | withBuildScriptWithDependencies("org.slf4j:slf4j-simple:1.7.25", "org.slf4j:slf4j-api:1.7.27") 147 | withBuildScript(""" 148 | loggingCapabilities { 149 | enableAlignment() 150 | } 151 | """) 152 | 153 | when: 154 | def result = build(['doIt']) 155 | 156 | then: 157 | result.output.contains("slf4j-simple-1.7.27.jar") 158 | } 159 | 160 | @Requires({ instance.testGradleVersion >= GradleVersion.version("6.0") }) 161 | def "provides alignment on Log4J 2"() { 162 | given: 163 | withBuildScriptWithDependencies("org.apache.logging.log4j:log4j-to-slf4j:2.17.0", "org.apache.logging.log4j:log4j-api:2.16.0") 164 | withBuildScript(""" 165 | loggingCapabilities { 166 | enableAlignment() 167 | } 168 | """) 169 | 170 | when: 171 | def result = build(['doIt']) 172 | 173 | then: 174 | result.output.contains("log4j-to-slf4j-2.17.0.jar") 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/functionalTest/groovy/dev/jacomet/gradle/plugins/logging/LoggingCapabilitiesPluginSelectionFunctionalTest.groovy: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging 2 | 3 | import org.gradle.util.GradleVersion 4 | import spock.lang.IgnoreIf 5 | import spock.lang.Requires 6 | import spock.lang.Unroll 7 | 8 | import static org.gradle.testkit.runner.TaskOutcome.FAILED 9 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS 10 | 11 | @Requires({ LoggingCapabilitiesPluginSelectionFunctionalTest.testGradleVersion >= GradleVersion.version("6.0") }) 12 | class LoggingCapabilitiesPluginSelectionFunctionalTest extends AbstractLoggingCapabilitiesPluginFunctionalTest { 13 | 14 | @Unroll 15 | // Looks like a regression in Gradle 6.7+: https://github.com/ljacomet/logging-capabilities/issues/20 16 | @IgnoreIf({ instance.testGradleVersion >= GradleVersion.version("6.7") }) 17 | def "can select logback in case of conflict (with extra #additional)"() { 18 | given: 19 | withBuildScript(""" 20 | plugins { 21 | `java-library` 22 | id("dev.jacomet.logging-capabilities") 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | } 28 | 29 | loggingCapabilities { 30 | selectSlf4JBinding("ch.qos.logback:logback-classic:1.2.3") 31 | } 32 | 33 | dependencies { 34 | runtimeOnly("org.slf4j:slf4j-simple:1.7.27") 35 | ${additional.collect { " runtimeOnly(\"$it\")" }.join("\n")} 36 | runtimeOnly("ch.qos.logback:logback-classic:1.2.3") 37 | } 38 | 39 | tasks.register("doIt") { 40 | doLast { 41 | println(configurations["runtimeClasspath"].files) 42 | } 43 | } 44 | """) 45 | when: 46 | def result = build(['doIt']) 47 | 48 | then: 49 | outcomeOf(result, ':doIt') == SUCCESS 50 | result.output.contains("logback-classic-1.2.3.jar") 51 | 52 | where: 53 | additional << [[], ["org.slf4j:slf4j-log4j12:1.7.27"], ["org.slf4j:slf4j-jcl:1.7.27", "org.slf4j:slf4j-log4j12:1.7.27"]] 54 | } 55 | 56 | def "fail to select logback in case of conflict but logback not available"() { 57 | given: 58 | withBuildScript(""" 59 | plugins { 60 | `java-library` 61 | id("dev.jacomet.logging-capabilities") 62 | } 63 | 64 | repositories { 65 | mavenCentral() 66 | } 67 | 68 | loggingCapabilities { 69 | selectSlf4JBinding("ch.qos.logback:logback-classic:1.2.3") 70 | } 71 | 72 | 73 | dependencies { 74 | runtimeOnly("org.slf4j:slf4j-simple:1.7.27") 75 | runtimeOnly("org.slf4j:slf4j-log4j12:1.7.27") 76 | } 77 | 78 | tasks.register("doIt") { 79 | doLast { 80 | println(configurations["runtimeClasspath"].files) 81 | } 82 | } 83 | """) 84 | when: 85 | def result = buildAndFail(['doIt']) 86 | 87 | then: 88 | outcomeOf(result, ':doIt') == FAILED 89 | result.output.contains("conflict on capability 'dev.jacomet.logging:slf4j-impl:1.0'") 90 | } 91 | 92 | @Unroll 93 | def "configuring logger leaves no open conflicts (using #prefered)"() { 94 | given: 95 | withBuildScript(""" 96 | plugins { 97 | `java-library` 98 | id("dev.jacomet.logging-capabilities") 99 | } 100 | 101 | repositories { 102 | mavenCentral() 103 | } 104 | 105 | loggingCapabilities { 106 | $prefered 107 | } 108 | 109 | dependencies { 110 | implementation("org.slf4j:slf4j-api:1.7.27") 111 | implementation("org.apache.logging.log4j:log4j-api:2.12.1") 112 | 113 | implementation("log4j:log4j:1.2.17") 114 | implementation("commons-logging:commons-logging:1.2") 115 | 116 | runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:2.12.1") 117 | runtimeOnly("ch.qos.logback:logback-classic:1.2.3") 118 | runtimeOnly("org.slf4j:slf4j-simple:1.7.27") 119 | runtimeOnly("org.slf4j:slf4j-log4j12:1.7.27") 120 | runtimeOnly("org.slf4j:log4j-over-slf4j:1.7.27") 121 | runtimeOnly("org.slf4j:jul-to-slf4j:1.7.27") 122 | runtimeOnly("org.slf4j:slf4j-jdk14:1.7.27") 123 | runtimeOnly("org.slf4j:slf4j-jcl:1.7.27") 124 | runtimeOnly("org.slf4j:jcl-over-slf4j:1.7.27") 125 | 126 | runtimeOnly("org.apache.logging.log4j:log4j-jul:2.12.1") 127 | runtimeOnly("org.apache.logging.log4j:log4j-1.2-api:2.12.1") 128 | runtimeOnly("org.apache.logging.log4j:log4j-to-slf4j:2.12.1") 129 | runtimeOnly("org.apache.logging.log4j:log4j-jcl:2.12.1") 130 | } 131 | 132 | tasks.register("doIt") { 133 | doLast { 134 | println(configurations["runtimeClasspath"].files) 135 | } 136 | } 137 | """) 138 | when: 139 | def result = build(['doIt']) 140 | 141 | then: 142 | outcomeOf(result, ':doIt') == SUCCESS 143 | result.output.contains(selected) 144 | 145 | where: 146 | prefered | selected 147 | "enforceLogback()" | "logback-classic-1.2.3.jar" 148 | "enforceSlf4JSimple()" | "slf4j-simple-1.7.27.jar" 149 | "enforceLog4J2()" | "log4j-slf4j-impl-2.12.1.jar" 150 | } 151 | 152 | @Unroll 153 | def "Enforcing a logger leaves no open conflict when enforcing #enforced and #extra is present"() { 154 | given: 155 | withBuildScript(""" 156 | plugins { 157 | `java-library` 158 | id("dev.jacomet.logging-capabilities") 159 | } 160 | 161 | repositories { 162 | mavenCentral() 163 | } 164 | 165 | loggingCapabilities { 166 | ${loggerFrom(enforced)} 167 | } 168 | 169 | dependencies { 170 | runtimeOnly("$enforced") 171 | runtimeOnly("$extra") 172 | } 173 | 174 | tasks.register("doIt") { 175 | doLast { 176 | println(configurations["runtimeClasspath"].files) 177 | } 178 | } 179 | """) 180 | when: 181 | def result = build(['doIt']) 182 | 183 | then: 184 | outcomeOf(result, ':doIt') == SUCCESS 185 | 186 | where: 187 | [enforced, extra] << [["ch.qos.logback:logback-classic:1.2.3", "org.slf4j:slf4j-simple:1.7.27", "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1"], 188 | ["log4j:log4j:1.2.17", "commons-logging:commons-logging:1.2", "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1", "ch.qos.logback:logback-classic:1.2.3", 189 | "org.slf4j:slf4j-simple:1.7.27", "org.slf4j:slf4j-log4j12:1.7.27", "org.slf4j:log4j-over-slf4j:1.7.27", "org.slf4j:jul-to-slf4j:1.7.27", "org.slf4j:slf4j-jdk14:1.7.27", 190 | "org.slf4j:slf4j-jcl:1.7.27", "org.slf4j:jcl-over-slf4j:1.7.27", "org.apache.logging.log4j:log4j-jul:2.12.1", "org.apache.logging.log4j:log4j-1.2-api:2.12.1", 191 | "org.apache.logging.log4j:log4j-to-slf4j:2.12.1", "org.apache.logging.log4j:log4j-jcl:2.12.1", "org.apache.logging.log4j:log4j-core:2.12.1"]].combinations() 192 | } 193 | 194 | String loggerFrom(String enforced) { 195 | if (enforced.contains('logback-classic')) { 196 | return 'enforceLogback()' 197 | } else if (enforced.contains('slf4j-simple')) { 198 | return 'enforceSlf4JSimple()' 199 | } else if (enforced.contains('log4j-slf4j-impl')) { 200 | return 'enforceLog4J2()' 201 | } 202 | throw new IllegalArgumentException("Unexpected value: $enforced") 203 | } 204 | 205 | 206 | def "can enforce logback and commons-logging is substituted"() { 207 | withBuildScript(""" 208 | plugins { 209 | `java-library` 210 | id("dev.jacomet.logging-capabilities") 211 | } 212 | 213 | repositories { 214 | mavenCentral() 215 | } 216 | 217 | loggingCapabilities { 218 | enforceLogback() 219 | enableAlignment() 220 | } 221 | 222 | dependencies { 223 | implementation("org.slf4j:slf4j-api:1.7.27") 224 | 225 | implementation("commons-logging:commons-logging:1.2") 226 | 227 | runtimeOnly("ch.qos.logback:logback-classic:1.2.3") 228 | } 229 | 230 | tasks.register("doIt") { 231 | doLast { 232 | println(configurations["runtimeClasspath"].files) 233 | } 234 | } 235 | """) 236 | when: 237 | def result = build(['doIt']) 238 | 239 | then: 240 | outcomeOf(result, ':doIt') == SUCCESS 241 | !result.output.contains("commons-logging-1.2.jar") 242 | result.output.contains("jcl-over-slf4j-1.7.27.jar") 243 | } 244 | 245 | def "can enforce logback and spring-jcl is substituted"() { 246 | withBuildScript(""" 247 | plugins { 248 | `java-library` 249 | id("dev.jacomet.logging-capabilities") 250 | } 251 | 252 | repositories { 253 | mavenCentral() 254 | } 255 | 256 | loggingCapabilities { 257 | enforceLogback() 258 | enableAlignment() 259 | } 260 | 261 | dependencies { 262 | implementation("org.slf4j:slf4j-api:1.7.27") 263 | 264 | implementation("org.springframework:spring-jcl:5.3.9") 265 | 266 | runtimeOnly("ch.qos.logback:logback-classic:1.2.3") 267 | } 268 | 269 | tasks.register("doIt") { 270 | doLast { 271 | println(configurations["runtimeClasspath"].files) 272 | } 273 | } 274 | """) 275 | when: 276 | def result = build(['doIt']) 277 | 278 | then: 279 | outcomeOf(result, ':doIt') == SUCCESS 280 | !result.output.contains("spring-jcl-5.3.9.jar") 281 | result.output.contains("jcl-over-slf4j-1.7.27.jar") 282 | } 283 | 284 | def "can enforce different loggers in runtime and test"() { 285 | given: 286 | withBuildScript(""" 287 | plugins { 288 | `java-library` 289 | id("dev.jacomet.logging-capabilities") 290 | } 291 | 292 | repositories { 293 | mavenCentral() 294 | } 295 | 296 | loggingCapabilities { 297 | enforceLog4J2("runtimeClasspath") 298 | enforceSlf4JSimple("testRuntimeClasspath") 299 | } 300 | 301 | dependencies { 302 | implementation("org.slf4j:slf4j-api:1.7.27") 303 | implementation("org.apache.logging.log4j:log4j-api:2.12.1") 304 | 305 | implementation("log4j:log4j:1.2.17") 306 | implementation("commons-logging:commons-logging:1.2") 307 | 308 | runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:2.12.1") 309 | runtimeOnly("ch.qos.logback:logback-classic:1.2.3") 310 | runtimeOnly("org.slf4j:slf4j-simple:1.7.27") 311 | runtimeOnly("org.slf4j:slf4j-log4j12:1.7.27") 312 | runtimeOnly("org.slf4j:log4j-over-slf4j:1.7.27") 313 | runtimeOnly("org.slf4j:jul-to-slf4j:1.7.27") 314 | runtimeOnly("org.slf4j:slf4j-jdk14:1.7.27") 315 | runtimeOnly("org.slf4j:slf4j-jcl:1.7.27") 316 | runtimeOnly("org.slf4j:jcl-over-slf4j:1.7.27") 317 | 318 | runtimeOnly("org.apache.logging.log4j:log4j-jul:2.12.1") 319 | runtimeOnly("org.apache.logging.log4j:log4j-1.2-api:2.12.1") 320 | runtimeOnly("org.apache.logging.log4j:log4j-to-slf4j:2.12.1") 321 | runtimeOnly("org.apache.logging.log4j:log4j-jcl:2.12.1") 322 | } 323 | 324 | tasks.register("doIt") { 325 | doLast { 326 | println(configurations["runtimeClasspath"].files) 327 | println(configurations["testRuntimeClasspath"].files) 328 | } 329 | } 330 | """) 331 | when: 332 | def result = build(['doIt']) 333 | 334 | then: 335 | outcomeOf(result, ':doIt') == SUCCESS 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/LoggingCapabilitiesPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging; 17 | 18 | import dev.jacomet.gradle.plugins.logging.extension.LoggingCapabilitiesExtension; 19 | import dev.jacomet.gradle.plugins.logging.rules.CommonsLoggingImplementationRule; 20 | import dev.jacomet.gradle.plugins.logging.rules.Log4J2Alignment; 21 | import dev.jacomet.gradle.plugins.logging.rules.Log4J2Implementation; 22 | import dev.jacomet.gradle.plugins.logging.rules.Log4J2vsSlf4J; 23 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JAlignment; 24 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JImplementation; 25 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JVsJCL; 26 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JVsLog4J2ForJCL; 27 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JvsJUL; 28 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JvsLog4J; 29 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JvsLog4J2ForJUL; 30 | import dev.jacomet.gradle.plugins.logging.rules.Slf4JvsLog4J2ForLog4J; 31 | import org.gradle.api.Plugin; 32 | import org.gradle.api.Project; 33 | import org.gradle.api.artifacts.dsl.DependencyHandler; 34 | import org.gradle.util.GradleVersion; 35 | 36 | public class LoggingCapabilitiesPlugin implements Plugin { 37 | 38 | public static final GradleVersion GRADLE_7_0 = GradleVersion.version("7.0"); 39 | private static final GradleVersion GRADLE_6_2 = GradleVersion.version("6.2"); 40 | private static final GradleVersion GRADLE_6 = GradleVersion.version("6.0"); 41 | private static final GradleVersion GRADLE_5_2 = GradleVersion.version("5.2"); 42 | 43 | @Override 44 | public void apply(Project project) { 45 | DependencyHandler dependencies = project.getDependencies(); 46 | GradleVersion gradleVersion = GradleVersion.current(); 47 | if (gradleVersion.compareTo(GRADLE_6) >= 0) { 48 | // Only add the extension for Gradle 6 and above 49 | project.getExtensions().create("loggingCapabilities", LoggingCapabilitiesExtension.class, project.getConfigurations(), dependencies, getAlignmentActivation(dependencies, gradleVersion)); 50 | } 51 | configureCommonsLogging(dependencies); 52 | configureJavaUtilLogging(dependencies); 53 | configureLog4J(dependencies); 54 | configureSlf4J(dependencies); 55 | configureLog4J2(dependencies); 56 | configureLog4J2Implementation(dependencies); 57 | 58 | // ljacomet/logging-capabilities#4 59 | if (gradleVersion.compareTo(GRADLE_5_2) < 0 || gradleVersion.compareTo(GRADLE_6_2) >= 0) { 60 | configureAlignment(dependencies); 61 | } 62 | } 63 | 64 | private Runnable getAlignmentActivation(DependencyHandler dependencies, GradleVersion gradleVersion) { 65 | if (gradleVersion.compareTo(GRADLE_6_2) < 0) { 66 | return () -> configureAlignment(dependencies); 67 | } 68 | return () -> {}; 69 | } 70 | 71 | private void configureAlignment(DependencyHandler dependencies) { 72 | dependencies.components(handler -> { 73 | handler.all(Slf4JAlignment.class); 74 | handler.all(Log4J2Alignment.class); 75 | }); 76 | } 77 | 78 | /** 79 | * Log4J2 can act as an Slf4J implementation with `log4j-slf4j-impl` or `log4j-slf4j2-impl`. 80 | * It can also delegate to Slf4J with `log4j-to-slf4j`. 81 | *

82 | * Given the above: 83 | * * `log4j-slf4j-impl`, `log4j-slf4j2-impl` and `log4j-to-slf4j` are exclusive 84 | */ 85 | private void configureLog4J2(DependencyHandler dependencies) { 86 | dependencies.components(handler -> { 87 | handler.withModule(LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.moduleId, Log4J2vsSlf4J.class); 88 | handler.withModule(LoggingModuleIdentifiers.LOG4J_SLF4J2_IMPL.moduleId, Log4J2vsSlf4J.class); 89 | handler.withModule(LoggingModuleIdentifiers.LOG4J_TO_SLF4J.moduleId, Log4J2vsSlf4J.class); 90 | }); 91 | } 92 | 93 | /** 94 | * Log4J2 has its own implementation with `log4j-core`. 95 | * It can also delegate to Slf4J with `log4j-to-slf4j`. 96 | *

97 | * Given the above: 98 | * * `log4j-core` and `log4j-to-slf4j` are exclusive 99 | */ 100 | private void configureLog4J2Implementation(DependencyHandler dependencies) { 101 | dependencies.components(handler -> { 102 | handler.withModule(LoggingModuleIdentifiers.LOG4J_TO_SLF4J.moduleId, Log4J2Implementation.class); 103 | handler.withModule(LoggingModuleIdentifiers.LOG4J_CORE.moduleId, Log4J2Implementation.class); 104 | }); 105 | } 106 | 107 | /** 108 | * Slf4J provides an API, which requires an implementation. 109 | * Only one implementation can be on the classpath, selected between: 110 | * * `slf4j-simple` 111 | * * `logback-classic` 112 | * * `slf4j-log4j12` to use Log4J 1.2 113 | * * `slf4j-jcl` to use Jakarta Commons Logging 114 | * * `slf4j-jdk14` to use Java Util Logging 115 | * * `log4j-slf4j-impl` to use Log4J2 116 | */ 117 | private void configureSlf4J(DependencyHandler dependencies) { 118 | dependencies.components(handler -> { 119 | handler.withModule(LoggingModuleIdentifiers.SLF4J_SIMPLE.moduleId, Slf4JImplementation.class); 120 | handler.withModule(LoggingModuleIdentifiers.LOGBACK_CLASSIC.moduleId, Slf4JImplementation.class); 121 | handler.withModule(LoggingModuleIdentifiers.SLF4J_LOG4J12.moduleId, Slf4JImplementation.class); 122 | handler.withModule(LoggingModuleIdentifiers.SLF4J_JCL.moduleId, Slf4JImplementation.class); 123 | handler.withModule(LoggingModuleIdentifiers.SLF4J_JDK14.moduleId, Slf4JImplementation.class); 124 | handler.withModule(LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.moduleId, Slf4JImplementation.class); 125 | handler.withModule(LoggingModuleIdentifiers.LOG4J_SLF4J2_IMPL.moduleId, Slf4JImplementation.class); 126 | }); 127 | } 128 | 129 | /** 130 | * `log4j:log4j` can be replaced by: 131 | * * Slf4j with `log4j-over-slf4j` 132 | * * Log4J2 with `log4j-1.2-api` 133 | *

134 | * Log4J can be used from: 135 | * * Slf4J API delegating to it with `slf4j-log4j12` 136 | * * Log4J2 API only through Slf4J delegation 137 | *

138 | * Given the above: 139 | * * `log4j-over-slf4j` and `slf4j-log4j12` are exclusive 140 | * * `log4j-over-slf4j` and `log4j-1.2-api` and `log4j` are exclusive 141 | */ 142 | private void configureLog4J(DependencyHandler dependencies) { 143 | dependencies.components(handler -> { 144 | handler.withModule(LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.moduleId, Slf4JvsLog4J.class); 145 | handler.withModule(LoggingModuleIdentifiers.SLF4J_LOG4J12.moduleId, Slf4JvsLog4J.class); 146 | 147 | handler.withModule(LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.moduleId, Slf4JvsLog4J2ForLog4J.class); 148 | handler.withModule(LoggingModuleIdentifiers.LOG4J12API.moduleId, Slf4JvsLog4J2ForLog4J.class); 149 | handler.withModule(LoggingModuleIdentifiers.LOG4J.moduleId, Slf4JvsLog4J2ForLog4J.class); 150 | }); 151 | } 152 | 153 | /** 154 | * Java Util Logging can be replaced by: 155 | * * Slf4J with `jul-to-slf4j` 156 | * * Log4J2 with `log4j-jul` 157 | *

158 | * Java Util Logging can be used from: 159 | * * Slf4J API delegating to it with `slf4j-jdk14` 160 | * * Log4J2 API only through SLF4J delegation 161 | *

162 | * Given the above: 163 | * * `jul-to-slf4j` and `slf4j-jdk14` are exclusive 164 | * * `jul-to-slf4j` and `log4j-jul` are exclusive 165 | */ 166 | private void configureJavaUtilLogging(DependencyHandler dependencies) { 167 | dependencies.components( handler -> { 168 | handler.withModule(LoggingModuleIdentifiers.JUL_TO_SLF4J.moduleId, Slf4JvsJUL.class); 169 | handler.withModule(LoggingModuleIdentifiers.SLF4J_JDK14.moduleId, Slf4JvsJUL.class); 170 | 171 | handler.withModule(LoggingModuleIdentifiers.JUL_TO_SLF4J.moduleId, Slf4JvsLog4J2ForJUL.class); 172 | handler.withModule(LoggingModuleIdentifiers.LOG4J_JUL.moduleId, Slf4JvsLog4J2ForJUL.class); 173 | }); 174 | } 175 | 176 | /** 177 | * `commons-logging:commons-logging` can be replaced by: 178 | * * Slf4J with `org.slf4j:jcl-over-slf4j` 179 | * * Log4J2 with `org.apache.logging.log4j:log4j-jcl` _which requires `commons-logging`_ 180 | * * Spring JCL with `org.springframework:spring-jcl` 181 | *

182 | * `commons-logging:commons-logging` can be used from: 183 | * * Slf4J API delegating to it with `org.slf4j:slf4j-jcl` 184 | * * Log4J2 API only through Slf4J delegation 185 | *

186 | * Given the above: 187 | * * `jcl-over-slf4j` and `slf4j-jcl` are exclusive 188 | * * `commons-logging`, `jcl-over-slf4j` and `spring-jcl` are exclusive 189 | * * `jcl-over-slf4j` and `log4j-jcl` are exclusive 190 | */ 191 | private void configureCommonsLogging(DependencyHandler dependencies) { 192 | dependencies.components( handler -> { 193 | handler.withModule(LoggingModuleIdentifiers.COMMONS_LOGGING.moduleId, CommonsLoggingImplementationRule.class); 194 | handler.withModule(LoggingModuleIdentifiers.JCL_OVER_SLF4J.moduleId, CommonsLoggingImplementationRule.class); 195 | handler.withModule(LoggingModuleIdentifiers.SPRING_JCL.moduleId, CommonsLoggingImplementationRule.class); 196 | 197 | handler.withModule(LoggingModuleIdentifiers.JCL_OVER_SLF4J.moduleId, Slf4JVsJCL.class); 198 | handler.withModule(LoggingModuleIdentifiers.SLF4J_JCL.moduleId, Slf4JVsJCL.class); 199 | 200 | handler.withModule(LoggingModuleIdentifiers.JCL_OVER_SLF4J.moduleId, Slf4JVsLog4J2ForJCL.class); 201 | handler.withModule(LoggingModuleIdentifiers.LOG4J_JCL.moduleId, Slf4JVsLog4J2ForJCL.class); 202 | }); 203 | } 204 | } -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/LoggingModuleIdentifiers.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging; 2 | 3 | import org.gradle.api.artifacts.Dependency; 4 | 5 | public enum LoggingModuleIdentifiers { 6 | LOG4J_SLF4J_IMPL("org.apache.logging.log4j", "log4j-slf4j-impl", "2.0"), 7 | LOG4J_SLF4J2_IMPL("org.apache.logging.log4j", "log4j-slf4j2-impl", "2.19.0"), 8 | LOG4J_TO_SLF4J("org.apache.logging.log4j", "log4j-to-slf4j", "2.0"), 9 | SLF4J_SIMPLE("org.slf4j", "slf4j-simple", "1.0"), 10 | LOGBACK_CLASSIC("ch.qos.logback", "logback-classic", "1.0.0"), 11 | SLF4J_LOG4J12("org.slf4j", "slf4j-log4j12", "1.0"), 12 | SLF4J_JCL("org.slf4j", "slf4j-jcl", "1.0"), 13 | SLF4J_JDK14("org.slf4j", "slf4j-jdk14", "1.0"), 14 | LOG4J_OVER_SLF4J("org.slf4j", "log4j-over-slf4j", "1.4.2"), 15 | LOG4J12API("org.apache.logging.log4j", "log4j-1.2-api", "2.0"), 16 | LOG4J("log4j", "log4j", "1.1.3"), 17 | JUL_TO_SLF4J("org.slf4j", "jul-to-slf4j", "1.5.10"), 18 | LOG4J_JUL("org.apache.logging.log4j", "log4j-jul", "2.1"), 19 | COMMONS_LOGGING("commons-logging", "commons-logging", "1.0"), 20 | JCL_OVER_SLF4J("org.slf4j", "jcl-over-slf4j", "1.5.10"), 21 | LOG4J_JCL("org.apache.logging.log4j", "log4j-jcl", "2.0"), 22 | LOG4J_CORE("org.apache.logging.log4j", "log4j-core", "2.0"), 23 | SPRING_JCL("org.springframework", "spring-jcl", "5.0.0.RELEASE"); 24 | 25 | public final String moduleId; 26 | public final String group; 27 | public final String name; 28 | private final String firstVersion; 29 | 30 | LoggingModuleIdentifiers(String group, String name, String firstVersion) { 31 | this.group = group; 32 | this.name = name; 33 | this.firstVersion = firstVersion; 34 | this.moduleId = group + ":" + name; 35 | } 36 | 37 | public boolean matches(Dependency dependency) { 38 | return group.equals(dependency.getGroup()) && name.equals(dependency.getName()); 39 | } 40 | 41 | public String asFirstVersion() { 42 | return moduleId + ":" + firstVersion; 43 | } 44 | 45 | public String asVersionZero() { 46 | return moduleId + ":0"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/actions/Slf4JEnforcementSubstitutionsUsing.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging.actions; 2 | 3 | import dev.jacomet.gradle.plugins.logging.LoggingModuleIdentifiers; 4 | import org.gradle.api.Action; 5 | import org.gradle.api.artifacts.Configuration; 6 | import org.gradle.api.artifacts.component.ComponentSelector; 7 | 8 | public class Slf4JEnforcementSubstitutionsUsing implements Action { 9 | @Override 10 | public void execute(Configuration configuration) { 11 | configuration.getResolutionStrategy().dependencySubstitution(substitution -> { 12 | ComponentSelector log4JOverSlf4J = substitution.module(LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.asFirstVersion()); 13 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J.moduleId)).using(log4JOverSlf4J); 14 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J12API.moduleId)).using(log4JOverSlf4J); 15 | 16 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J_JUL.moduleId)).using(substitution.module(LoggingModuleIdentifiers.JUL_TO_SLF4J.asFirstVersion())); 17 | 18 | ComponentSelector jclOverSlf4J = substitution.module(LoggingModuleIdentifiers.JCL_OVER_SLF4J.asFirstVersion()); 19 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.COMMONS_LOGGING.moduleId)).using(jclOverSlf4J); 20 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J_JCL.moduleId)).using(jclOverSlf4J); 21 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.SPRING_JCL.moduleId)).using(jclOverSlf4J); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/actions/Slf4JEnforcementSubstitutionsWith.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging.actions; 2 | 3 | import dev.jacomet.gradle.plugins.logging.LoggingModuleIdentifiers; 4 | import org.gradle.api.Action; 5 | import org.gradle.api.artifacts.Configuration; 6 | import org.gradle.api.artifacts.component.ComponentSelector; 7 | 8 | public class Slf4JEnforcementSubstitutionsWith implements Action { 9 | @Override 10 | public void execute(Configuration configuration) { 11 | configuration.getResolutionStrategy().dependencySubstitution(substitution -> { 12 | ComponentSelector log4JOverSlf4J = substitution.module(LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.asFirstVersion()); 13 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J.moduleId)).with(log4JOverSlf4J); 14 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J12API.moduleId)).with(log4JOverSlf4J); 15 | 16 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J_JUL.moduleId)).with(substitution.module(LoggingModuleIdentifiers.JUL_TO_SLF4J.asFirstVersion())); 17 | 18 | ComponentSelector jclOverSlf4J = substitution.module(LoggingModuleIdentifiers.JCL_OVER_SLF4J.asFirstVersion()); 19 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.COMMONS_LOGGING.moduleId)).with(jclOverSlf4J); 20 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.LOG4J_JCL.moduleId)).with(jclOverSlf4J); 21 | substitution.substitute(substitution.module(LoggingModuleIdentifiers.SPRING_JCL.moduleId)).with(jclOverSlf4J); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/extension/LoggingCapabilitiesExtension.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging.extension; 2 | 3 | import dev.jacomet.gradle.plugins.logging.LoggingCapabilitiesPlugin; 4 | import dev.jacomet.gradle.plugins.logging.LoggingModuleIdentifiers; 5 | import dev.jacomet.gradle.plugins.logging.actions.Slf4JEnforcementSubstitutionsUsing; 6 | import dev.jacomet.gradle.plugins.logging.actions.Slf4JEnforcementSubstitutionsWith; 7 | import dev.jacomet.gradle.plugins.logging.rules.*; 8 | import org.gradle.api.Action; 9 | import org.gradle.api.artifacts.CapabilitiesResolution; 10 | import org.gradle.api.artifacts.Configuration; 11 | import org.gradle.api.artifacts.ConfigurationContainer; 12 | import org.gradle.api.artifacts.Dependency; 13 | import org.gradle.api.artifacts.ExternalDependency; 14 | import org.gradle.api.artifacts.component.ComponentIdentifier; 15 | import org.gradle.api.artifacts.component.ModuleComponentIdentifier; 16 | import org.gradle.api.artifacts.dsl.DependencyHandler; 17 | import org.gradle.util.GradleVersion; 18 | 19 | /** 20 | * Project extension that enables expressing preference over potential logging capabilities conflicts. 21 | */ 22 | public class LoggingCapabilitiesExtension { 23 | private final ConfigurationContainer configurations; 24 | private final DependencyHandler dependencies; 25 | private final Runnable alignmentActivation; 26 | 27 | public LoggingCapabilitiesExtension(ConfigurationContainer configurations, DependencyHandler dependencies, Runnable alignmentActivation) { 28 | this.configurations = configurations; 29 | this.dependencies = dependencies; 30 | this.alignmentActivation = alignmentActivation; 31 | } 32 | 33 | /** 34 | * Selects the provided module as the Slf4J binding to use. 35 | *

36 | * This also resolves all other potential conflicts with the passed in module in favor of it. 37 | * 38 | * @param dependencyNotation the Slf4J binding module as a dependency or {@code group:name:version} notation 39 | */ 40 | public void selectSlf4JBinding(Object dependencyNotation) { 41 | ExternalDependency dependency = validateNotation(dependencyNotation); 42 | String because = "Logging capabilities plugin selected Slf4J binding"; 43 | if (LoggingModuleIdentifiers.SLF4J_LOG4J12.matches(dependency)) { 44 | selectCapabilityConflict(Slf4JvsLog4J.CAPABILITY_ID, dependency, because); 45 | selectCapabilityConflict(Slf4JImplementation.CAPABILITY_ID, dependency, because); 46 | } else if (LoggingModuleIdentifiers.SLF4J_JDK14.matches(dependency)) { 47 | selectCapabilityConflict(Slf4JvsJUL.CAPABILITY_ID, dependency, because); 48 | selectCapabilityConflict(Slf4JImplementation.CAPABILITY_ID, dependency, because); 49 | } else if (LoggingModuleIdentifiers.SLF4J_JCL.matches(dependency)) { 50 | selectCapabilityConflict(Slf4JVsJCL.CAPABILITY_ID, dependency, because); 51 | selectCapabilityConflict(Slf4JImplementation.CAPABILITY_ID, dependency, because); 52 | } else if (LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.matches(dependency) || LoggingModuleIdentifiers.LOG4J_SLF4J2_IMPL.matches(dependency)) { 53 | selectCapabilityConflict(Log4J2vsSlf4J.CAPABILITY_ID, dependency, because); 54 | selectCapabilityConflict(Slf4JImplementation.CAPABILITY_ID, dependency, because); 55 | // Slf4j binding towards log4j2, so we need to make sure Log4j-core is selected 56 | selectCapabilityConflict(Log4J2Implementation.CAPABILITY_ID, validateNotation(LoggingModuleIdentifiers.LOG4J_CORE.moduleId), because); 57 | } else if (LoggingModuleIdentifiers.LOGBACK_CLASSIC.matches(dependency) || LoggingModuleIdentifiers.SLF4J_SIMPLE.matches(dependency)) { 58 | selectCapabilityConflict(Slf4JImplementation.CAPABILITY_ID, dependency, because); 59 | } else { 60 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Slf4J binding"); 61 | } 62 | } 63 | 64 | /** 65 | * Selects the provided module as the Slf4J binding to use for the resolution of the given configuration. 66 | *

67 | * This also resolves all other potential conflicts with the passed in module in favor of it. 68 | * 69 | * @param configurationName the configuration to be setup 70 | * @param dependencyNotation the Slf4J binding module as a dependency or {@code group:name:version} notation 71 | */ 72 | public void selectSlf4JBinding(String configurationName, Object dependencyNotation) { 73 | ExternalDependency dependency = validateNotation(dependencyNotation); 74 | String because = "Logging capabilities plugin selected Slf4J binding"; 75 | if (LoggingModuleIdentifiers.SLF4J_LOG4J12.matches(dependency)) { 76 | selectCapabilityConflict(configurationName, Slf4JvsLog4J.CAPABILITY_ID, dependency, because); 77 | selectCapabilityConflict(configurationName, Slf4JImplementation.CAPABILITY_ID, dependency, because); 78 | } else if (LoggingModuleIdentifiers.SLF4J_JDK14.matches(dependency)) { 79 | selectCapabilityConflict(configurationName, Slf4JvsJUL.CAPABILITY_ID, dependency, because); 80 | selectCapabilityConflict(configurationName, Slf4JImplementation.CAPABILITY_ID, dependency, because); 81 | } else if (LoggingModuleIdentifiers.SLF4J_JCL.matches(dependency)) { 82 | selectCapabilityConflict(configurationName, Slf4JVsJCL.CAPABILITY_ID, dependency, because); 83 | selectCapabilityConflict(configurationName, Slf4JImplementation.CAPABILITY_ID, dependency, because); 84 | } else if (LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.matches(dependency) || LoggingModuleIdentifiers.LOG4J_SLF4J2_IMPL.matches(dependency)) { 85 | selectCapabilityConflict(configurationName, Log4J2vsSlf4J.CAPABILITY_ID, dependency, because); 86 | selectCapabilityConflict(configurationName, Slf4JImplementation.CAPABILITY_ID, dependency, because); 87 | // Slf4j binding towards log4j2, so we need to make sure Log4j-core is selected 88 | selectCapabilityConflict(configurationName, Log4J2Implementation.CAPABILITY_ID, validateNotation(LoggingModuleIdentifiers.LOG4J_CORE.moduleId), because); 89 | } else if (LoggingModuleIdentifiers.LOGBACK_CLASSIC.matches(dependency) || LoggingModuleIdentifiers.SLF4J_SIMPLE.matches(dependency)) { 90 | selectCapabilityConflict(configurationName, Slf4JImplementation.CAPABILITY_ID, dependency, because); 91 | } else { 92 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Slf4J binding"); 93 | } 94 | } 95 | 96 | /** 97 | * Selects the provided module as the Log4J2 implementation to use. 98 | *

99 | * This also resolves all other potential conflicts with the passed in module in favor of it. 100 | * 101 | * @param dependencyNotation the Log4J 2 implementation as a dependency or {@code group:name:version} notation 102 | */ 103 | public void selectLog4J2Implementation(Object dependencyNotation) { 104 | ExternalDependency dependency = validateNotation(dependencyNotation); 105 | String because = "Logging capabilities plugin selected Log4J2 implementation"; 106 | if (LoggingModuleIdentifiers.LOG4J_CORE.matches(dependency)) { 107 | selectCapabilityConflict(Log4J2Implementation.CAPABILITY_ID, dependency, because); 108 | } else if (LoggingModuleIdentifiers.LOG4J_TO_SLF4J.matches(dependency)) { 109 | selectCapabilityConflict(Log4J2Implementation.CAPABILITY_ID, dependency, because); 110 | } else { 111 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Log4J2 implementation"); 112 | } 113 | } 114 | 115 | /** 116 | * Selects the provided module as the Log4J2 implementation to use for the resolution of the given configuration. 117 | *

118 | * This also resolves all other potential conflicts with the passed in module in favor of it. 119 | * 120 | * @param configurationName the configuration to be setup 121 | * @param dependencyNotation the Log4J 2 implementation as a dependency or {@code group:name:version} notation 122 | */ 123 | public void selectLog4J2Implementation(String configurationName, Object dependencyNotation) { 124 | ExternalDependency dependency = validateNotation(dependencyNotation); 125 | String because = "Logging capabilities plugin selected Log4J2 implementation"; 126 | if (LoggingModuleIdentifiers.LOG4J_CORE.matches(dependency)) { 127 | selectCapabilityConflict(configurationName, Log4J2Implementation.CAPABILITY_ID, dependency, because); 128 | } else if (LoggingModuleIdentifiers.LOG4J_TO_SLF4J.matches(dependency)) { 129 | selectCapabilityConflict(configurationName, Log4J2Implementation.CAPABILITY_ID, dependency, because); 130 | } else { 131 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Log4J2 implementation"); 132 | } 133 | } 134 | 135 | /** 136 | * Selects the provided module as the Log4J 1.2 implementation to use. 137 | *

138 | * This also resolves all other potential conflicts with the passed in module in favor of it. 139 | * 140 | * @param dependencyNotation the Log4J 1.2 implementation module as a dependency or {@code group:name:version} notation 141 | */ 142 | public void selectLog4J12Implementation(Object dependencyNotation) { 143 | ExternalDependency dependency = validateNotation(dependencyNotation); 144 | String because = "Logging capabilities plugin selected Log4J implementation"; 145 | if (LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.matches(dependency)) { 146 | selectCapabilityConflict(Slf4JvsLog4J2ForLog4J.CAPABILITY_ID, dependency, because); 147 | selectCapabilityConflict(Slf4JvsLog4J.CAPABILITY_ID, dependency, because); 148 | } else if (LoggingModuleIdentifiers.LOG4J12API.matches(dependency)) { 149 | selectCapabilityConflict(Slf4JvsLog4J2ForLog4J.CAPABILITY_ID, dependency, because); 150 | } else if (LoggingModuleIdentifiers.LOG4J.matches(dependency)) { 151 | selectCapabilityConflict(Slf4JvsLog4J2ForLog4J.CAPABILITY_ID, dependency, because); 152 | } else if (LoggingModuleIdentifiers.SLF4J_LOG4J12.matches(dependency)) { 153 | selectCapabilityConflict(Slf4JvsLog4J.CAPABILITY_ID, dependency, because); 154 | } else { 155 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Log4J implementation"); 156 | } 157 | } 158 | 159 | /** 160 | * Selects the provided module as the Log4J 1.2 implementation to use for the resolution of the given configuration. 161 | *

162 | * This also resolves all other potential conflicts with the passed in module in favor of it. 163 | * 164 | * @param configurationName the configuration to be setup 165 | * @param dependencyNotation the Log4J 1.2 implementation module as a dependency or {@code group:name:version} notation 166 | */ 167 | public void selectLog4J12Implementation(String configurationName, Object dependencyNotation) { 168 | ExternalDependency dependency = validateNotation(dependencyNotation); 169 | String because = "Logging capabilities plugin selected Log4J implementation"; 170 | if (LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.matches(dependency)) { 171 | selectCapabilityConflict(configurationName, Slf4JvsLog4J2ForLog4J.CAPABILITY_ID, dependency, because); 172 | selectCapabilityConflict(configurationName, Slf4JvsLog4J.CAPABILITY_ID, dependency, because); 173 | } else if (LoggingModuleIdentifiers.LOG4J12API.matches(dependency)) { 174 | selectCapabilityConflict(configurationName, Slf4JvsLog4J2ForLog4J.CAPABILITY_ID, dependency, because); 175 | } else if (LoggingModuleIdentifiers.LOG4J.matches(dependency)) { 176 | selectCapabilityConflict(configurationName, Slf4JvsLog4J2ForLog4J.CAPABILITY_ID, dependency, because); 177 | } else if (LoggingModuleIdentifiers.SLF4J_LOG4J12.matches(dependency)) { 178 | selectCapabilityConflict(configurationName, Slf4JvsLog4J.CAPABILITY_ID, dependency, because); 179 | } else { 180 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Log4J implementation"); 181 | } 182 | } 183 | 184 | /** 185 | * Selects the provided module as the java util logging delegation to use. 186 | *

187 | * This also resolves all other potential conflicts with the passed in module in favor of it. 188 | * 189 | * @param dependencyNotation the JUL delegation module as a dependency or {@code group:name:version} notation 190 | */ 191 | public void selectJulDelegation(Object dependencyNotation) { 192 | ExternalDependency dependency = validateNotation(dependencyNotation); 193 | String because = "Logging capabilities plugin selected JUL delegation"; 194 | if (LoggingModuleIdentifiers.JUL_TO_SLF4J.matches(dependency)) { 195 | selectCapabilityConflict(Slf4JvsLog4J2ForJUL.CAPABILITY_ID, dependency, because); 196 | selectCapabilityConflict(Slf4JvsJUL.CAPABILITY_ID, dependency, because); 197 | } else if (LoggingModuleIdentifiers.SLF4J_JDK14.matches(dependency)) { 198 | selectCapabilityConflict(Slf4JvsJUL.CAPABILITY_ID, dependency, because); 199 | } else if (LoggingModuleIdentifiers.LOG4J_JUL.matches(dependency)) { 200 | selectCapabilityConflict(Slf4JvsLog4J2ForJUL.CAPABILITY_ID, dependency, because); 201 | } else { 202 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid JUL delegation"); 203 | } 204 | } 205 | 206 | /** 207 | * Selects the provided module as the java util logging delegation to use for the resolution of the given configuration. 208 | *

209 | * This also resolves all other potential conflicts with the passed in module in favor of it. 210 | * 211 | * @param configurationName the configuration to be setup 212 | * @param dependencyNotation the JUL delegation module as a dependency or {@code group:name:version} notation 213 | */ 214 | public void selectJulDelegation(String configurationName, Object dependencyNotation) { 215 | ExternalDependency dependency = validateNotation(dependencyNotation); 216 | String because = "Logging capabilities plugin selected JUL delegation"; 217 | if (LoggingModuleIdentifiers.JUL_TO_SLF4J.matches(dependency)) { 218 | selectCapabilityConflict(configurationName, Slf4JvsLog4J2ForJUL.CAPABILITY_ID, dependency, because); 219 | selectCapabilityConflict(configurationName, Slf4JvsJUL.CAPABILITY_ID, dependency, because); 220 | } else if (LoggingModuleIdentifiers.SLF4J_JDK14.matches(dependency)) { 221 | selectCapabilityConflict(configurationName, Slf4JvsJUL.CAPABILITY_ID, dependency, because); 222 | } else if (LoggingModuleIdentifiers.LOG4J_JUL.matches(dependency)) { 223 | selectCapabilityConflict(configurationName, Slf4JvsLog4J2ForJUL.CAPABILITY_ID, dependency, because); 224 | } else { 225 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid JUL delegation"); 226 | } 227 | } 228 | 229 | /** 230 | * Selects the provided module as the commons-logging implementation to use. 231 | *

232 | * This also resolves all other potential conflicts with the passed in module in favor of it. 233 | * 234 | * @param dependencyNotation the commons-logging implementation module as a dependency or {@code group:name:version} notation 235 | */ 236 | public void selectJCLImplementation(Object dependencyNotation) { 237 | ExternalDependency dependency = validateNotation(dependencyNotation); 238 | String because = "Logging capabilities plugin selected JCL implementation"; 239 | if (LoggingModuleIdentifiers.JCL_OVER_SLF4J.matches(dependency)) { 240 | selectCapabilityConflict(CommonsLoggingImplementationRule.CAPABILITY_ID, dependency, because); 241 | selectCapabilityConflict(Slf4JVsJCL.CAPABILITY_ID, dependency, because); 242 | selectCapabilityConflict(Slf4JVsLog4J2ForJCL.CAPABILITY_ID, dependency, because); 243 | } else if (LoggingModuleIdentifiers.COMMONS_LOGGING.matches(dependency)) { 244 | selectCapabilityConflict(CommonsLoggingImplementationRule.CAPABILITY_ID, dependency, because); 245 | } else if (LoggingModuleIdentifiers.SLF4J_JCL.matches(dependency)) { 246 | selectCapabilityConflict(Slf4JVsJCL.CAPABILITY_ID, dependency, because); 247 | } else if (LoggingModuleIdentifiers.LOG4J_JCL.matches(dependency)) { 248 | selectCapabilityConflict(Slf4JVsLog4J2ForJCL.CAPABILITY_ID, dependency, because); 249 | ExternalDependency commonsLogging = validateNotation(LoggingModuleIdentifiers.COMMONS_LOGGING.asVersionZero()); 250 | selectCapabilityConflict(CommonsLoggingImplementationRule.CAPABILITY_ID, commonsLogging, because); 251 | } else { 252 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid JCL implementation"); 253 | } 254 | } 255 | 256 | /** 257 | * Selects the provided module as the commons-logging implementation to use for the resolution of the given configuration. 258 | *

259 | * This also resolves all other potential conflicts with the passed in module in favor of it. 260 | * 261 | * @param configurationName the configuration to be setup 262 | * @param dependencyNotation the commons-logging implementation module as a dependency or {@code group:name:version} notation 263 | */ 264 | public void selectJCLImplementation(String configurationName, Object dependencyNotation) { 265 | ExternalDependency dependency = validateNotation(dependencyNotation); 266 | String because = "Logging capabilities plugin selected JCL implementation"; 267 | if (LoggingModuleIdentifiers.JCL_OVER_SLF4J.matches(dependency)) { 268 | selectCapabilityConflict(configurationName, CommonsLoggingImplementationRule.CAPABILITY_ID, dependency, because); 269 | selectCapabilityConflict(configurationName, Slf4JVsJCL.CAPABILITY_ID, dependency, because); 270 | selectCapabilityConflict(configurationName, Slf4JVsLog4J2ForJCL.CAPABILITY_ID, dependency, because); 271 | } else if (LoggingModuleIdentifiers.COMMONS_LOGGING.matches(dependency)) { 272 | selectCapabilityConflict(configurationName, CommonsLoggingImplementationRule.CAPABILITY_ID, dependency, because); 273 | } else if (LoggingModuleIdentifiers.SLF4J_JCL.matches(dependency)) { 274 | selectCapabilityConflict(configurationName, Slf4JVsJCL.CAPABILITY_ID, dependency, because); 275 | } else if (LoggingModuleIdentifiers.LOG4J_JCL.matches(dependency)) { 276 | selectCapabilityConflict(configurationName, Slf4JVsLog4J2ForJCL.CAPABILITY_ID, dependency, because); 277 | ExternalDependency commonsLogging = validateNotation(LoggingModuleIdentifiers.COMMONS_LOGGING.asVersionZero()); 278 | selectCapabilityConflict(configurationName, CommonsLoggingImplementationRule.CAPABILITY_ID, commonsLogging, because); 279 | } else { 280 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid JCL implementation"); 281 | } 282 | } 283 | 284 | /** 285 | * Selects the provided module as the Slf4J / Log4J 2 interaction to use. 286 | *

287 | * This also resolves all other potential conflicts with the passed in module in favor of it. 288 | * 289 | * @param dependencyNotation the Slf4J / Log4J 2 interaction module as a dependency or {@code group:name:version} notation 290 | */ 291 | public void selectSlf4JLog4J2Interaction(Object dependencyNotation) { 292 | ExternalDependency dependency = validateNotation(dependencyNotation); 293 | String because = "Logging capabilities plugin selected Slf4J Log4J 2 interaction"; 294 | if (LoggingModuleIdentifiers.LOG4J_TO_SLF4J.matches(dependency)) { 295 | selectCapabilityConflict(Log4J2vsSlf4J.CAPABILITY_ID, dependency, because); 296 | selectCapabilityConflict(Log4J2Implementation.CAPABILITY_ID, dependency, because); 297 | } else if (LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.matches(dependency) || LoggingModuleIdentifiers.LOG4J_SLF4J2_IMPL.matches(dependency)) { 298 | selectCapabilityConflict(Slf4JImplementation.CAPABILITY_ID, dependency, because); 299 | selectCapabilityConflict(Log4J2vsSlf4J.CAPABILITY_ID, dependency, because); 300 | } else { 301 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Slf4J / Log4J 2 interaction"); 302 | } 303 | } 304 | 305 | /** 306 | * Selects the provided module as the Slf4J / Log4J 2 interaction to use for the resolution of the given configuration. 307 | *

308 | * This also resolves all other potential conflicts with the passed in module in favor of it. 309 | * 310 | * @param configurationName the configuration to be setup 311 | * @param dependencyNotation the Slf4J / Log4J 2 interaction module as a dependency or {@code group:name:version} notation 312 | */ 313 | public void selectSlf4JLog4J2Interaction(String configurationName, Object dependencyNotation) { 314 | ExternalDependency dependency = validateNotation(dependencyNotation); 315 | String because = "Logging capabilities plugin selected Slf4J Log4J 2 interaction"; 316 | if (LoggingModuleIdentifiers.LOG4J_TO_SLF4J.matches(dependency)) { 317 | selectCapabilityConflict(configurationName, Log4J2vsSlf4J.CAPABILITY_ID, dependency, because); 318 | selectCapabilityConflict(configurationName, Log4J2Implementation.CAPABILITY_ID, dependency, because); 319 | } else if (LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.matches(dependency) || LoggingModuleIdentifiers.LOG4J_SLF4J2_IMPL.matches(dependency)) { 320 | selectCapabilityConflict(configurationName, Slf4JImplementation.CAPABILITY_ID, dependency, because); 321 | selectCapabilityConflict(configurationName, Log4J2vsSlf4J.CAPABILITY_ID, dependency, because); 322 | } else { 323 | throw new IllegalArgumentException("Provided dependency '" + dependency + "' is not a valid Slf4J / Log4J 2 interaction"); 324 | } 325 | } 326 | 327 | /** 328 | * Selects logback as the Slf4J binding and makes sure all other supported logging frameworks end up in logback as well. 329 | *

330 | * While having logback as a dependency is required for this to work, substitution is used for enforcing other selections that could cause missed events in logback because there are no conflicts. 331 | * For example, {@code commons-logging} and {@code log4j-jcl} will be substituted with {@code jcl-over-slf4j}. 332 | */ 333 | public void enforceLogback() { 334 | selectSlf4JBinding(LoggingModuleIdentifiers.LOGBACK_CLASSIC.asVersionZero()); 335 | enforceSlf4JImplementation(); 336 | } 337 | 338 | /** 339 | * Selects logback as the Slf4J binding and makes sure all other supported logging frameworks end up in logback as well for the resolution of the given configuration. 340 | *

341 | * While having logback as a dependency is required for this to work, substitution is used for enforcing other selections that could cause missed events in logback because there are no conflicts. 342 | * For example, {@code commons-logging} and {@code log4j-jcl} will be substituted with {@code jcl-over-slf4j}. 343 | * 344 | * @param configurationName the configuration to be setup 345 | */ 346 | public void enforceLogback(String configurationName) { 347 | selectSlf4JBinding(configurationName, LoggingModuleIdentifiers.LOGBACK_CLASSIC.asVersionZero()); 348 | enforceSlf4JImplementation(configurationName); 349 | } 350 | 351 | /** 352 | * Selects {@code slf4j-simple} as the Slf4J binding and makes sure all other supported logging frameworks end up in it as well. 353 | *

354 | * While having {@code slf4j-simple} as a dependency is required for this to work, substitution is used for enforcing other selections that could cause missed events in {@code slf4j-simple} because there are no conflicts. 355 | * For example, {@code commons-logging} and {@code log4j-jcl} will be substituted with {@code jcl-over-slf4j}. 356 | */ 357 | public void enforceSlf4JSimple() { 358 | selectSlf4JBinding(LoggingModuleIdentifiers.SLF4J_SIMPLE.asVersionZero()); 359 | enforceSlf4JImplementation(); 360 | } 361 | 362 | /** 363 | * Selects {@code slf4j-simple} as the Slf4J binding and makes sure all other supported logging frameworks end up in it as well for the resolution of the given configuration. 364 | *

365 | * While having {@code slf4j-simple} as a dependency is required for this to work, substitution is used for enforcing other selections that could cause missed events in {@code slf4j-simple} because there are no conflicts. 366 | * For example, {@code commons-logging} and {@code log4j-jcl} will be substituted with {@code jcl-over-slf4j}. 367 | * 368 | * @param configurationName the configuration to be setup 369 | */ 370 | public void enforceSlf4JSimple(String configurationName) { 371 | selectSlf4JBinding(configurationName, LoggingModuleIdentifiers.SLF4J_SIMPLE.asVersionZero()); 372 | enforceSlf4JImplementation(configurationName); 373 | } 374 | 375 | /** 376 | * Selects {@code log4j-slf4j-impl} as the Slf4J binding and makes sure all other supported logging frameworks end up in Log4J 2 as well. 377 | *

378 | * While having {@code log4j-slf4j-impl} as a dependency is required for this to work, substitution is used for enforcing other selections that could cause missed events in Log4J 2 because there are no conflicts. 379 | * For example, {@code commons-logging} and {@code log4j} will be configured to end up in Log4J 2 as well. 380 | */ 381 | public void enforceLog4J2() { 382 | selectLog4J2Implementation( LoggingModuleIdentifiers.LOG4J_CORE.asVersionZero()); 383 | selectSlf4JLog4J2Interaction(LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.asVersionZero()); 384 | selectJulDelegation(LoggingModuleIdentifiers.LOG4J_JUL.asVersionZero()); 385 | selectJCLImplementation(LoggingModuleIdentifiers.LOG4J_JCL.asVersionZero()); 386 | selectLog4J12Implementation(LoggingModuleIdentifiers.LOG4J12API.asVersionZero()); 387 | 388 | } 389 | 390 | /** 391 | * Selects {@code log4j-slf4j-impl} as the Slf4J binding and makes sure all other supported logging frameworks end up in Log4J 2 as well for the resolution of the given configuration. 392 | *

393 | * While having {@code log4j-slf4j-impl} as a dependency is required for this to work, substitution is used for enforcing other selections that could cause missed events in Log4J 2 because there are no conflicts. 394 | * For example, {@code commons-logging} and {@code log4j} will be configured to end up in Log4J 2 as well. 395 | * 396 | * @param configurationName the configuration to be setup 397 | */ 398 | public void enforceLog4J2(String configurationName) { 399 | selectLog4J2Implementation(configurationName, LoggingModuleIdentifiers.LOG4J_CORE.asVersionZero()); 400 | selectSlf4JLog4J2Interaction(configurationName, LoggingModuleIdentifiers.LOG4J_SLF4J_IMPL.asVersionZero()); 401 | selectJulDelegation(configurationName, LoggingModuleIdentifiers.LOG4J_JUL.asVersionZero()); 402 | selectJCLImplementation(configurationName, LoggingModuleIdentifiers.LOG4J_JCL.asVersionZero()); 403 | selectLog4J12Implementation(configurationName, LoggingModuleIdentifiers.LOG4J12API.asVersionZero()); 404 | } 405 | 406 | /** 407 | * Enables the alignment feature. 408 | *

409 | * The feature is enabled by default for Gradle 6.2 and beyond. 410 | *

411 | * For Gradle 6.0 and 6.1, the feature is disabled due to a bug that may prevent all conflicts from being properly detected. 412 | * So enabling alignment for these versions should be handled carefully. 413 | */ 414 | public void enableAlignment() { 415 | alignmentActivation.run(); 416 | } 417 | 418 | private void enforceSlf4JImplementation() { 419 | selectLog4J12Implementation(LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.asVersionZero()); 420 | selectJulDelegation(LoggingModuleIdentifiers.JUL_TO_SLF4J.asVersionZero()); 421 | selectJCLImplementation(LoggingModuleIdentifiers.JCL_OVER_SLF4J.asVersionZero()); 422 | selectSlf4JLog4J2Interaction(LoggingModuleIdentifiers.LOG4J_TO_SLF4J.asVersionZero()); 423 | selectLog4J2Implementation(LoggingModuleIdentifiers.LOG4J_TO_SLF4J.asVersionZero()); 424 | 425 | configurations.all(getSlf4JEnforcementSubstitutions()); 426 | } 427 | 428 | private void enforceSlf4JImplementation(String configurationName) { 429 | selectLog4J12Implementation(configurationName, LoggingModuleIdentifiers.LOG4J_OVER_SLF4J.asVersionZero()); 430 | selectJulDelegation(configurationName, LoggingModuleIdentifiers.JUL_TO_SLF4J.asVersionZero()); 431 | selectJCLImplementation(configurationName, LoggingModuleIdentifiers.JCL_OVER_SLF4J.asVersionZero()); 432 | selectSlf4JLog4J2Interaction(configurationName, LoggingModuleIdentifiers.LOG4J_TO_SLF4J.asVersionZero()); 433 | selectLog4J2Implementation(configurationName, LoggingModuleIdentifiers.LOG4J_TO_SLF4J.asVersionZero()); 434 | 435 | configurations.matching(conf -> conf.getName().equals(configurationName)).all(getSlf4JEnforcementSubstitutions()); 436 | } 437 | 438 | private Action getSlf4JEnforcementSubstitutions() { 439 | if (GradleVersion.current().compareTo(LoggingCapabilitiesPlugin.GRADLE_7_0) > 0) { 440 | return new Slf4JEnforcementSubstitutionsUsing(); 441 | } 442 | return new Slf4JEnforcementSubstitutionsWith(); 443 | } 444 | 445 | private ExternalDependency validateNotation(Object dependencyNotation) { 446 | Dependency dependency = dependencies.create(dependencyNotation); 447 | if (dependency instanceof ExternalDependency) { 448 | return (ExternalDependency) dependency; 449 | } else { 450 | throw new IllegalArgumentException("Provided notation '" + dependencyNotation + "' cannot be converted to an ExternalDependency"); 451 | } 452 | } 453 | 454 | private void selectCapabilityConflict(String configuration, String capabilityId, ExternalDependency target, String because) { 455 | configurations.matching(conf -> conf.getName().equals(configuration)).all(conf -> conf.getResolutionStrategy().capabilitiesResolution(getCapabilitiesResolutionAction(capabilityId, target, because))); 456 | } 457 | 458 | private void selectCapabilityConflict(String capabilityId, ExternalDependency target, String because) { 459 | configurations.all(conf -> conf.getResolutionStrategy().capabilitiesResolution(getCapabilitiesResolutionAction(capabilityId, target, because))); 460 | } 461 | 462 | private Action getCapabilitiesResolutionAction(String capabilityId, ExternalDependency target, String because) { 463 | return resolution -> resolution.withCapability(capabilityId, details -> { 464 | details.getCandidates().stream().filter(candidate -> { 465 | ComponentIdentifier id = candidate.getId(); 466 | if (!(id instanceof ModuleComponentIdentifier)) { 467 | return false; 468 | } 469 | ModuleComponentIdentifier moduleId = (ModuleComponentIdentifier) id; 470 | return moduleId.getGroup().equals(target.getGroup()) 471 | && moduleId.getModule().equals(target.getName()); 472 | }).findFirst().ifPresent(candidate -> details.select(candidate).because(because)); 473 | }); 474 | } 475 | 476 | } 477 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/CommonsLoggingImplementationRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:commons-logging-impl:1.0} to all variants. 20 | */ 21 | public class CommonsLoggingImplementationRule extends FixedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "commons-logging-impl"; 23 | public static final String CAPABILITY_ID = CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public CommonsLoggingImplementationRule() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/FixedCapabilityRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | import org.gradle.api.artifacts.ComponentMetadataContext; 19 | import org.gradle.api.artifacts.ComponentMetadataRule; 20 | 21 | /** 22 | * Abstract rule adding a capability with a hard coded version. 23 | */ 24 | abstract class FixedCapabilityRule implements ComponentMetadataRule { 25 | static final String CAPABILITY_GROUP = "dev.jacomet.logging"; 26 | 27 | private final String name; 28 | 29 | protected FixedCapabilityRule(String name) { 30 | this.name = name; 31 | } 32 | 33 | @Override 34 | public void execute(ComponentMetadataContext context) { 35 | context.getDetails().allVariants(variant -> { 36 | variant.withCapabilities(capabilities -> { 37 | capabilities.addCapability("dev.jacomet.logging", name, "1.0"); 38 | }); 39 | }); 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Log4J2Alignment.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging.rules; 2 | 3 | import org.gradle.api.artifacts.ComponentMetadataContext; 4 | import org.gradle.api.artifacts.ComponentMetadataDetails; 5 | import org.gradle.api.artifacts.ComponentMetadataRule; 6 | 7 | public class Log4J2Alignment implements ComponentMetadataRule { 8 | @Override 9 | public void execute(ComponentMetadataContext context) { 10 | ComponentMetadataDetails details = context.getDetails(); 11 | if (details.getId().getGroup().startsWith("org.apache.logging.log4j")) { 12 | details.belongsTo("org.apache.logging.log4j:log4j-bom:" + details.getId().getVersion(), false); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Log4J2Implementation.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging.rules; 2 | 3 | public class Log4J2Implementation extends VersionedCapabilityRule { 4 | 5 | public static final String CAPABILITY_NAME = "log4j2-impl"; 6 | public static final String CAPABILITY_ID = FixedCapabilityRule.CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 7 | 8 | public Log4J2Implementation() { 9 | super(CAPABILITY_NAME); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Log4J2vsSlf4J.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:log4j2-vs-slf4j:} to all variants, using the version of the module targeted. 20 | */ 21 | public class Log4J2vsSlf4J extends VersionedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "log4j2-vs-slf4j"; 23 | public static final String CAPABILITY_ID = FixedCapabilityRule.CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Log4J2vsSlf4J() { 26 | super(CAPABILITY_NAME); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JAlignment.java: -------------------------------------------------------------------------------- 1 | package dev.jacomet.gradle.plugins.logging.rules; 2 | 3 | import org.gradle.api.artifacts.ComponentMetadataContext; 4 | import org.gradle.api.artifacts.ComponentMetadataDetails; 5 | import org.gradle.api.artifacts.ComponentMetadataRule; 6 | 7 | public class Slf4JAlignment implements ComponentMetadataRule { 8 | @Override 9 | public void execute(ComponentMetadataContext context) { 10 | ComponentMetadataDetails details = context.getDetails(); 11 | if (details.getId().getGroup().startsWith("org.slf4j")) { 12 | details.belongsTo("dev.jacomet.logging.align:slf4j:" + details.getId().getVersion()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JImplementation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-impl:1.0} to all variants. 20 | */ 21 | public class Slf4JImplementation extends FixedCapabilityRule { 22 | 23 | public static final String CAPABILITY_NAME = "slf4j-impl"; 24 | public static final String CAPABILITY_ID = CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 25 | 26 | public Slf4JImplementation() { 27 | super(CAPABILITY_NAME); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JVsJCL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-vs-jcl:} to all variants, using the version of the module targeted. 20 | */ 21 | public class Slf4JVsJCL extends VersionedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "slf4j-vs-jcl"; 23 | public static final String CAPABILITY_ID = FixedCapabilityRule.CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Slf4JVsJCL() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JVsLog4J2ForJCL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-vs-log4j2-jcl:1.0} to all variants. 20 | */ 21 | public class Slf4JVsLog4J2ForJCL extends FixedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "slf4j-vs-log4j2-jcl"; 23 | public static final String CAPABILITY_ID = CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Slf4JVsLog4J2ForJCL() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JvsJUL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-vs-jul:} to all variants, using the version of the module targeted. 20 | */ 21 | public class Slf4JvsJUL extends VersionedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "slf4j-vs-jul"; 23 | public static final String CAPABILITY_ID = FixedCapabilityRule.CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Slf4JvsJUL() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JvsLog4J.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-vs-log4j:} to all variants, using the version of the module targeted. 20 | */ 21 | public class Slf4JvsLog4J extends VersionedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "slf4j-vs-log4j"; 23 | public static final String CAPABILITY_ID = FixedCapabilityRule.CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Slf4JvsLog4J() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JvsLog4J2ForJUL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-vs-log4j2-jul:1.0} to all variants. 20 | */ 21 | public class Slf4JvsLog4J2ForJUL extends FixedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "slf4j-vs-log4j2-jul"; 23 | public static final String CAPABILITY_ID = CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Slf4JvsLog4J2ForJUL() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/Slf4JvsLog4J2ForLog4J.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | /** 19 | * Adds capability {@code dev.jacomet.logging:slf4j-vs-log4j2-log4j:1.0} to all variants. 20 | */ 21 | public class Slf4JvsLog4J2ForLog4J extends FixedCapabilityRule { 22 | public static final String CAPABILITY_NAME = "slf4j-vs-log4j2-log4j"; 23 | public static final String CAPABILITY_ID = CAPABILITY_GROUP + ":" + CAPABILITY_NAME; 24 | 25 | public Slf4JvsLog4J2ForLog4J() { 26 | super(CAPABILITY_NAME); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/jacomet/gradle/plugins/logging/rules/VersionedCapabilityRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules; 17 | 18 | import org.gradle.api.artifacts.ComponentMetadataContext; 19 | import org.gradle.api.artifacts.ComponentMetadataDetails; 20 | import org.gradle.api.artifacts.ComponentMetadataRule; 21 | 22 | /** 23 | * Abstract rule adding a capability using the version of the module targeted. 24 | */ 25 | abstract class VersionedCapabilityRule implements ComponentMetadataRule { 26 | private final String name; 27 | 28 | public VersionedCapabilityRule(String name) { 29 | this.name = name; 30 | } 31 | 32 | @Override 33 | public void execute(ComponentMetadataContext context) { 34 | ComponentMetadataDetails details = context.getDetails(); 35 | String version = details.getId().getVersion(); 36 | details.allVariants( variant -> { 37 | variant.withCapabilities(capabilities -> { 38 | capabilities.addCapability("dev.jacomet.logging", name, version); 39 | }); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/groovy/dev/jacomet/gradle/plugins/logging/LoggingCapabilitiesPluginTest.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging 17 | 18 | import org.gradle.testfixtures.ProjectBuilder 19 | import spock.lang.Specification 20 | 21 | class LoggingCapabilitiesPluginTest extends Specification { 22 | def project = ProjectBuilder.builder().build() 23 | 24 | def "plugin is applied without error"() { 25 | given: 26 | project.plugins.apply("dev.jacomet.logging-capabilities") 27 | 28 | expect: 29 | project.plugins.getPlugin(LoggingCapabilitiesPlugin) != null 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/groovy/dev/jacomet/gradle/plugins/logging/rules/LoggingCapabilitiesRulesTest.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package dev.jacomet.gradle.plugins.logging.rules 17 | 18 | import org.gradle.api.Action 19 | import org.gradle.api.artifacts.ComponentMetadataContext 20 | import org.gradle.api.artifacts.ComponentMetadataDetails 21 | import org.gradle.api.artifacts.ModuleVersionIdentifier 22 | import org.gradle.api.artifacts.VariantMetadata 23 | import org.gradle.api.capabilities.MutableCapabilitiesMetadata 24 | import spock.lang.Specification 25 | 26 | class LoggingCapabilitiesRulesTest extends Specification { 27 | 28 | def "versioned capability rule adds capability with module version"() { 29 | given: 30 | def name = "testName" 31 | def version = "2.2" 32 | def rule = new MyVersionedCapabilityRule(name) 33 | def context = Mock(ComponentMetadataContext) 34 | def details = Mock(ComponentMetadataDetails) 35 | def identifier = Mock(ModuleVersionIdentifier) 36 | def variant = Mock(VariantMetadata) 37 | def capabilities = Mock(MutableCapabilitiesMetadata) 38 | 39 | when: 40 | rule.execute(context) 41 | 42 | then: 43 | 1 * context.details >> details 44 | 1 * details.id >> identifier 45 | 1 * identifier.version >> version 46 | 1 * details.allVariants(_) >> { Action action -> action.execute(variant) } 47 | 1 * variant.withCapabilities(_) >> { Action action -> action.execute(capabilities) } 48 | 1 * capabilities.addCapability("dev.jacomet.logging", "testName", "2.2") 49 | } 50 | 51 | def "fixed capability rule adds capability with version 1.0"() { 52 | given: 53 | def name = "testName" 54 | def rule = new MyFixedCapabilityRule(name) 55 | def context = Mock(ComponentMetadataContext) 56 | def details = Mock(ComponentMetadataDetails) 57 | def variant = Mock(VariantMetadata) 58 | def capabilities = Mock(MutableCapabilitiesMetadata) 59 | 60 | when: 61 | rule.execute(context) 62 | 63 | then: 64 | 1 * context.details >> details 65 | 1 * details.allVariants(_) >> { Action action -> action.execute(variant) } 66 | 1 * variant.withCapabilities(_) >> { Action action -> action.execute(capabilities) } 67 | 1 * capabilities.addCapability("dev.jacomet.logging", "testName", "1.0") 68 | } 69 | 70 | static class MyVersionedCapabilityRule extends VersionedCapabilityRule { 71 | MyVersionedCapabilityRule(String name) { 72 | super(name) 73 | } 74 | } 75 | 76 | static class MyFixedCapabilityRule extends FixedCapabilityRule { 77 | MyFixedCapabilityRule(String name) { 78 | super(name) 79 | } 80 | } 81 | } 82 | --------------------------------------------------------------------------------