Comments (8)
You're right, the predefined API doesn't support this case at the moment. However, you can add it yourself:
@Test
void serviceAnnotation() {
JavaClasses classes = new ClassFileImporter().importPackagesOf(DummyBean.class);
classes()
.that().areAssignableTo(DummyBean.class)
.should(beMetaAnnotatedWith(Service.class))
.check(classes);
}
private ArchCondition<JavaClass> beMetaAnnotatedWith(
final Class<? extends Annotation> annotationType) {
final String annotationName = annotationType.getSimpleName();
return new ArchCondition<JavaClass>("be meta-annotated with @" + annotationName) {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
if (!isMetaAnnotated(javaClass)) {
events.add(SimpleConditionEvent.violated(javaClass,
String.format("Class %s is not meta-annotated with @%s",
javaClass.getName(), annotationName)));
}
}
private boolean isMetaAnnotated(JavaClass javaClass) {
if (javaClass.isAnnotatedWith(annotationType)) {
return true;
}
for (JavaClass metaAnnotationType : getMetaAnnotationTypes(javaClass.getAnnotations())) {
if (metaAnnotationType.isAnnotatedWith(annotationType)) {
return true;
}
}
return false;
}
private Set<JavaClass> getMetaAnnotationTypes(Set<JavaAnnotation> annotations) {
Set<JavaClass> types = new HashSet<>();
for (JavaAnnotation annotation : annotations) {
types.add(annotation.getType());
types.addAll(getMetaAnnotationTypes(annotation.getType().getAnnotations()));
}
return types;
}
};
}
It's a little verbose, but it should do, what you want, if I've correctly understood your issue. Does this help you?
from archunit.
Actually I was just pondering, this can be written a little shorter, by using the syntax should().beAnnotatedWith(predicate)
(this time in Java 8):
@Test
void serviceAnnotation() {
JavaClasses classes = new ClassFileImporter().importPackagesOf(DummyBean.class);
classes()
.that().areAssignableTo(DummyBean.class)
.should().beAnnotatedWith(metaAnnotation(Service.class))
.check(classes);
}
private DescribedPredicate<JavaAnnotation> metaAnnotation(
final Class<? extends Annotation> annotationType) {
return new DescribedPredicate<JavaAnnotation>("meta-annotation @" + annotationType.getSimpleName()) {
@Override
public boolean apply(JavaAnnotation annotation) {
return annotation.getType().isEquivalentTo(annotationType) ||
getMetaAnnotations(annotation).stream()
.anyMatch(type -> type.isAnnotatedWith(annotationType));
}
private Set<JavaClass> getMetaAnnotations(JavaAnnotation annotation) {
Set<JavaClass> types = new HashSet<>(singleton(annotation.getType()));
annotation.getType().getAnnotations().stream()
.map(this::getMetaAnnotations)
.forEach(types::addAll);
return types;
}
};
}
Should do the same thing, as the code above, i.e. accept all types that are directly annotated with @Service
, or meta-annotated with @Service
...
from archunit.
Yep, that's just right for my requirement.
Should this be part of ArchUnit, either as API or as an "how to extend"-example in the user guide?
from archunit.
Glad to hear that it covers your case 😃
Yes, I think this would be a good addition to the predefined API, so I'll keep this issue open and propose to extend the syntax consistently to annotatedWith
:
classes().
that().areMetaAnnotatedWith(Class<? extends Annotation> clazz)
that().areMetaAnnotatedWith(String className)
that().areMetaAnnotatedWith(DescribedPredicate<JavaAnnotation> predicate)
should().beMetaAnnotatedWith(Class<? extends Annotation> clazz)
should().beMetaAnnotatedWith(String className)
should().beMetaAnnotatedWith(DescribedPredicate<JavaAnnotation> predicate)
and the respective negations. I'll see if someone wants to contribute a PR, otherwise I'll do it myself, as soon as I get around to it (which might be a little while though, since there are so many older issues in the pipeline).
from archunit.
Since I'm able to build the project now (thanks @codecholeric), I will start to work on this issue.
from archunit.
Cool, thanks a lot 😃 Let me know, if you need any support!
from archunit.
While implementing tests for this one, I found an issue relating to class resolving. If an annotation type is not imported directly, the information about meta-annotations are not present.
Example:
@Deprecated
public class ExampleTest {
@Test
public void demo() {
ArchRule rule = classes()
.that().haveSimpleName("ExampleTest")
.should().beAnnotatedWith(metaAnnotation(Retention.class));
ClassFileImporter importer = new ClassFileImporter();
// this check succeeds
JavaClasses allClasses = importer.importClasses(ExampleTest.class, Deprecated.class);
rule.check(allClasses);
// this check fails, but should succeed
JavaClasses myClasses = importer.importClasses(ExampleTest.class);
rule.check(myClasses);
}
private DescribedPredicate<? super JavaAnnotation> metaAnnotation(final Class<? extends Annotation> type) {
return new DescribedPredicate<JavaAnnotation>("meta-annotation @" + type.getSimpleName()) {
@Override
public boolean apply(JavaAnnotation input) {
return input.getType().isAnnotatedWith(type);
}
};
}
}
@codecholeric any ideas how to proceed?
from archunit.
I don't think this is an issue, but a known limitation. Imagine this structure:
foo
|-- bar/MyAnnotation.class
|-- baz/AnnotatedClass.class
If you import via new ClassFileImporter().importPath("/foo/baz")
, then ArchUnit will find that AnnotatedClass
is annotated with MyAnnotation
from the byte code. However, all further information about MyAnnotation
(like meta-annotation) is not available. And there is simply no way, to know anything about MyAnnotation
, if it was not imported as well.
On the other hand, this is something the user of ArchUnit has to take care of. All classes that are to be considered within tests have to be imported. So in my opinion, this is no obstacle for you, simply import all necessary classes within tests, including meta-annotated types to test.
One thing you could argue about though, is if the ClassResolverFromClasspath
should be able to correctly import meta-annotations, since it does not do this at the moment (I would expect, that if resolveMissingDependenciesFromClasspath=true
, the above test should work, since ArchUnit should resolve Deprecated
including all meta-annotations from the classpath, however, meta-annotations are still missing).
I'll think about this, but I think this is a separate issue, independently of syntax extensions, since those work fine if all necessary classes are imported.
from archunit.
Related Issues (20)
- Unable to find the JavaAccess call HOT 8
- Feature suggestion: Freeze files and stored.rules validation / sanity check
- How To : validate return type is not `void` HOT 2
- How To : Is there way to filter classes when using `importJar()` HOT 1
- DEPRECATED_API_SHOULD_NOT_BE_USED does not consider constants nor final fields HOT 2
- ASM is not up-to-date
- Jakarta Inject package name incorrect in BE_ANNOTATED_WITH_AN_INJECTION_ANNOTATION rule HOT 1
- How can I find out which gradle module a class belongs to HOT 7
- Problem with maven multi module project HOT 2
- False positive results when I use ArchRuleDefinition.noClasses() HOT 7
- Check access classes only works for some cases HOT 1
- ArchUnit do not correctly check Annotation on enum Constants HOT 2
- ArchUnit thinks "Switch with arrows" produces non-final fields HOT 2
- Feature suggestion: Expose synchronized blocks in core API
- Performance: archunit-junit5 may scan complete JDK for `@ArchTest` HOT 4
- SimpleName-Check failing because of trailing file extension (Kt) HOT 8
- ArchUnits - get name of lambda method HOT 1
- How to fail tests if no classes are imported? HOT 1
- Configuring a Custom gradle Plugin to Run on Client Project HOT 3
- Tests fail when using non-default "target" folder HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from archunit.