├── .editorconfig ├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── de │ └── is24 │ └── maven │ └── enforcer │ └── rules │ ├── ArtifactRepositoryAnalyzer.java │ ├── ClassDependencyResolvingVisitor.java │ ├── ClassFilter.java │ ├── IllegalTransitiveDependencyCheck.java │ ├── Repository.java │ └── Types.java └── test └── java └── de └── is24 └── maven └── enforcer └── rules ├── ArtifactRepositoryAnalyzerTest.java ├── ClassFileReference.java ├── ClassFilterTest.java ├── EnforcerRuleHelperWrapper.java ├── IllegalTransitiveDependencyCheckTest.java ├── LogStub.java ├── RepositoryTest.java ├── TypesTest.java └── testtypes ├── ClassInAnotherTransitiveDependency.java ├── ClassInDirectDependency.java ├── ClassInMavenProjectSource.java └── ClassInTransitiveDependency.java /.editorconfig: -------------------------------------------------------------------------------- 1 | # default identation settings 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | # workflow_dispatch enables manual triggering of the workflow 5 | workflow_dispatch: 6 | schedule: 7 | - cron: '51 2 * * 3' 8 | env: 9 | FAST_EMAIL: ${{ secrets.FAST_EMAIL }} 10 | FAST_USER: ${{ secrets.FAST_USER }} 11 | FAST_TOKEN: ${{ secrets.FAST_TOKEN }} 12 | FAST_HTTPAUTH: ${{ secrets.FAST_HTTPAUTH }} 13 | 14 | jobs: 15 | analyze: 16 | name: Analyze 17 | runs-on: ubuntu-latest 18 | permissions: 19 | actions: read 20 | contents: read 21 | security-events: write 22 | 23 | steps: 24 | - name: S24 static application security testing (SAST) action 25 | uses: scout24/s24-sast-action@v1 26 | with: 27 | languages: java 28 | fast_user: ${{ env.FAST_USER }} 29 | fast_token: ${{ env.FAST_TOKEN }} 30 | java_version: '11' 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignored resources 2 | .idea 3 | *.iml 4 | target -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | branches: 5 | only: 6 | - master 7 | after_success: 8 | - mvn clean test jacoco:report coveralls:jacoco 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 André Schubert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is end of live and not any longer maintained! 2 | ========================================================== 3 | 4 | 5 | The 'Illegal' Transitive Dependency Check Rule 6 | ============================================== 7 | 8 | [![Build Status](https://travis-ci.org/ImmobilienScout24/illegal-transitive-dependency-check.svg?branch=master)](https://travis-ci.org/ImmobilienScout24/illegal-transitive-dependency-check) 9 | [![Coverage Status](https://img.shields.io/coveralls/ImmobilienScout24/illegal-transitive-dependency-check.svg?branch=master)](https://coveralls.io/r/ImmobilienScout24/illegal-transitive-dependency-check) 10 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.is24.maven.enforcer.rules/illegal-transitive-dependency-check/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.is24.maven.enforcer.rules/illegal-transitive-dependency-check/) 11 | 12 | The `IllegalTransitiveDependencyCheck` is an additional rule for the `maven-enforcer-plugin`. The rule checks if 13 | all classes in a certain artifact references only classes that are provided by explicitly declared dependencies. 14 | Thus the rule will list (or complain about) all classes that are only available through transitive dependencies. 15 | 16 | You can run the check by configuring the maven-enforcer-plugin to make use of the additional rule: 17 | 18 | ```xml 19 | 20 | ... 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-enforcer-plugin 26 | 1.3.1 27 | 28 | 29 | de.is24.maven.enforcer.rules 30 | illegal-transitive-dependency-check 31 | 1.7.4 32 | 33 | 34 | 35 | 36 | enforce 37 | verify 38 | 39 | enforce 40 | 41 | 42 | 43 | 44 | false 45 | true 46 | true 47 | 48 | javax\..+ 49 | org\.hibernate\..+ 50 | 51 | false 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ... 61 | 62 | ``` 63 | 64 | The rule itself can be configured to only report violations or even to signal the enforcer-plugin to break the build by 65 | specifying the attribute `reportOnly`. You may also exclude classes or packages from analysis by providing 66 | regex-patterns to parameter `regexIgnoredClasses` (e.g. `my\.suppressed\.Type`). 67 | 68 | In addition to these exclusions types from packages `javax.*`,`sun.*`, `jdk.*`, `org.*` and `com.sun.*` that are available through the current 69 | Java runtime can be excluded automatically by setting parameter `suppressTypesFromJavaRuntime`. 70 | 71 | By default the rule will resolve the currently analyzed artifact in the Maven repository. In case the enforcer-plugin 72 | runs in a phase compiled classes are available in the target folder (e.g. `verify`) artifact-resolving can be avoided 73 | by setting parameter `useClassesFromLastBuild` to `true`. 74 | 75 | (Since version 1.7.4 the `regexIngoredClasses` filtering is also applied to the classes of the artifact currently 76 | analyzed. Thus direct dependencies of that classes will not be considered. See request [#29](https://github.com/ImmobilienScout24/illegal-transitive-dependency-check/issues/29)) 77 | 78 | If not only the classes but also the transitively used artifacts should be listed the parameter `listMissingArtifacts` 79 | can be set to `true`. **Caution: This option is really slow!** 80 | 81 | Releases are available [here](http://repo1.maven.org/maven2/de/is24/maven/enforcer/rules/illegal-transitive-dependency-check/) in Maven's central repository. 82 | 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.sonatype.oss 8 | oss-parent 9 | 7 10 | 11 | 12 | de.is24.maven.enforcer.rules 13 | illegal-transitive-dependency-check 14 | 1.8-SNAPSHOT 15 | jar 16 | 17 | Illegal Transitive Dependency Check 18 | This rule checks whether the code uses classes of another module through transitive dependencies. 19 | https://github.com/ImmobilienScout24/illegal-transitive-dependency-check 20 | 21 | 22 | 23 | The MIT License (MIT) 24 | http://opensource.org/licenses/MIT 25 | repo 26 | 27 | 28 | 29 | 30 | scm:git:https://github.com/ImmobilienScout24/illegal-transitive-dependency-check.git 31 | scm:git:git@github.com:ImmobilienScout24/illegal-transitive-dependency-check.git 32 | https://github.com/ImmobilienScout24/illegal-transitive-dependency-check.git 33 | 34 | 35 | 36 | 37 | 2bad4u 38 | André Schubert 39 | 2bad4u@scubizone.de 40 | 41 | 42 | 43 | 44 | 1.3.1 45 | 3.1 46 | 2.0.9 47 | 1.7.6 48 | 5.0.3 49 | 1.6 50 | 1.8 51 | 0.7.1.201405082137 52 | 2.2.0 53 | 2.1 54 | 1.5.5 55 | 56 | 57 | 58 | 59 | 60 | maven-compiler-plugin 61 | ${build.plugin.version} 62 | 63 | 64 | default-compile 65 | 66 | 67 | ${jdk.src.version} 68 | ${jdk.src.version} 69 | 70 | 71 | 72 | 73 | default-testCompile 74 | 75 | 76 | ${jdk.test.version} 77 | ${jdk.test.version} 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.eluder.coveralls 85 | coveralls-maven-plugin 86 | ${coveralls.plugin.version} 87 | 88 | 89 | org.jacoco 90 | jacoco-maven-plugin 91 | ${jacoco.plugin.version} 92 | 93 | 94 | prepare-agent 95 | 96 | prepare-agent 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.eclipse.m2e 107 | lifecycle-mapping 108 | 1.0.0 109 | 110 | 111 | 112 | 113 | 114 | org.jacoco 115 | 116 | jacoco-maven-plugin 117 | 118 | 119 | [0.7.1.201405082137,) 120 | 121 | 122 | prepare-agent 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | org.apache.maven.enforcer 140 | enforcer-api 141 | ${enforcer.plugin.version} 142 | 143 | 144 | org.apache.maven 145 | maven-project 146 | ${maven.version} 147 | 148 | 149 | org.apache.maven 150 | maven-core 151 | ${maven.version} 152 | 153 | 154 | org.apache.maven 155 | maven-artifact 156 | ${maven.version} 157 | 158 | 159 | org.apache.maven 160 | maven-plugin-api 161 | ${maven.version} 162 | 163 | 164 | org.codehaus.plexus 165 | plexus-container-default 166 | ${plexus-container.version} 167 | 168 | 169 | org.apache.maven.shared 170 | maven-dependency-tree 171 | ${maven-dependency-tree.version} 172 | 173 | 174 | junit 175 | junit 176 | 4.11 177 | test 178 | 179 | 180 | org.ow2.asm 181 | asm-analysis 182 | ${asm.version} 183 | 184 | 185 | org.apache.maven.enforcer 186 | enforcer-rules 187 | ${enforcer.plugin.version} 188 | test-jar 189 | test 190 | 191 | 192 | org.apache.maven.shared 193 | maven-plugin-testing-harness 194 | 1.1 195 | test 196 | 197 | 198 | org.apache.maven.plugins 199 | maven-enforcer-plugin 200 | ${enforcer.plugin.version} 201 | maven-plugin 202 | test 203 | 204 | 205 | org.slf4j 206 | slf4j-api 207 | ${sl4j.version} 208 | test 209 | 210 | 211 | org.slf4j 212 | slf4j-jdk14 213 | ${sl4j.version} 214 | test 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /src/main/java/de/is24/maven/enforcer/rules/ArtifactRepositoryAnalyzer.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.artifact.Artifact; 4 | import org.apache.maven.plugin.logging.Log; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassVisitor; 7 | 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.IOException; 11 | import java.util.Enumeration; 12 | import java.util.regex.Pattern; 13 | import java.util.zip.ZipEntry; 14 | import java.util.zip.ZipFile; 15 | 16 | 17 | final class ArtifactRepositoryAnalyzer { 18 | private static final String CLASS_SUFFIX = ".class"; 19 | private static final Pattern JAR_FILE_PATTERN = Pattern.compile("^.+\\.(jar|war|JAR|WAR)$"); 20 | 21 | private final Log logger; 22 | private final boolean analyzeDependencies; 23 | private final ClassFilter filter; 24 | 25 | private ArtifactRepositoryAnalyzer(Log logger, boolean analyzeDependencies, ClassFilter filter) { 26 | this.logger = logger; 27 | this.analyzeDependencies = analyzeDependencies; 28 | this.filter = filter; 29 | } 30 | 31 | static ArtifactRepositoryAnalyzer analyzeArtifacts(Log logger, boolean analyzeDependencies, 32 | ClassFilter filter) { 33 | return new ArtifactRepositoryAnalyzer(logger, analyzeDependencies, filter); 34 | } 35 | 36 | Repository analyzeArtifacts(Iterable artifacts) { 37 | final Repository repository = new Repository(filter); 38 | 39 | for (Artifact artifact : artifacts) { 40 | final File artifactFile = artifact.getFile(); 41 | if (artifactFile == null) { 42 | logger.info("Artifact '" + artifact + "' has no associated file, skip it."); 43 | continue; 44 | } 45 | 46 | if (artifactFile.isDirectory()) { 47 | analyzeClassesDirectory(repository, artifactFile); 48 | } else { 49 | final String absolutePath = artifactFile.getAbsolutePath(); 50 | if (JAR_FILE_PATTERN.matcher(absolutePath).matches()) { 51 | analyzeJar(repository, artifactFile); 52 | } else { 53 | logger.info("Artifact '" + artifact + "' associated file '" + absolutePath + "', is skipped."); 54 | } 55 | } 56 | } 57 | return repository; 58 | } 59 | 60 | private void analyzeJar(Repository repository, File jar) { 61 | final ClassVisitor classVisitor = new ClassDependencyResolvingVisitor(repository, logger); 62 | 63 | ZipFile zipFile = null; 64 | try { 65 | zipFile = new ZipFile(jar.getAbsolutePath()); 66 | 67 | final Enumeration entries = zipFile.entries(); 68 | while (entries.hasMoreElements()) { 69 | final ZipEntry entry = entries.nextElement(); 70 | final String fileName = entry.getName(); 71 | if (fileName.endsWith(CLASS_SUFFIX)) { 72 | if (logger.isDebugEnabled()) { 73 | logger.debug("Analyze class '" + fileName + "' in JAR '" + jar + "'."); 74 | } 75 | 76 | final ClassReader classReader = new ClassReader(zipFile.getInputStream(entry)); 77 | 78 | final String className = classReader.getClassName().replace('/', '.'); 79 | if (analyzeDependencies) { 80 | if (filter.isConsideredType(className)) { 81 | classReader.accept(classVisitor, ClassReader.SKIP_FRAMES); 82 | } 83 | } else { 84 | repository.addType(className); 85 | } 86 | } 87 | } 88 | } catch (IOException e) { 89 | throw logAndWrapIOException(e, jar, "artifact"); 90 | } finally { 91 | if (zipFile != null) { 92 | try { 93 | zipFile.close(); 94 | } catch (IOException e) { 95 | throw logAndWrapIOException(e, jar, "artifact"); 96 | } 97 | } 98 | } 99 | } 100 | 101 | private IllegalStateException logAndWrapIOException(IOException e, File file, final String description) { 102 | final String error = "Unable to read class(es) from " + description + " '" + file + "'."; 103 | logger.error(error, e); 104 | return new IllegalStateException(error, e); 105 | } 106 | 107 | private void analyzeClassesDirectory(Repository repository, File classesDirectory) { 108 | final ClassVisitor classVisitor = new ClassDependencyResolvingVisitor(repository, logger); 109 | analyzeClassesDirectory(repository, classesDirectory, classVisitor); 110 | } 111 | 112 | private void analyzeClassesDirectory(Repository repository, File directory, ClassVisitor classVisitor) { 113 | if (directory.isDirectory()) { 114 | final String[] entries = directory.list(); 115 | for (String entry : entries) { 116 | analyzeClassesDirectory(repository, new File(directory, entry), classVisitor); 117 | } 118 | } 119 | 120 | final String path = directory.getPath(); 121 | if (path.endsWith(CLASS_SUFFIX)) { 122 | if (logger.isDebugEnabled()) { 123 | logger.debug("Analyze class '" + path + "'."); 124 | } 125 | 126 | FileInputStream classFileStream = null; 127 | try { 128 | classFileStream = new FileInputStream(directory); 129 | final ClassReader classReader = new ClassReader(classFileStream); 130 | String className = classReader.getClassName().replace('/', '.'); 131 | if (analyzeDependencies) { 132 | if (filter.isConsideredType(className)) { 133 | classReader.accept(classVisitor, ClassReader.SKIP_FRAMES); 134 | } 135 | } else { 136 | repository.addType(className); 137 | } 138 | } catch (IOException e) { 139 | throw logAndWrapIOException(e, directory, "file"); 140 | } finally { 141 | try { 142 | if (classFileStream != null) { 143 | classFileStream.close(); 144 | } 145 | } catch (IOException e) { 146 | throw logAndWrapIOException(e, directory, "file"); 147 | } 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/de/is24/maven/enforcer/rules/ClassDependencyResolvingVisitor.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.plugin.logging.Log; 4 | import org.objectweb.asm.AnnotationVisitor; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.FieldVisitor; 7 | import org.objectweb.asm.Label; 8 | import org.objectweb.asm.MethodVisitor; 9 | import org.objectweb.asm.Opcodes; 10 | import org.objectweb.asm.Type; 11 | import org.objectweb.asm.signature.SignatureReader; 12 | import org.objectweb.asm.signature.SignatureVisitor; 13 | 14 | 15 | final class ClassDependencyResolvingVisitor extends ClassVisitor { 16 | private final Repository repository; 17 | private final Log logger; 18 | private final AnnotationVisitor annotationVisitor = new ClassDependencyAnnotationVisitor(); 19 | private final FieldVisitor fieldVisitor = new ClassDependencyFieldVisitor(); 20 | private final MethodVisitor methodVisitor = new ClassDependencyMethodVisitor(); 21 | private final SignatureVisitor signatureVisitor = new ClassDependencySignatureVisitor(); 22 | 23 | ClassDependencyResolvingVisitor(Repository repository, Log logger) { 24 | super(Opcodes.ASM5); 25 | this.repository = repository; 26 | this.logger = logger; 27 | } 28 | 29 | @Override 30 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 31 | final String className = Types.readInternalTypeName(name); 32 | logger.debug("Add new type '" + className + "'."); 33 | repository.addType(className); 34 | 35 | if (superName != null) { 36 | final String superTypeName = Types.readInternalTypeName(superName); 37 | addDependency("super type", superTypeName); 38 | } 39 | 40 | if (interfaces != null) { 41 | for (String iface : interfaces) { 42 | final String interfaceType = Types.readInternalTypeName(iface); 43 | addDependency("interface type", interfaceType); 44 | } 45 | } 46 | 47 | processSignature(signature); 48 | } 49 | 50 | @Override 51 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { 52 | final String fieldType = Types.readTypeDescription(desc); 53 | addDependency("field type", fieldType); 54 | 55 | // add initial field value if any 56 | if (value != null) { 57 | final String fieldValueType = Types.readValueType(value); 58 | addDependency("field value type", fieldValueType); 59 | } 60 | processSignature(signature); 61 | 62 | return fieldVisitor; 63 | } 64 | 65 | @Override 66 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 67 | return delegateToAnnotationVisitor(desc); 68 | } 69 | 70 | @Override 71 | public void visitInnerClass(String name, String outerName, String innerName, int access) { 72 | final String innerClassName = Types.readInternalTypeName(name); 73 | addDependency("inner class", innerClassName); 74 | } 75 | 76 | @Override 77 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 78 | final Type[] argumentTypes = Type.getArgumentTypes(desc); 79 | 80 | for (Type argumentType : argumentTypes) { 81 | final String parameterTypeName = Types.readType(argumentType); 82 | addDependency("annotation's method parameter type", 83 | parameterTypeName); 84 | } 85 | 86 | final Type returnType = Type.getReturnType(desc); 87 | final String returnTypeName = Types.readType(returnType); 88 | addDependency("annotation's method return type", returnTypeName); 89 | 90 | if (exceptions != null) { 91 | for (String exception : exceptions) { 92 | final String exceptionName = Types.readInternalTypeName(exception); 93 | addDependency("exception type", exceptionName); 94 | } 95 | } 96 | 97 | processSignature(signature); 98 | 99 | return methodVisitor; 100 | } 101 | 102 | private AnnotationVisitor delegateToAnnotationVisitor(String desc) { 103 | final String annotationType = Types.readTypeDescription(desc); 104 | addDependency("annotation", annotationType); 105 | 106 | return annotationVisitor; 107 | } 108 | 109 | private void addDependency(String typeDescription, String typeName) { 110 | if (logger.isDebugEnabled()) { 111 | logger.debug("Add " + typeDescription + " '" + typeName + "' as dependency."); 112 | } 113 | repository.addDependency(typeName); 114 | } 115 | 116 | private void processSignature(String signature) { 117 | if (signature != null) { 118 | final SignatureReader signatureReader = new SignatureReader( 119 | signature); 120 | signatureReader.accept(signatureVisitor); 121 | } 122 | } 123 | 124 | private final class ClassDependencySignatureVisitor extends SignatureVisitor { 125 | private ClassDependencySignatureVisitor() { 126 | super(Opcodes.ASM5); 127 | } 128 | 129 | @Override 130 | public void visitClassType(String name) { 131 | final String classType = Types.readInternalTypeName(name); 132 | addDependency("class type", classType); 133 | } 134 | } 135 | 136 | private final class ClassDependencyAnnotationVisitor extends AnnotationVisitor { 137 | private ClassDependencyAnnotationVisitor() { 138 | super(Opcodes.ASM5); 139 | } 140 | 141 | @Override 142 | public void visit(String name, Object value) { 143 | final String valueType = Types.readValueType(value); 144 | addDependency("annotation's value type", valueType); 145 | } 146 | 147 | @Override 148 | public void visitEnum(String name, String desc, String value) { 149 | final String enumType = Types.readTypeDescription(desc); 150 | addDependency("annotation's enum type", enumType); 151 | } 152 | 153 | @Override 154 | public AnnotationVisitor visitAnnotation(String name, String desc) { 155 | final String annotationType = Types.readTypeDescription(desc); 156 | addDependency("annotation's annotation type", annotationType); 157 | 158 | return this; 159 | } 160 | 161 | @Override 162 | public AnnotationVisitor visitArray(String name) { 163 | return this; 164 | } 165 | } 166 | 167 | private final class ClassDependencyFieldVisitor extends FieldVisitor { 168 | private ClassDependencyFieldVisitor() { 169 | super(Opcodes.ASM5); 170 | } 171 | 172 | @Override 173 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 174 | return delegateToAnnotationVisitor(desc); 175 | } 176 | } 177 | 178 | private final class ClassDependencyMethodVisitor extends MethodVisitor { 179 | private ClassDependencyMethodVisitor() { 180 | super(Opcodes.ASM5); 181 | } 182 | 183 | @Override 184 | public AnnotationVisitor visitAnnotationDefault() { 185 | return annotationVisitor; 186 | } 187 | 188 | @Override 189 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 190 | return delegateToAnnotationVisitor(desc); 191 | } 192 | 193 | @Override 194 | public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 195 | return delegateToAnnotationVisitor(desc); 196 | } 197 | 198 | @Override 199 | public void visitTypeInsn(int opcode, String type) { 200 | final String typeName = Types.readInternalTypeName(type); 201 | addDependency("Type instruction type", typeName); 202 | } 203 | 204 | @Override 205 | public void visitFieldInsn(int opcode, String owner, String name, String desc) { 206 | final String fieldType = Types.readTypeDescription(desc); 207 | addDependency("field instruction type", fieldType); 208 | } 209 | 210 | @Override 211 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 212 | final String ownerType = Types.readInternalTypeName(owner); 213 | addDependency("method owner", ownerType); 214 | 215 | final Type[] argumentTypes = Type.getArgumentTypes(desc); 216 | 217 | for (Type argumentType : argumentTypes) { 218 | final String parameterTypeName = Types.readType(argumentType); 219 | addDependency("method parameter type", parameterTypeName); 220 | } 221 | 222 | final Type returnType = Type.getReturnType(desc); 223 | final String returnTypeName = Types.readType(returnType); 224 | addDependency("method return type", returnTypeName); 225 | } 226 | 227 | @Override 228 | public void visitLdcInsn(Object cst) { 229 | final String constantTypeName = Types.readValueType(cst); 230 | addDependency("constant's type", constantTypeName); 231 | } 232 | 233 | @Override 234 | public void visitMultiANewArrayInsn(String desc, int dims) { 235 | final String arrayType = Types.readTypeDescription(desc); 236 | addDependency("array's type", arrayType); 237 | } 238 | 239 | @Override 240 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 241 | if (type != null) { 242 | final String exceptionType = Types.readInternalTypeName(type); 243 | addDependency("exception type", exceptionType); 244 | } 245 | } 246 | 247 | @Override 248 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { 249 | final String localVariableType = Types.readTypeDescription(desc); 250 | addDependency("local variable", localVariableType); 251 | 252 | processSignature(signature); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/de/is24/maven/enforcer/rules/ClassFilter.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.plugin.logging.Log; 4 | import org.codehaus.plexus.util.StringUtils; 5 | 6 | import java.net.URL; 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.regex.Pattern; 11 | 12 | final class ClassFilter { 13 | // ignore primitives, numerical names (for anonymous classes) and all classes in package java 14 | private static final String JAVA_TYPES_REGEX = "[0-9\\$]+|" + 15 | "(boolean)|(byte)|(char)|(short)|(int)|(long)|(float)|(double)|(void)|" + 16 | "(java\\.[\\w\\.\\$]*)"; 17 | 18 | // path of current Java runtime environment 19 | private static final String JAVA_HOME_PATH = "file:" + System.getProperty("java.home"); 20 | private static final Pattern JAVA_RUNTIME_PACKAGES = Pattern.compile( 21 | "^(javax|com\\.sun|org|sun|jdk)\\..+"); 22 | 23 | // cache JDK types that already have been checked.. 24 | private final Map alreadyProcessedJavaTypes = new HashMap(); 25 | 26 | private final Pattern ignoredClassesPattern; 27 | private final boolean suppressTypesFromJavaRuntime; 28 | private final Log logger; 29 | 30 | ClassFilter(Log logger, boolean suppressTypesFromJavaRuntime, String... regexIgnoredClasses) { 31 | this.logger = logger; 32 | this.suppressTypesFromJavaRuntime = suppressTypesFromJavaRuntime; 33 | 34 | if ((regexIgnoredClasses == null) || (regexIgnoredClasses.length == 0)) { 35 | ignoredClassesPattern = Pattern.compile(JAVA_TYPES_REGEX); 36 | } else { 37 | final StringBuilder regexBuilder = new StringBuilder(JAVA_TYPES_REGEX); 38 | for (String regex : regexIgnoredClasses) { 39 | if (StringUtils.isNotEmpty(regex)) { 40 | regexBuilder.append("|(").append(regex).append(")"); 41 | } 42 | } 43 | 44 | final String regex = regexBuilder.toString(); 45 | logger.debug("Use type suppression pattern '" + regex + "'."); 46 | ignoredClassesPattern = Pattern.compile(regex); 47 | } 48 | } 49 | 50 | private boolean typeFromJavaRuntime(String type) { 51 | if (JAVA_RUNTIME_PACKAGES.matcher(type).matches()) { 52 | // check if this type has already been checked 53 | final Boolean isJdkType = alreadyProcessedJavaTypes.get(type); 54 | if (isJdkType != null) { 55 | if (logger.isDebugEnabled()) { 56 | logger.debug("Type's '" + type + "' existence in current Java runtime has already been checked."); 57 | } 58 | return isJdkType; 59 | } 60 | 61 | final String classResource = type.replace('.', '/') + ".class"; 62 | final URL it = ClassLoader.getSystemClassLoader().getResource(classResource); 63 | if (it != null) { 64 | final String sourcePath = it.getFile(); 65 | if (sourcePath.startsWith(JAVA_HOME_PATH)) { 66 | if (logger.isDebugEnabled()) { 67 | logger.debug("Suppress type '" + type + "', it's in current Java runtime '" + JAVA_HOME_PATH + "'."); 68 | } 69 | alreadyProcessedJavaTypes.put(type, true); 70 | return true; 71 | } 72 | } 73 | alreadyProcessedJavaTypes.put(type, false); 74 | return false; 75 | } 76 | return false; 77 | } 78 | 79 | void addFiltered(Collection set, String type) { 80 | if (isConsideredType(type)) { 81 | set.add(type); 82 | } 83 | } 84 | 85 | boolean isConsideredType(String type) { 86 | if (ignoredClassesPattern.matcher(type).matches()) { 87 | if (logger.isDebugEnabled()) { 88 | logger.debug("Suppress type '" + type + "'."); 89 | } 90 | return false; 91 | } 92 | 93 | // check if JDK classes should be ignored and class comes from current Java runtime.. 94 | return !(suppressTypesFromJavaRuntime && typeFromJavaRuntime(type)); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/de/is24/maven/enforcer/rules/IllegalTransitiveDependencyCheck.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.artifact.Artifact; 4 | import org.apache.maven.artifact.repository.ArtifactRepository; 5 | import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; 6 | import org.apache.maven.artifact.resolver.ArtifactResolver; 7 | import org.apache.maven.enforcer.rule.api.EnforcerRule; 8 | import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 9 | import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 10 | import org.apache.maven.model.Build; 11 | import org.apache.maven.plugin.logging.Log; 12 | import org.apache.maven.project.MavenProject; 13 | import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; 14 | import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException; 15 | import org.apache.maven.shared.dependency.graph.DependencyNode; 16 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 17 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 18 | import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 19 | import org.codehaus.plexus.util.StringUtils; 20 | 21 | import java.io.File; 22 | import java.io.FileWriter; 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.Collections; 26 | import java.util.HashSet; 27 | import java.util.List; 28 | import java.util.Set; 29 | 30 | 31 | /** 32 | * Rule enforcing directly declared maven dependencies only 33 | * 34 | * @author André Schubert 35 | */ 36 | public final class IllegalTransitiveDependencyCheck implements EnforcerRule { 37 | private static final String NO_CACHE_ID_AVAILABLE = null; 38 | private static final String OUTPUT_FILE_EXTENSION = ".txt"; 39 | private static final String OUTPUT_FILE_PREFIX = "itd-"; 40 | 41 | private ArtifactResolver resolver; 42 | 43 | private ArtifactRepository localRepository; 44 | 45 | private DependencyGraphBuilder dependencyGraphBuilder; 46 | 47 | private List remoteRepositories; 48 | 49 | private String outputDirectory; 50 | 51 | private MavenProject project; 52 | 53 | private Log logger; 54 | 55 | private boolean reportOnly; 56 | 57 | private boolean listMissingArtifacts; 58 | 59 | private String[] regexIgnoredClasses; 60 | 61 | private boolean useClassesFromLastBuild; 62 | 63 | private boolean suppressTypesFromJavaRuntime; 64 | 65 | private ClassFilter filter; 66 | 67 | 68 | @Override 69 | public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException { 70 | logger = helper.getLog(); 71 | 72 | if (reportOnly) { 73 | logger.info("Flag 'reportOnly' is set. Exceptions from rule will only be reported!"); 74 | } 75 | 76 | if (listMissingArtifacts) { 77 | logger.info("Flag 'listMissingArtifacts' is set. Transitively used artifacts are resolved."); 78 | initializeDependencyGraphBuilder(helper); 79 | } 80 | 81 | if (useClassesFromLastBuild) { 82 | logger.info("Flag 'useClassesFromLastBuild' is set. Try to use existing output folder."); 83 | } 84 | 85 | if (suppressTypesFromJavaRuntime) { 86 | logger.info( 87 | "Flag 'suppressTypesFromJavaRuntime' is set. Classes available in current Java-runtime will be ignored."); 88 | } 89 | 90 | initializeArtifactResolver(helper); 91 | 92 | initializeProject(helper); 93 | 94 | final Artifact artifact = resolveArtifact(); 95 | 96 | // skip analyzes if the artifact has no associated file.. 97 | if (artifact.getFile() == null) { 98 | logger.info("Nothing to analyze in '" + artifact.getId() + "'."); 99 | return; 100 | } 101 | 102 | // initialize the suppression filter 103 | filter = new ClassFilter(logger, suppressTypesFromJavaRuntime, regexIgnoredClasses); 104 | 105 | final Repository artifactClassesRepository = ArtifactRepositoryAnalyzer.analyzeArtifacts(logger, 106 | true, 107 | filter) 108 | .analyzeArtifacts(Collections.singleton(artifact)); 109 | 110 | final Set dependencies = resolveDirectDependencies(artifact); 111 | 112 | final Repository dependenciesClassesRepository = ArtifactRepositoryAnalyzer.analyzeArtifacts(logger, 113 | false, 114 | filter) 115 | .analyzeArtifacts(dependencies); 116 | 117 | if (logger.isDebugEnabled()) { 118 | logger.debug("Artifact's type dependencies are: " + artifactClassesRepository.getDependencies()); 119 | logger.debug("Classes defined in direct dependencies are: " + dependenciesClassesRepository.getTypes()); 120 | } 121 | 122 | final Set unresolvedTypes = new HashSet(artifactClassesRepository.getDependencies()); 123 | unresolvedTypes.removeAll(artifactClassesRepository.getTypes()); 124 | unresolvedTypes.removeAll(dependenciesClassesRepository.getTypes()); 125 | 126 | // traverse transitive dependencies to find the artifact a certain class is loaded from 127 | if (unresolvedTypes.isEmpty()) { 128 | logger.info("No illegal transitive dependencies found in '" + artifact.getId() + "'."); 129 | } else { 130 | final String message = buildOutput(artifact, unresolvedTypes); 131 | 132 | writeOutputFile(artifact, message); 133 | 134 | if (reportOnly) { 135 | logger.error(message); 136 | } else { 137 | throw new EnforcerRuleException(message); 138 | } 139 | } 140 | } 141 | 142 | @SuppressWarnings("unchecked") 143 | private Set resolveTransitiveDependencies(Artifact artifact) throws EnforcerRuleException { 144 | final DependencyNode root; 145 | try { 146 | root = dependencyGraphBuilder.buildDependencyGraph(project, null); 147 | } catch (DependencyGraphBuilderException e) { 148 | throw new EnforcerRuleException("Unable to build the dependency graph!", e); 149 | } 150 | if (logger.isDebugEnabled()) { 151 | logger.debug("Root node is '" + root + "'."); 152 | } 153 | 154 | final Set transitiveDependencies = new HashSet(); 155 | traverseDependencyNodes(root, transitiveDependencies); 156 | 157 | final Set directDependencies = resolveDirectDependencies(artifact); 158 | transitiveDependencies.removeAll(directDependencies); 159 | if (logger.isDebugEnabled()) { 160 | logger.debug("Transitive dependencies are '" + transitiveDependencies + "'."); 161 | } 162 | return transitiveDependencies; 163 | } 164 | 165 | private void traverseDependencyNodes(DependencyNode node, Set transitiveDependencies) 166 | throws EnforcerRuleException { 167 | final List children = node.getChildren(); 168 | if ((children == null) || children.isEmpty()) { 169 | return; 170 | } 171 | for (DependencyNode child : children) { 172 | final Artifact artifact = child.getArtifact(); 173 | enforceArtifactResolution(artifact); 174 | if (logger.isDebugEnabled()) { 175 | logger.debug("Add dependency '" + artifact.getId() + "'"); 176 | } 177 | transitiveDependencies.add(artifact); 178 | traverseDependencyNodes(child, transitiveDependencies); 179 | } 180 | } 181 | 182 | @SuppressWarnings("unchecked") 183 | private Set resolveDirectDependencies(Artifact artifact) { 184 | final Set dependencies = new HashSet(project.getDependencyArtifacts()); 185 | dependencies.remove(artifact); 186 | if (logger.isDebugEnabled()) { 187 | logger.debug("Direct dependencies are '" + dependencies + "'."); 188 | } 189 | return dependencies; 190 | } 191 | 192 | @SuppressWarnings("unchecked") 193 | private void initializeProject(ExpressionEvaluator helper) throws EnforcerRuleException { 194 | try { 195 | project = (MavenProject) helper.evaluate("${project}"); 196 | 197 | localRepository = (ArtifactRepository) helper.evaluate("${localRepository}"); 198 | remoteRepositories = (List) helper.evaluate("${project.remoteArtifactRepositories}"); 199 | 200 | outputDirectory = (String) helper.evaluate("${project.build.directory}"); 201 | 202 | } catch (ExpressionEvaluationException e) { 203 | throw new EnforcerRuleException("Unable to locate Maven project and/or repositories!", e); 204 | } 205 | logger.debug("Analyze project '" + project + "'."); 206 | } 207 | 208 | private void initializeArtifactResolver(EnforcerRuleHelper helper) throws EnforcerRuleException { 209 | try { 210 | resolver = (ArtifactResolver) helper.getComponent(ArtifactResolver.class); 211 | } catch (ComponentLookupException e) { 212 | throw new EnforcerRuleException("Unable to lookup artifact resolver!", e); 213 | } 214 | } 215 | 216 | private void initializeDependencyGraphBuilder(EnforcerRuleHelper helper) throws EnforcerRuleException { 217 | try { 218 | dependencyGraphBuilder = helper.getContainer().lookup(DependencyGraphBuilder.class, "default"); 219 | } catch (ComponentLookupException e) { 220 | throw new EnforcerRuleException("Unable to lookup dependency graph builder!", e); 221 | } 222 | } 223 | 224 | private Artifact resolveArtifact() throws EnforcerRuleException { 225 | final Artifact artifact = project.getArtifact(); 226 | logger.info("Analyze dependencies of artifact '" + artifact.getId() + "'."); 227 | 228 | // use the current project's target/classes directory as fake artifact.. 229 | if (useClassesFromLastBuild) { 230 | final File targetClassesDirectory = getTargetClassesDirectory(); 231 | artifact.setFile(targetClassesDirectory); 232 | return artifact; 233 | } 234 | 235 | return enforceArtifactResolution(artifact); 236 | } 237 | 238 | private File getTargetClassesDirectory() { 239 | final Build build = project.getBuild(); 240 | if (build != null) { 241 | final String classesOutputDirectory = build.getOutputDirectory(); 242 | if (StringUtils.isNotEmpty(classesOutputDirectory)) { 243 | final File targetClasses = new File(classesOutputDirectory); 244 | if (targetClasses.isDirectory() && (targetClasses.list().length > 0)) { 245 | logger.debug("Found valid target/classes directory '" + targetClasses.getAbsolutePath() + "'."); 246 | return targetClasses; 247 | } 248 | } 249 | } 250 | logger.debug("No target/classes directory found."); 251 | return null; 252 | } 253 | 254 | private Artifact enforceArtifactResolution(Artifact artifact) throws EnforcerRuleException { 255 | logger.debug("Enforce artifact resolution for project '" + project + "'."); 256 | try { 257 | resolver.resolve(artifact, remoteRepositories, localRepository); 258 | return artifact; 259 | } catch (AbstractArtifactResolutionException e) { 260 | final String error = "Unable to resolve artifact '" + artifact.getId() + "'!"; 261 | logger.error(error, e); 262 | throw new EnforcerRuleException(error, e); 263 | } 264 | } 265 | 266 | private String buildOutput(Artifact artifact, Set unresolvedTypes) throws EnforcerRuleException { 267 | final StringBuilder output = new StringBuilder(); 268 | output.append("Found ") 269 | .append(unresolvedTypes.size()) 270 | .append(" illegal transitive type dependencies in artifact '") 271 | .append(artifact.getId()) 272 | .append("':\n"); 273 | 274 | final List illegalTransitiveDependencies; 275 | if (listMissingArtifacts) { 276 | illegalTransitiveDependencies = new ArrayList( 277 | findArtifactsForUnresolvedTypes(artifact, unresolvedTypes)); 278 | } else { 279 | illegalTransitiveDependencies = new ArrayList(unresolvedTypes); 280 | } 281 | 282 | Collections.sort(illegalTransitiveDependencies); 283 | 284 | int k = 1; 285 | for (String illegalTransitiveDependency : illegalTransitiveDependencies) { 286 | output.append(k).append(".) ").append(illegalTransitiveDependency).append("\n"); 287 | k++; 288 | } 289 | return output.toString(); 290 | } 291 | 292 | private Set findArtifactsForUnresolvedTypes(Artifact artifact, Set unresolvedTypes) 293 | throws EnforcerRuleException { 294 | final Set transitiveDependencies = resolveTransitiveDependencies(artifact); 295 | final Set unresolvedTypesWithArtifact = new HashSet(); 296 | 297 | for (Artifact transitiveDependency : transitiveDependencies) { 298 | // skip further artifacts if all types have been found 299 | if (unresolvedTypesWithArtifact.size() == unresolvedTypes.size()) { 300 | break; 301 | } 302 | 303 | final Repository repository = ArtifactRepositoryAnalyzer.analyzeArtifacts(logger, 304 | false, 305 | filter) 306 | .analyzeArtifacts(Collections.singleton(transitiveDependency)); 307 | 308 | final Set repositoryTypes = repository.getTypes(); 309 | for (String unresolvedType : unresolvedTypes) { 310 | if (repositoryTypes.contains(unresolvedType)) { 311 | unresolvedTypesWithArtifact.add(unresolvedType + ", [" + transitiveDependency.getId() + "]"); 312 | } 313 | } 314 | } 315 | return unresolvedTypesWithArtifact; 316 | } 317 | 318 | private void writeOutputFile(Artifact artifact, String output) throws EnforcerRuleException { 319 | if (outputDirectory == null) { 320 | logger.warn("Project's output directory has not been set, skip writing!"); 321 | return; 322 | } 323 | 324 | final String outputFilePath = determineOutputFilePath(artifact); 325 | final File outputFile = new File(outputFilePath); 326 | final File targetFolder = outputFile.getParentFile(); 327 | if (!targetFolder.exists() && !targetFolder.mkdirs()) { 328 | final String error = "Unable to create directory '" + targetFolder + "'!"; 329 | logger.error(error); 330 | throw new EnforcerRuleException(error); 331 | } 332 | 333 | FileWriter resultFileWriter = null; 334 | try { 335 | resultFileWriter = new FileWriter(outputFile); 336 | resultFileWriter.write(output); 337 | } catch (IOException e) { 338 | throw logAndWrapIOException(e, outputFilePath); 339 | } finally { 340 | if (resultFileWriter != null) { 341 | try { 342 | resultFileWriter.close(); 343 | } catch (IOException e) { 344 | throw logAndWrapIOException(e, outputFilePath); 345 | } 346 | } 347 | } 348 | } 349 | 350 | private EnforcerRuleException logAndWrapIOException(IOException e, String outputFilePath) { 351 | final String error = "Unable to write output file '" + outputFilePath + "'!"; 352 | logger.error(error, e); 353 | return new EnforcerRuleException(error, e); 354 | } 355 | 356 | private String determineOutputFilePath(Artifact artifact) { 357 | final String separator = outputDirectory.endsWith("/") ? "" : "/"; 358 | final String formattedArtifactId = artifact.getId().replace(':', '-'); 359 | return outputDirectory + separator + OUTPUT_FILE_PREFIX + formattedArtifactId + OUTPUT_FILE_EXTENSION; 360 | } 361 | 362 | @Override 363 | public boolean isCacheable() { 364 | return false; 365 | } 366 | 367 | @Override 368 | public boolean isResultValid(EnforcerRule enforcerRule) { 369 | return false; 370 | } 371 | 372 | @Override 373 | public String getCacheId() { 374 | return NO_CACHE_ID_AVAILABLE; 375 | } 376 | 377 | public void setListMissingArtifacts(boolean listMissingArtifacts) { 378 | this.listMissingArtifacts = listMissingArtifacts; 379 | } 380 | 381 | public void setReportOnly(boolean reportOnly) { 382 | this.reportOnly = reportOnly; 383 | } 384 | 385 | public void setRegexIgnoredClasses(String[] regexIgnoredClasses) { 386 | this.regexIgnoredClasses = regexIgnoredClasses; 387 | } 388 | 389 | public void setUseClassesFromLastBuild(boolean useClassesFromLastBuild) { 390 | this.useClassesFromLastBuild = useClassesFromLastBuild; 391 | } 392 | 393 | public void setSuppressTypesFromJavaRuntime(boolean suppressTypesFromJavaRuntime) { 394 | this.suppressTypesFromJavaRuntime = suppressTypesFromJavaRuntime; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /src/main/java/de/is24/maven/enforcer/rules/Repository.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import java.util.Collections; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import static java.lang.String.format; 8 | 9 | 10 | final class Repository { 11 | 12 | private final Set types = new HashSet(); 13 | private final Set dependencies = new HashSet(); 14 | 15 | private final ClassFilter filter; 16 | 17 | Repository(ClassFilter filter) { 18 | this.filter = filter; 19 | } 20 | 21 | Set getTypes() { 22 | return Collections.unmodifiableSet(types); 23 | } 24 | 25 | Set getDependencies() { 26 | return Collections.unmodifiableSet(dependencies); 27 | } 28 | 29 | void addType(String type) { 30 | filter.addFiltered(types, type); 31 | } 32 | 33 | void addDependency(String type) { 34 | filter.addFiltered(dependencies, type); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return format("Repository{types=%s, dependencies=%s}", types, dependencies); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/de/is24/maven/enforcer/rules/Types.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.objectweb.asm.Type; 4 | 5 | 6 | final class Types { 7 | private Types() { 8 | } 9 | 10 | static String readType(Type type) { 11 | switch (type.getSort()) { 12 | case Type.ARRAY: { 13 | return readType(type.getElementType()); 14 | } 15 | 16 | default: { 17 | return type.getClassName(); 18 | } 19 | } 20 | } 21 | 22 | static String readValueType(Object value) { 23 | final Type type; 24 | if (value instanceof Type) { 25 | type = (Type) value; 26 | } else { 27 | type = Type.getType(value.getClass()); 28 | } 29 | return readType(type); 30 | } 31 | 32 | static String readTypeDescription(String description) { 33 | final Type type = Type.getType(description); 34 | return readType(type); 35 | } 36 | 37 | static String readInternalTypeName(String internalName) { 38 | final Type type = Type.getObjectType(internalName); 39 | return readType(type); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/ArtifactRepositoryAnalyzerTest.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.artifact.Artifact; 4 | import org.apache.maven.plugin.testing.stubs.ArtifactStub; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | import java.util.Collections; 9 | 10 | import static org.hamcrest.CoreMatchers.containsString; 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.junit.Assert.assertThat; 13 | import static org.junit.Assert.fail; 14 | 15 | 16 | public class ArtifactRepositoryAnalyzerTest { 17 | @Test 18 | public void analyzeEmptyArtifact() { 19 | final LogStub logger = new LogStub(); 20 | final ArtifactRepositoryAnalyzer analyzer = ArtifactRepositoryAnalyzer.analyzeArtifacts( 21 | logger, 22 | false, 23 | new ClassFilter(logger, false)); 24 | 25 | final Artifact artifact = makeArtifact(null); 26 | 27 | final Repository repository = analyzer.analyzeArtifacts( 28 | Collections.singleton(artifact)); 29 | 30 | assertThat(repository.getTypes().isEmpty(), is(true)); 31 | assertThat(repository.getDependencies().isEmpty(), is(true)); 32 | assertThat(logger.getInfoLog(), 33 | containsString("has no associated file, skip it.")); 34 | } 35 | 36 | @Test 37 | public void analyzePomArtifact() { 38 | final LogStub logger = new LogStub(); 39 | final ArtifactRepositoryAnalyzer analyzer = ArtifactRepositoryAnalyzer.analyzeArtifacts( 40 | logger, 41 | false, 42 | new ClassFilter(logger, false)); 43 | 44 | final Artifact artifact = makeArtifact(new File("pom.xml")); 45 | 46 | final Repository repository = analyzer.analyzeArtifacts( 47 | Collections.singleton(artifact)); 48 | 49 | assertThat(repository.getTypes().isEmpty(), is(true)); 50 | assertThat(repository.getDependencies().isEmpty(), is(true)); 51 | assertThat(logger.getInfoLog(), containsString("pom.xml', is skipped")); 52 | } 53 | 54 | @Test 55 | public void invalidJarArtifact() { 56 | final LogStub logger = new LogStub(); 57 | final ArtifactRepositoryAnalyzer analyzer = ArtifactRepositoryAnalyzer.analyzeArtifacts( 58 | logger, 59 | false, 60 | new ClassFilter(logger, false)); 61 | 62 | final Artifact artifact = makeArtifact(new File("invalid.jar")); 63 | 64 | final String expectedErrorMessage = "Unable to read class(es) from artifact 'invalid.jar'."; 65 | try { 66 | analyzer.analyzeArtifacts(Collections.singleton(artifact)); 67 | fail("IllegalStateException expected!"); 68 | } catch (IllegalStateException e) { 69 | assertThat(e.getMessage(), is(expectedErrorMessage)); 70 | } 71 | 72 | assertThat(logger.getErrorLog(), containsString(expectedErrorMessage)); 73 | } 74 | 75 | @Test 76 | public void readTypesFromClassDirectory() { 77 | final LogStub logger = new LogStub(); 78 | final ArtifactRepositoryAnalyzer analyzer = ArtifactRepositoryAnalyzer.analyzeArtifacts( 79 | logger, 80 | false, 81 | new ClassFilter(logger, false)); 82 | 83 | final File classFile = getCurrentClassFile(); 84 | final File classesDirectory = new File(classFile.getParent()); 85 | final Artifact artifact = makeArtifact(classesDirectory); 86 | 87 | final Repository repository = analyzer.analyzeArtifacts( 88 | Collections.singleton(artifact)); 89 | 90 | assertThat(repository.getTypes().isEmpty(), is(false)); 91 | assertThat(repository.getDependencies().isEmpty(), is(true)); 92 | assertThat(logger.getDebugLog(), containsString(classFile.getPath())); 93 | } 94 | 95 | private File getCurrentClassFile() { 96 | final String resourcePath = "/" + ArtifactRepositoryAnalyzerTest.class.getName().replace(".", "/") + ".class"; 97 | return new File(ArtifactRepositoryAnalyzerTest.class.getResource(resourcePath).getFile()); 98 | } 99 | 100 | private Artifact makeArtifact(File file) { 101 | final Artifact artifact = new ArtifactStub(); 102 | artifact.setArtifactId("artifactId"); 103 | artifact.setGroupId("groupId"); 104 | artifact.setScope("scope"); 105 | artifact.setVersion("0.123456789"); 106 | artifact.setFile(file); 107 | return artifact; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/ClassFileReference.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.artifact.Artifact; 4 | import org.apache.maven.model.Build; 5 | import org.apache.maven.project.MavenProject; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.net.URL; 15 | import java.util.HashSet; 16 | import java.util.Set; 17 | import java.util.zip.ZipEntry; 18 | import java.util.zip.ZipOutputStream; 19 | 20 | import static java.lang.String.format; 21 | 22 | final class ClassFileReference { 23 | private static final Logger LOG = LoggerFactory.getLogger(ClassFileReference.class); 24 | 25 | private static final String CLASS_SUFFIX = ".class"; 26 | 27 | private final String resource; 28 | private final File classFile; 29 | 30 | private ClassFileReference(String resource, File classFile) { 31 | this.resource = resource; 32 | this.classFile = classFile; 33 | } 34 | 35 | private static File replaceJarWithPacketClassFile(File jar, Set classFilesInJarReference) { 36 | final String fileName = jar.getAbsolutePath(); 37 | jar.delete(); 38 | 39 | final File newJar = new File(fileName); 40 | 41 | try { 42 | try(ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(jar))) { 43 | for (ClassFileReference classFileReference : classFilesInJarReference) { 44 | try(InputStream in = new FileInputStream(classFileReference.getClassFile())) { 45 | final byte[] buffer = new byte[1024]; 46 | 47 | zipOutputStream.putNextEntry(new ZipEntry(classFileReference.getResource())); 48 | 49 | int bytesRead = in.read(buffer); 50 | do { 51 | zipOutputStream.write(buffer, 0, bytesRead); 52 | bytesRead = in.read(buffer); 53 | } while (bytesRead > 0); 54 | zipOutputStream.closeEntry(); 55 | } 56 | } 57 | } 58 | } catch (IOException e) { 59 | final String error = "Unable to pack class files '" + classFilesInJarReference + "'!"; 60 | LOG.error(error, e); 61 | throw new IllegalStateException(error, e); 62 | } 63 | return newJar; 64 | } 65 | 66 | private static Set makeClassFileSet(Class... classes) { 67 | final Set fileEntries = new HashSet<>(); 68 | for (Class clazz : classes) { 69 | final ClassLoader classLoader = clazz.getClassLoader(); 70 | final String resource = clazz.getName().replace('.', '/') + CLASS_SUFFIX; 71 | 72 | // validate that the class file is accessible.. 73 | final URL url = classLoader.getResource(resource); 74 | if (url == null) { 75 | final String error = "Test class file '" + resource + "' not readable!"; 76 | LOG.error(error); 77 | throw new IllegalStateException(error); 78 | } 79 | 80 | final File classFile = new File(url.getFile()); 81 | fileEntries.add(new ClassFileReference(resource, classFile)); 82 | } 83 | return fileEntries; 84 | } 85 | 86 | static void prepareArtifactTargetClassesDirectory(MavenProject project, Class clazz) { 87 | final ClassFileReference classFileReference = makeClassFileSet(clazz).iterator().next(); 88 | final Build build = new Build(); 89 | project.setBuild(build); 90 | build.setDirectory(classFileReference.getClassFile().getParentFile().getAbsolutePath()); 91 | build.setOutputDirectory(classFileReference.getClassFile().getParentFile().getAbsolutePath()); 92 | } 93 | 94 | static void makeArtifactJarFromClassFile(Artifact artifact, Class... classes) { 95 | artifact.setFile(replaceJarWithPacketClassFile(artifact.getFile(), makeClassFileSet(classes))); 96 | } 97 | 98 | String getResource() { 99 | return resource; 100 | } 101 | 102 | File getClassFile() { 103 | return classFile; 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return format("ClassFileReference{resource='%s', classFile=%s}", resource, classFile); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/ClassFilterTest.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.apache.maven.plugin.logging.Log; 5 | import org.junit.Test; 6 | 7 | import javax.sql.DataSource; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | import static org.hamcrest.CoreMatchers.is; 12 | import static org.junit.Assert.assertThat; 13 | 14 | public class ClassFilterTest { 15 | private final Log logger = new LogStub(); 16 | 17 | 18 | @Test 19 | public void testSuppressionOfNativeTypes() throws Exception { 20 | final ClassFilter filter = new ClassFilter(logger, true); 21 | assertThat(filter.isConsideredType("byte"), is(false)); 22 | assertThat(filter.isConsideredType("int"), is(false)); 23 | assertThat(filter.isConsideredType("long"), is(false)); 24 | 25 | final Set types = new HashSet<>(); 26 | filter.addFiltered(types, "char"); 27 | filter.addFiltered(types, "float"); 28 | filter.addFiltered(types, "double"); 29 | assertThat(types.isEmpty(), is(true)); 30 | } 31 | 32 | @Test 33 | public void testSuppressionOfJdkTypes() { 34 | final ClassFilter filter = new ClassFilter(logger, true); 35 | final Set types = new HashSet<>(); 36 | 37 | // add a package not in the current JDK 38 | assertThat(filter.isConsideredType(StringUtils.class.getName()), is(true)); 39 | 40 | // add a package that is part of all JDKs 41 | assertThat(filter.isConsideredType(DataSource.class.getName()), is(false)); 42 | 43 | // the same tests for filtered adding 44 | filter.addFiltered(types, StringUtils.class.getName()); 45 | filter.addFiltered(types, DataSource.class.getName()); 46 | 47 | assertThat(types.size(), is(1)); 48 | assertThat(types.iterator().next(), is(StringUtils.class.getName())); 49 | } 50 | 51 | @Test 52 | public void testSuppressionOfClasses() { 53 | final ClassFilter filter = new ClassFilter(logger, false, "de\\.is24\\.suppress.*", ".*SuppressMe.*"); 54 | 55 | assertThat(filter.isConsideredType("de.is24.package.Type"), is(true)); 56 | assertThat(filter.isConsideredType("de.is24.package.subpackage.Type"), is(true)); 57 | assertThat(filter.isConsideredType("de.is24.package.Type$Subtype"), is(true)); 58 | 59 | assertThat(filter.isConsideredType("de.is24.suppress.subpackage.Type"), is(false)); 60 | assertThat(filter.isConsideredType("de.is24.suppress.Type"), is(false)); 61 | assertThat(filter.isConsideredType("de.is24.suppress.Type$SubType"), is(false)); 62 | 63 | assertThat(filter.isConsideredType("de.is24.SuppressMe"), is(false)); 64 | assertThat(filter.isConsideredType("de.is24.SuppressMe$Subtype"), is(false)); 65 | } 66 | } -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/EnforcerRuleHelperWrapper.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.artifact.Artifact; 4 | import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 5 | import org.codehaus.plexus.PlexusContainer; 6 | import org.codehaus.plexus.PlexusContainerException; 7 | import org.codehaus.plexus.classworlds.realm.ClassRealm; 8 | import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; 9 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 10 | import org.codehaus.plexus.component.discovery.ComponentDiscoveryListener; 11 | import org.codehaus.plexus.component.repository.ComponentDescriptor; 12 | import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; 13 | import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 14 | import org.codehaus.plexus.configuration.PlexusConfigurationException; 15 | import org.codehaus.plexus.context.Context; 16 | import java.io.File; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Set; 21 | 22 | 23 | final class EnforcerRuleHelperWrapper implements EnforcerRuleHelper { 24 | private final EnforcerRuleHelper wrappedEnforcerRuleHelper; 25 | private final Map components = new HashMap<>(); 26 | 27 | private final PlexusContainerWrapper plexusContainerWrapper; 28 | private final LogStub logStub = new LogStub(); 29 | 30 | private Artifact projectArtifact; 31 | private Artifact directDependencyArtifact; 32 | private Set transitiveDependencyArtifacts; 33 | 34 | public Artifact getDirectDependencyArtifact() { 35 | return directDependencyArtifact; 36 | } 37 | 38 | public void setDirectDependencyArtifact(Artifact directDependencyArtifact) { 39 | this.directDependencyArtifact = directDependencyArtifact; 40 | } 41 | 42 | public Set getTransitiveDependencyArtifacts() { 43 | return transitiveDependencyArtifacts; 44 | } 45 | 46 | public void setTransitiveDependencyArtifacts(Set transitiveDependencyArtifacts) { 47 | this.transitiveDependencyArtifacts = transitiveDependencyArtifacts; 48 | } 49 | 50 | public Artifact getProjectArtifact() { 51 | return projectArtifact; 52 | } 53 | 54 | public void setProjectArtifact(Artifact projectArtifact) { 55 | this.projectArtifact = projectArtifact; 56 | } 57 | 58 | EnforcerRuleHelperWrapper(EnforcerRuleHelper wrappedEnforcerRuleHelper) { 59 | this.wrappedEnforcerRuleHelper = wrappedEnforcerRuleHelper; 60 | plexusContainerWrapper = new PlexusContainerWrapper(wrappedEnforcerRuleHelper.getContainer()); 61 | } 62 | 63 | void addComponent(Object component, Class key) { 64 | components.put(key.getName(), component); 65 | } 66 | 67 | @Override 68 | public LogStub getLog() { 69 | return logStub; 70 | } 71 | 72 | @Override 73 | public Object getComponent(Class clazz) throws ComponentLookupException { 74 | return getComponent(clazz.getName()); 75 | } 76 | 77 | @Override 78 | public Object getComponent(String componentKey) throws ComponentLookupException { 79 | if (components.containsKey(componentKey)) { 80 | return components.get(componentKey); 81 | } 82 | return wrappedEnforcerRuleHelper.getComponent(componentKey); 83 | } 84 | 85 | @Override 86 | public Object getComponent(String role, String roleHint) throws ComponentLookupException { 87 | return wrappedEnforcerRuleHelper.getComponent(role, roleHint); 88 | } 89 | 90 | @Override 91 | public Map getComponentMap(String role) throws ComponentLookupException { 92 | return wrappedEnforcerRuleHelper.getComponentMap(role); 93 | } 94 | 95 | @Override 96 | public List getComponentList(String role) throws ComponentLookupException { 97 | return wrappedEnforcerRuleHelper.getComponentList(role); 98 | } 99 | 100 | 101 | @Override 102 | public PlexusContainerWrapper getContainer() { 103 | return plexusContainerWrapper; 104 | } 105 | 106 | @Override 107 | public Object evaluate(String expression) throws ExpressionEvaluationException { 108 | return wrappedEnforcerRuleHelper.evaluate(expression); 109 | } 110 | 111 | @Override 112 | public File alignToBaseDirectory(File file) { 113 | return wrappedEnforcerRuleHelper.alignToBaseDirectory(file); 114 | } 115 | 116 | public static final class PlexusContainerWrapper implements PlexusContainer { 117 | private final PlexusContainer plexusContainer; 118 | 119 | private final Map objects = new HashMap(); 120 | 121 | private PlexusContainerWrapper(PlexusContainer plexusContainer) { 122 | this.plexusContainer = plexusContainer; 123 | } 124 | 125 | @Override 126 | public Object lookup(String role) throws ComponentLookupException { 127 | return plexusContainer.lookup(role); 128 | } 129 | 130 | @Override 131 | public Object lookup(String role, String roleHint) throws ComponentLookupException { 132 | return plexusContainer.lookup(role, roleHint); 133 | } 134 | 135 | @Override 136 | public T lookup(Class type) throws ComponentLookupException { 137 | return plexusContainer.lookup(type); 138 | } 139 | 140 | @Override 141 | @SuppressWarnings("unchecked") 142 | public T lookup(Class type, String roleHint) throws ComponentLookupException { 143 | final T object = (T) objects.get(type.getCanonicalName() + "#" + roleHint); 144 | return (object != null) ? object : plexusContainer.lookup(type, roleHint); 145 | } 146 | 147 | @Override 148 | public T lookup(Class type, String role, String roleHint) throws ComponentLookupException { 149 | return plexusContainer.lookup(type, role, roleHint); 150 | } 151 | 152 | @Override 153 | public T lookup(ComponentDescriptor componentDescriptor) throws ComponentLookupException { 154 | return plexusContainer.lookup(componentDescriptor); 155 | } 156 | 157 | @Override 158 | public List lookupList(String role) throws ComponentLookupException { 159 | return plexusContainer.lookupList(role); 160 | } 161 | 162 | @Override 163 | public List lookupList(String role, List roleHints) throws ComponentLookupException { 164 | return plexusContainer.lookupList(role, roleHints); 165 | } 166 | 167 | @Override 168 | public List lookupList(Class type) throws ComponentLookupException { 169 | return plexusContainer.lookupList(type); 170 | } 171 | 172 | @Override 173 | public List lookupList(Class type, List roleHints) throws ComponentLookupException { 174 | return plexusContainer.lookupList(type, roleHints); 175 | } 176 | 177 | @Override 178 | public Map lookupMap(String role) throws ComponentLookupException { 179 | return plexusContainer.lookupMap(role); 180 | } 181 | 182 | @Override 183 | public Map lookupMap(String role, List roleHints) throws ComponentLookupException { 184 | return plexusContainer.lookupMap(role, roleHints); 185 | } 186 | 187 | @Override 188 | public Map lookupMap(Class type) throws ComponentLookupException { 189 | return plexusContainer.lookupMap(type); 190 | } 191 | 192 | @Override 193 | public Map lookupMap(Class type, List roleHints) throws ComponentLookupException { 194 | return plexusContainer.lookupMap(type, roleHints); 195 | } 196 | 197 | @Override 198 | public ComponentDescriptor getComponentDescriptor(String role) { 199 | return plexusContainer.getComponentDescriptor(role); 200 | } 201 | 202 | @Override 203 | public ComponentDescriptor getComponentDescriptor(String role, String roleHint) { 204 | return plexusContainer.getComponentDescriptor(role, roleHint); 205 | } 206 | 207 | @Override 208 | public ComponentDescriptor getComponentDescriptor(Class type, String role, String roleHint) { 209 | return plexusContainer.getComponentDescriptor(type, role, roleHint); 210 | } 211 | 212 | @Override 213 | public Map> getComponentDescriptorMap(String role) { 214 | return plexusContainer.getComponentDescriptorMap(role); 215 | } 216 | 217 | @Override 218 | public Map> getComponentDescriptorMap(Class type, String role) { 219 | return plexusContainer.getComponentDescriptorMap(type, role); 220 | } 221 | 222 | @Override 223 | public List> getComponentDescriptorList(String role) { 224 | return plexusContainer.getComponentDescriptorList(role); 225 | } 226 | 227 | @Override 228 | public List> getComponentDescriptorList(Class type, String role) { 229 | return plexusContainer.getComponentDescriptorList(type, role); 230 | } 231 | 232 | @Override 233 | public void addComponentDescriptor(ComponentDescriptor componentDescriptor) 234 | throws CycleDetectedInComponentGraphException { 235 | plexusContainer.addComponentDescriptor(componentDescriptor); 236 | } 237 | 238 | @Override 239 | public void release(Object component) throws ComponentLifecycleException { 240 | plexusContainer.release(component); 241 | } 242 | 243 | @Override 244 | public void releaseAll(Map components) throws ComponentLifecycleException { 245 | plexusContainer.releaseAll(components); 246 | } 247 | 248 | @Override 249 | public void releaseAll(List components) throws ComponentLifecycleException { 250 | plexusContainer.releaseAll(components); 251 | } 252 | 253 | @Override 254 | public boolean hasComponent(String role) { 255 | return plexusContainer.hasComponent(role); 256 | } 257 | 258 | @Override 259 | public boolean hasComponent(String role, String roleHint) { 260 | return plexusContainer.hasComponent(role, roleHint); 261 | } 262 | 263 | @Override 264 | public boolean hasComponent(Class type) { 265 | return plexusContainer.hasComponent(type); 266 | } 267 | 268 | @Override 269 | public boolean hasComponent(Class type, String roleHint) { 270 | return plexusContainer.hasComponent(type, roleHint); 271 | } 272 | 273 | @Override 274 | public boolean hasComponent(Class type, String role, String roleHint) { 275 | return plexusContainer.hasComponent(type, role, roleHint); 276 | } 277 | 278 | @Override 279 | public void dispose() { 280 | plexusContainer.dispose(); 281 | } 282 | 283 | @Override 284 | public void addContextValue(Object key, Object value) { 285 | plexusContainer.addContextValue(key, value); 286 | } 287 | 288 | @Override 289 | public Context getContext() { 290 | return plexusContainer.getContext(); 291 | } 292 | 293 | @Override 294 | public ClassRealm getContainerRealm() { 295 | return plexusContainer.getContainerRealm(); 296 | } 297 | 298 | @Override 299 | public void registerComponentDiscoveryListener(ComponentDiscoveryListener listener) { 300 | plexusContainer.registerComponentDiscoveryListener(listener); 301 | } 302 | 303 | @Override 304 | public void removeComponentDiscoveryListener(ComponentDiscoveryListener listener) { 305 | plexusContainer.removeComponentDiscoveryListener(listener); 306 | } 307 | 308 | @Override 309 | public List> discoverComponents(ClassRealm childRealm) 310 | throws PlexusConfigurationException, 311 | CycleDetectedInComponentGraphException { 312 | return plexusContainer.discoverComponents(childRealm); 313 | } 314 | 315 | @Override 316 | public List> discoverComponents(ClassRealm realm, Object data) 317 | throws PlexusConfigurationException, 318 | CycleDetectedInComponentGraphException { 319 | return plexusContainer.discoverComponents(realm, data); 320 | } 321 | 322 | @Override 323 | public ClassRealm createChildRealm(String id) { 324 | return plexusContainer.createChildRealm(id); 325 | } 326 | 327 | @Override 328 | public ClassRealm getComponentRealm(String realmId) { 329 | return plexusContainer.getComponentRealm(realmId); 330 | } 331 | 332 | @Override 333 | public void removeComponentRealm(ClassRealm componentRealm) throws PlexusContainerException { 334 | plexusContainer.removeComponentRealm(componentRealm); 335 | } 336 | 337 | @Override 338 | public ClassRealm getLookupRealm() { 339 | return plexusContainer.getLookupRealm(); 340 | } 341 | 342 | @Override 343 | public ClassRealm setLookupRealm(ClassRealm realm) { 344 | return plexusContainer.setLookupRealm(realm); 345 | } 346 | 347 | @Override 348 | public ClassRealm getLookupRealm(Object component) { 349 | return plexusContainer.getLookupRealm(component); 350 | } 351 | 352 | @Override 353 | public void addComponent(Object component, String role) throws CycleDetectedInComponentGraphException { 354 | plexusContainer.addComponent(component, role); 355 | } 356 | 357 | @Override 358 | public void addComponent(T component, Class role, String roleHint) { 359 | objects.put(role.getCanonicalName() + "#" + roleHint, component); 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/IllegalTransitiveDependencyCheckTest.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import de.is24.maven.enforcer.rules.testtypes.ClassInAnotherTransitiveDependency; 4 | import de.is24.maven.enforcer.rules.testtypes.ClassInAnotherTransitiveDependency.EnumInClassInAnotherTransitiveDependency; 5 | import de.is24.maven.enforcer.rules.testtypes.ClassInDirectDependency; 6 | import de.is24.maven.enforcer.rules.testtypes.ClassInDirectDependency.EnumInClassInDirectDependency; 7 | import de.is24.maven.enforcer.rules.testtypes.ClassInMavenProjectSource; 8 | import de.is24.maven.enforcer.rules.testtypes.ClassInTransitiveDependency; 9 | import de.is24.maven.enforcer.rules.testtypes.ClassInTransitiveDependency.SomeUsefulAnnotation; 10 | import org.apache.maven.artifact.Artifact; 11 | import org.apache.maven.artifact.resolver.ArtifactResolver; 12 | import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 13 | import org.apache.maven.enforcer.rule.api.EnforcerRule; 14 | import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 15 | import org.apache.maven.model.Build; 16 | import org.apache.maven.plugin.testing.ArtifactStubFactory; 17 | import org.apache.maven.plugin.testing.stubs.StubArtifactResolver; 18 | import org.apache.maven.plugins.enforcer.EnforcerTestUtils; 19 | import org.apache.maven.plugins.enforcer.MockProject; 20 | import org.apache.maven.plugins.enforcer.utils.TestEnforcerRuleUtils; 21 | import org.apache.maven.project.MavenProject; 22 | import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; 23 | import org.apache.maven.shared.dependency.graph.DependencyNode; 24 | import org.apache.maven.shared.dependency.graph.internal.DefaultDependencyNode; 25 | import org.codehaus.plexus.PlexusContainer; 26 | import org.junit.Before; 27 | import org.junit.Rule; 28 | import org.junit.Test; 29 | import org.junit.rules.TemporaryFolder; 30 | 31 | import java.io.IOException; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.Collections; 35 | import java.util.HashSet; 36 | import java.util.List; 37 | import java.util.Set; 38 | 39 | import static org.hamcrest.CoreMatchers.*; 40 | import static org.junit.Assert.assertThat; 41 | 42 | 43 | public class IllegalTransitiveDependencyCheckTest { 44 | private static final String ARTIFACT_ID = "some-artifact"; 45 | private static final String DEPENDENCY_ARTIFACT_ID = "dependency-artifact"; 46 | private static final String TRANSITIVE_DEPENDENCY_ARTIFACT_ID = "transitive-dependency-artifact"; 47 | private static final String GROUP_ID = "some-group"; 48 | private static final String ARTIFACT_VERSION = "1.0"; 49 | @Rule 50 | public final TemporaryFolder folder = new TemporaryFolder(); 51 | private ArtifactStubFactory factory; 52 | 53 | @Before 54 | public void prepareArtifactStubFactory() throws IOException { 55 | factory = new ArtifactStubFactory(); 56 | factory.setWorkingDir(folder.newFolder("repository")); 57 | factory.setCreateFiles(true); 58 | } 59 | 60 | @Test 61 | public void ruleIsNotCacheable() { 62 | assertThat(new IllegalTransitiveDependencyCheck().isCacheable(), is(false)); 63 | assertThat(new IllegalTransitiveDependencyCheck().getCacheId(), nullValue()); 64 | assertThat(new IllegalTransitiveDependencyCheck().isResultValid(null), is(false)); 65 | } 66 | 67 | @Test 68 | public void ruleFiresExceptionOnTransitiveDependency() throws IOException { 69 | final EnforcerRuleHelper helper = prepareProjectWithIllegalTransitiveDependencies(ArtifactFileType.JAR); 70 | 71 | final EnforcerRule rule = new IllegalTransitiveDependencyCheck(); 72 | 73 | TestEnforcerRuleUtils.execute(rule, helper, true); 74 | } 75 | 76 | @Test 77 | public void ruleLogsOnlyTransitiveDependency() throws IOException { 78 | final EnforcerRuleHelperWrapper helper = prepareProjectWithIllegalTransitiveDependencies(ArtifactFileType.JAR); 79 | 80 | final IllegalTransitiveDependencyCheck rule = new IllegalTransitiveDependencyCheck(); 81 | rule.setReportOnly(true); 82 | rule.setRegexIgnoredClasses(new String[]{""}); 83 | 84 | TestEnforcerRuleUtils.execute(rule, helper, false); 85 | 86 | assertNumberOfIllegalTransitiveDependencies(helper, 7); 87 | assertNonJdkDependenciesAreListed(helper); 88 | assertJdkDependenciesAreListed(helper); 89 | } 90 | 91 | @Test 92 | public void ruleLogsTransitiveDependenciesWithArtifacts() throws IOException { 93 | final EnforcerRuleHelperWrapper helper = prepareProjectWithIllegalTransitiveDependencies(ArtifactFileType.JAR); 94 | 95 | final IllegalTransitiveDependencyCheck rule = new IllegalTransitiveDependencyCheck(); 96 | rule.setReportOnly(true); 97 | rule.setRegexIgnoredClasses(new String[]{""}); 98 | rule.setListMissingArtifacts(true); 99 | 100 | final PlexusContainer container = helper.getContainer(); 101 | 102 | final DependencyGraphBuilder dependencyGraphBuilder = new DependencyGraphBuilder() { 103 | @Override 104 | public DependencyNode buildDependencyGraph(MavenProject mavenProject, ArtifactFilter artifactFilter) { 105 | final DefaultDependencyNode root = new DefaultDependencyNode(null, 106 | mavenProject.getArtifact(), 107 | null, 108 | null, 109 | null); 110 | 111 | final DefaultDependencyNode direct = new DefaultDependencyNode(root, 112 | helper.getDirectDependencyArtifact(), 113 | null, 114 | null, 115 | null); 116 | 117 | root.setChildren(Collections.singletonList(direct)); 118 | 119 | final List transitives = new ArrayList(); 120 | for (Artifact transitiveArtifact : helper.getTransitiveDependencyArtifacts()) { 121 | final DependencyNode transitive = new DefaultDependencyNode(direct, 122 | transitiveArtifact, 123 | null, 124 | null, 125 | null); 126 | transitives.add(transitive); 127 | } 128 | 129 | direct.setChildren(transitives); 130 | 131 | return root; 132 | } 133 | }; 134 | 135 | container.addComponent(dependencyGraphBuilder, DependencyGraphBuilder.class, "default"); 136 | 137 | TestEnforcerRuleUtils.execute(rule, helper, false); 138 | 139 | assertNumberOfIllegalTransitiveDependencies(helper, 7); 140 | assertNonJdkDependenciesAreListedWithArtifactId(helper); 141 | } 142 | 143 | @Test 144 | public void tryToUseExistingTargetClassesDirectory() throws IOException { 145 | final EnforcerRuleHelperWrapper helper = prepareProjectWithIllegalTransitiveDependencies( 146 | ArtifactFileType.TARGET_CLASSES); 147 | final IllegalTransitiveDependencyCheck rule = new IllegalTransitiveDependencyCheck(); 148 | 149 | rule.setReportOnly(true); 150 | rule.setRegexIgnoredClasses(new String[]{""}); 151 | rule.setUseClassesFromLastBuild(true); 152 | 153 | TestEnforcerRuleUtils.execute(rule, helper, false); 154 | 155 | assertNumberOfIllegalTransitiveDependencies(helper, 5); 156 | assertJdkDependenciesAreListed(helper); 157 | } 158 | 159 | @Test 160 | public void tryToUseMissingTargetClassesDirectory() throws IOException { 161 | final EnforcerRuleHelperWrapper helper = prepareProjectWithIllegalTransitiveDependencies(ArtifactFileType.NOTHING); 162 | final IllegalTransitiveDependencyCheck rule = new IllegalTransitiveDependencyCheck(); 163 | 164 | rule.setReportOnly(true); 165 | rule.setRegexIgnoredClasses(new String[]{""}); 166 | rule.setUseClassesFromLastBuild(true); 167 | 168 | TestEnforcerRuleUtils.execute(rule, helper, false); 169 | 170 | assertThat(helper.getLog().getDebugLog(), 171 | containsString("No target/classes directory found")); 172 | 173 | assertThat(helper.getLog().getInfoLog(), 174 | containsString("Nothing to analyze in 'some-group:some-artifact:jar:1.0'")); 175 | } 176 | 177 | @Test 178 | public void suppressTypesFromJavaRuntime() throws IOException { 179 | final EnforcerRuleHelperWrapper helper = prepareProjectWithIllegalTransitiveDependencies(ArtifactFileType.JAR); 180 | final IllegalTransitiveDependencyCheck rule = new IllegalTransitiveDependencyCheck(); 181 | 182 | rule.setReportOnly(true); 183 | rule.setSuppressTypesFromJavaRuntime(true); 184 | rule.setRegexIgnoredClasses(new String[]{""}); 185 | 186 | TestEnforcerRuleUtils.execute(rule, helper, false); 187 | 188 | assertThat(helper.getLog().getWarnLog(), 189 | containsString("Project's output directory has not been set, skip writing!")); 190 | 191 | assertThat(helper.getLog().getDebugLog(), 192 | containsString("Suppress type 'com.sun.management.DiagnosticCommandMBean', it's in current Java runtime")); 193 | 194 | assertNumberOfIllegalTransitiveDependencies(helper, 3); 195 | assertNonJdkDependenciesAreListed(helper); 196 | } 197 | 198 | private void assertNumberOfIllegalTransitiveDependencies(EnforcerRuleHelperWrapper helper, int number) { 199 | assertThat(helper.getLog().getErrorLog(), 200 | containsString( 201 | "Found " + number + " illegal transitive type dependencies in artifact 'some-group:some-artifact:jar:1.0")); 202 | } 203 | 204 | private void assertNonJdkDependenciesAreListed(EnforcerRuleHelperWrapper helper) { 205 | final String errorLog = helper.getLog().getErrorLog(); 206 | assertThat(errorLog, 207 | containsString("de.is24.maven.enforcer.rules.testtypes.ClassInAnotherTransitiveDependency")); 208 | assertThat(errorLog, 209 | containsString("de.is24.maven.enforcer.rules.testtypes.ClassInTransitiveDependency")); 210 | assertThat(errorLog, 211 | containsString("de.is24.maven.enforcer.rules.testtypes.ClassInTransitiveDependency$SomeUsefulAnnotation")); 212 | } 213 | 214 | private void assertNonJdkDependenciesAreListedWithArtifactId(EnforcerRuleHelperWrapper helper) { 215 | final String errorLog = helper.getLog().getErrorLog(); 216 | assertThat(errorLog, 217 | containsString( 218 | "de.is24.maven.enforcer.rules.testtypes.ClassInAnotherTransitiveDependency, [some-group:transitive-dependency-artifact2:jar:1.0]")); 219 | assertThat(errorLog, 220 | containsString( 221 | "de.is24.maven.enforcer.rules.testtypes.ClassInTransitiveDependency, [some-group:transitive-dependency-artifact:jar:1.0]")); 222 | assertThat(errorLog, 223 | containsString( 224 | "de.is24.maven.enforcer.rules.testtypes.ClassInTransitiveDependency$SomeUsefulAnnotation, [some-group:transitive-dependency-artifact:jar:1.0]")); 225 | } 226 | 227 | private void assertJdkDependenciesAreListed(EnforcerRuleHelperWrapper helper) { 228 | final String errorLog = helper.getLog().getErrorLog(); 229 | assertThat(errorLog, 230 | containsString("com.sun.management.DiagnosticCommandMBean")); 231 | assertThat(errorLog, 232 | containsString("javax.sql.DataSource")); 233 | assertThat(errorLog, 234 | containsString("jdk.Exported")); 235 | assertThat(errorLog, 236 | containsString("org.w3c.dom.Text")); 237 | } 238 | 239 | private EnforcerRuleHelperWrapper prepareProjectWithIllegalTransitiveDependencies(ArtifactFileType artifactFileType) 240 | throws IOException { 241 | final MockProject project = new MockProject() { 242 | private Build build; 243 | 244 | @Override 245 | public Build getBuild() { 246 | return build; 247 | } 248 | 249 | @Override 250 | public void setBuild(Build build) { 251 | this.build = build; 252 | } 253 | }; 254 | 255 | final EnforcerRuleHelperWrapper helper = new EnforcerRuleHelperWrapper(EnforcerTestUtils.getHelper(project)); 256 | helper.addComponent(new StubArtifactResolver(factory, false, false), ArtifactResolver.class); 257 | 258 | 259 | final Artifact artifact = factory.createArtifact(GROUP_ID, ARTIFACT_ID, ARTIFACT_VERSION); 260 | project.setArtifact(artifact); 261 | project.setArtifactId(ARTIFACT_ID); 262 | project.setGroupId(GROUP_ID); 263 | project.setVersion(ARTIFACT_VERSION); 264 | 265 | switch (artifactFileType) { 266 | case JAR: { 267 | ClassFileReference.makeArtifactJarFromClassFile(artifact, ClassInMavenProjectSource.class); 268 | break; 269 | } 270 | 271 | case TARGET_CLASSES: { 272 | ClassFileReference.prepareArtifactTargetClassesDirectory(project, ClassInMavenProjectSource.class); 273 | break; 274 | } 275 | 276 | case NOTHING: { 277 | artifact.setFile(null); 278 | break; 279 | } 280 | 281 | default: { 282 | throw new IllegalStateException("Unexpected type " + artifactFileType); 283 | } 284 | } 285 | 286 | final Artifact dependency = factory.createArtifact(GROUP_ID, DEPENDENCY_ARTIFACT_ID, ARTIFACT_VERSION); 287 | 288 | // add the direct dependency and it's children 289 | ClassFileReference.makeArtifactJarFromClassFile(dependency, 290 | ClassInDirectDependency.class, 291 | EnumInClassInDirectDependency.class); 292 | 293 | 294 | final Artifact transitiveDependency = factory.createArtifact(GROUP_ID, 295 | TRANSITIVE_DEPENDENCY_ARTIFACT_ID, 296 | ARTIFACT_VERSION); 297 | 298 | // add the transitive dependency and the enclosed annotation 299 | ClassFileReference.makeArtifactJarFromClassFile(transitiveDependency, 300 | ClassInTransitiveDependency.class, 301 | SomeUsefulAnnotation.class); 302 | 303 | final Artifact anotherTransitiveDependency = factory.createArtifact(GROUP_ID, 304 | TRANSITIVE_DEPENDENCY_ARTIFACT_ID + "2", 305 | ARTIFACT_VERSION); 306 | 307 | ClassFileReference.makeArtifactJarFromClassFile(anotherTransitiveDependency, 308 | ClassInAnotherTransitiveDependency.class, 309 | EnumInClassInAnotherTransitiveDependency.class); 310 | 311 | // set projects direct dependencies 312 | project.setDependencyArtifacts(Collections.singleton(dependency)); 313 | 314 | // set projects direct and transitive dependencies 315 | final Set dependencies = new HashSet<>(); 316 | dependencies.add(dependency); 317 | dependencies.add(transitiveDependency); 318 | dependencies.add(anotherTransitiveDependency); 319 | project.setArtifacts(dependencies); 320 | 321 | helper.setProjectArtifact(artifact); 322 | helper.setDirectDependencyArtifact(dependency); 323 | helper.setTransitiveDependencyArtifacts( 324 | new HashSet(Arrays.asList(transitiveDependency, anotherTransitiveDependency))); 325 | 326 | return helper; 327 | } 328 | 329 | private enum ArtifactFileType { 330 | JAR, 331 | TARGET_CLASSES, 332 | NOTHING 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/LogStub.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.plugin.logging.Log; 4 | 5 | 6 | final class LogStub implements Log { 7 | private final StringBuilder debugLog = new StringBuilder(); 8 | private final StringBuilder infoLog = new StringBuilder(); 9 | private final StringBuilder warnLog = new StringBuilder(); 10 | private final StringBuilder errorLog = new StringBuilder(); 11 | 12 | @Override 13 | public boolean isDebugEnabled() { 14 | return true; 15 | } 16 | 17 | @Override 18 | public void debug(CharSequence content) { 19 | debugLog.append(content).append('\n'); 20 | } 21 | 22 | @Override 23 | public void debug(CharSequence content, Throwable error) { 24 | debugLog.append(content).append(error).append('\n'); 25 | } 26 | 27 | @Override 28 | public void debug(Throwable error) { 29 | debugLog.append(error).append('\n'); 30 | } 31 | 32 | @Override 33 | public boolean isInfoEnabled() { 34 | return true; 35 | } 36 | 37 | @Override 38 | public void info(CharSequence content) { 39 | infoLog.append(content).append('\n'); 40 | } 41 | 42 | @Override 43 | public void info(CharSequence content, Throwable error) { 44 | infoLog.append(content).append(error).append('\n'); 45 | } 46 | 47 | @Override 48 | public void info(Throwable error) { 49 | infoLog.append(error).append('\n'); 50 | } 51 | 52 | @Override 53 | public boolean isWarnEnabled() { 54 | return true; 55 | } 56 | 57 | @Override 58 | public void warn(CharSequence content) { 59 | warnLog.append(content).append('\n'); 60 | } 61 | 62 | @Override 63 | public void warn(CharSequence content, Throwable error) { 64 | warnLog.append(content).append(error).append('\n'); 65 | } 66 | 67 | @Override 68 | public void warn(Throwable error) { 69 | warnLog.append(error).append('\n'); 70 | } 71 | 72 | @Override 73 | public boolean isErrorEnabled() { 74 | return true; 75 | } 76 | 77 | @Override 78 | public void error(CharSequence content) { 79 | errorLog.append(content).append('\n'); 80 | } 81 | 82 | @Override 83 | public void error(CharSequence content, Throwable error) { 84 | errorLog.append(content).append(error).append('\n'); 85 | } 86 | 87 | @Override 88 | public void error(Throwable error) { 89 | errorLog.append(error).append('\n'); 90 | } 91 | 92 | public String getDebugLog() { 93 | return debugLog.toString(); 94 | } 95 | 96 | public String getInfoLog() { 97 | return infoLog.toString(); 98 | } 99 | 100 | public String getWarnLog() { 101 | return warnLog.toString(); 102 | } 103 | 104 | public String getErrorLog() { 105 | return errorLog.toString(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/RepositoryTest.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.apache.maven.plugin.logging.Log; 4 | import org.junit.Test; 5 | 6 | import static org.hamcrest.CoreMatchers.is; 7 | import static org.junit.Assert.assertThat; 8 | 9 | 10 | public class RepositoryTest { 11 | private final Log logger = new LogStub(); 12 | 13 | @Test 14 | public void testAddType() { 15 | final Repository repository = new Repository(new ClassFilter(logger, false)); 16 | 17 | assertThat(repository.getTypes().size(), is(0)); 18 | 19 | repository.addType("byte"); 20 | assertThat(repository.getTypes().size(), is(0)); 21 | 22 | repository.addType("java.lang.Fake"); 23 | assertThat(repository.getTypes().size(), is(0)); 24 | 25 | repository.addType("de.is24.Type"); 26 | assertThat(repository.getTypes().size(), is(1)); 27 | assertThat(repository.getTypes().iterator().next(), is("de.is24.Type")); 28 | } 29 | 30 | @Test 31 | public void testSuppressionOfAnonymousTypeName() { 32 | final Repository repository = new Repository(new ClassFilter(logger, false)); 33 | 34 | assertThat(repository.getTypes().size(), is(0)); 35 | 36 | repository.addType("1"); 37 | assertThat(repository.getTypes().size(), is(0)); 38 | 39 | repository.addType("123$2"); 40 | assertThat(repository.getTypes().size(), is(0)); 41 | } 42 | 43 | @Test 44 | public void testAddDependency() { 45 | final Repository repository = new Repository(new ClassFilter(logger, false)); 46 | assertThat(repository.getDependencies().size(), is(0)); 47 | 48 | repository.addDependency("char"); 49 | assertThat(repository.getDependencies().size(), is(0)); 50 | 51 | repository.addDependency("java.lang.Fake"); 52 | assertThat(repository.getDependencies().size(), is(0)); 53 | 54 | repository.addDependency("de.is24.Type"); 55 | assertThat(repository.getDependencies().size(), is(1)); 56 | assertThat(repository.getDependencies().iterator().next(), is("de.is24.Type")); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/TypesTest.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules; 2 | 3 | import org.junit.Test; 4 | import org.objectweb.asm.Type; 5 | 6 | import static org.hamcrest.CoreMatchers.is; 7 | import static org.junit.Assert.assertThat; 8 | 9 | public class TypesTest { 10 | 11 | @Test 12 | public void testReadType() { 13 | assertThat(Types.readType(Type.BOOLEAN_TYPE), is("boolean")); 14 | assertThat(Types.readType(Type.getType(getClass())), is(getClass().getName())); 15 | 16 | final int[][] intIntArray = new int[0][0]; 17 | assertThat(Types.readType(Type.getType(intIntArray.getClass())), is("int")); 18 | 19 | final TypesTest[] typesTestArray = new TypesTest[0]; 20 | assertThat(Types.readType(Type.getType(typesTestArray.getClass())), is(getClass().getName())); 21 | } 22 | 23 | @Test 24 | public void testReadValueType() { 25 | assertThat(Types.readValueType(Type.BOOLEAN_TYPE), is("boolean")); 26 | assertThat(Types.readValueType(Boolean.TRUE), is(Boolean.class.getName())); 27 | assertThat(Types.readValueType(Type.VOID_TYPE), is("void")); 28 | 29 | final int i = 0; 30 | assertThat(Types.readValueType(i), is(Integer.class.getName())); 31 | 32 | final int[] ia = {i}; 33 | assertThat(Types.readValueType(ia), is("int")); 34 | } 35 | 36 | @Test 37 | public void testReadTypeDescription() { 38 | 39 | } 40 | 41 | @Test 42 | public void testReadInternalTypeName() { 43 | assertThat(Types.readInternalTypeName("int"), is("int")); 44 | 45 | final int[][] intIntArray = new int[0][0]; 46 | assertThat(Types.readInternalTypeName(intIntArray.getClass().getName()), is("int")); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/testtypes/ClassInAnotherTransitiveDependency.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules.testtypes; 2 | 3 | public interface ClassInAnotherTransitiveDependency { 4 | enum EnumInClassInAnotherTransitiveDependency { 5 | EINS, 6 | ZWEI, 7 | DREI 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/testtypes/ClassInDirectDependency.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules.testtypes; 2 | 3 | 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class ClassInDirectDependency { 8 | public enum EnumInClassInDirectDependency { 9 | ONE, 10 | TWO, 11 | THREE 12 | } 13 | 14 | public EnumInClassInDirectDependency getOne() { 15 | final List list = Arrays.asList("1", "2", "3"); 16 | list.forEach(element -> Integer.parseInt(element)); 17 | long countOnes = list.stream().filter(element -> "1".equals(element)).count(); 18 | 19 | return EnumInClassInDirectDependency.ONE; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/testtypes/ClassInMavenProjectSource.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules.testtypes; 2 | 3 | import com.sun.management.DiagnosticCommandMBean; 4 | import jdk.Exported; 5 | import org.w3c.dom.Text; 6 | import javax.sql.DataSource; 7 | import java.io.Serializable; 8 | import java.sql.SQLException; 9 | import java.util.Date; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | 14 | @Exported 15 | @SuppressWarnings("all") 16 | public class ClassInMavenProjectSource implements Serializable { 17 | private final double[][] doubleDoubleArray = new double[1][1]; 18 | 19 | private final byte b = 0; 20 | private final short s = 1; 21 | private final char c = 2; 22 | private final float f = 3; 23 | private final double d = 4; 24 | private final int i = 5; 25 | private final Object[][] l = { 26 | { 6 }, 27 | { 6 } 28 | }; 29 | 30 | 31 | ClassInDirectDependency referenceClassInDirectAndTransitiveDependency( 32 | ClassInTransitiveDependency referenceToClassInTransitiveDependency, double d, Float f, X string) { 33 | final ClassInTransitiveDependency localReference = referenceToClassInTransitiveDependency; 34 | 35 | try { 36 | final Long longValue = someTee(Long.class); 37 | } catch (SQLException e) { 38 | // 39 | } 40 | 41 | // types that should come from runtime's rt.jar 42 | final DataSource dataSource = null; 43 | final DataSource secondDataSource = null; 44 | final DataSource thirdDataSource = null; 45 | final Text text = null; 46 | final DiagnosticCommandMBean dcmb = null; 47 | 48 | final int k = 3 + 4; 49 | 50 | final Date date = new Date(System.currentTimeMillis()); 51 | 52 | float m = Math.max(1, 2); 53 | 54 | final Class stringArrayClass = String[].class; 55 | final Class longArrayArrayClass = Long[][].class; 56 | 57 | final ClassInDirectDependency directDependency = new ClassInDirectDependency(); 58 | final String s = String.valueOf(directDependency.getOne()); 59 | return directDependency; 60 | } 61 | 62 | public static T someTee(@SuppressWarnings("egal") Class someTeeClass) throws SQLException { 63 | try { 64 | return someTeeClass.newInstance(); 65 | } catch (InstantiationException | IllegalAccessException e) { 66 | throw new IllegalStateException(e); 67 | } 68 | } 69 | 70 | @ClassInTransitiveDependency.SomeUsefulAnnotation(stringArrayParameter = { "eins", "zwei", "drei" }, 71 | intArrayParameter = {1,2,3}, floatParameter = 1.23f) 72 | public ClassInTransitiveDependency referenceToTransitiveClass; 73 | public ClassInAnotherTransitiveDependency classInAnotherTransitiveDependency; 74 | 75 | public final int integerValue = 0; 76 | public final long[] integerValueArray = {}; 77 | 78 | public final Double doubleValue = 0d; 79 | public final Double[] doubleValueArray = {}; 80 | 81 | public final Set set = new HashSet<>(); 82 | 83 | private static final String[][] XXX = { 84 | { "A", "B" } 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/de/is24/maven/enforcer/rules/testtypes/ClassInTransitiveDependency.java: -------------------------------------------------------------------------------- 1 | package de.is24.maven.enforcer.rules.testtypes; 2 | 3 | import org.junit.Ignore; 4 | import java.io.Serializable; 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | 11 | @Deprecated 12 | public interface ClassInTransitiveDependency extends Serializable { 13 | @Ignore("DummyValue") 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD }) 16 | @interface SomeUsefulAnnotation { 17 | String defaultParameter() default "default"; 18 | 19 | String[] stringArrayParameter(); 20 | 21 | int[] intArrayParameter(); 22 | 23 | float floatParameter(); 24 | } 25 | } 26 | --------------------------------------------------------------------------------