diff --git a/docs/build.gradle b/docs/build.gradle index fa92cb706..d9cfc97dc 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -24,7 +24,7 @@ asciidoctor { asciidoctorj { modules { diagram.use() - diagram.version '2.2.4' + diagram.version '2.2.14' // https://github.com/asciidoctor/asciidoctorj-diagram/releases/ } } } @@ -34,7 +34,7 @@ asciidoctor.doFirst { } asciidoctorj { - version = '2.3.0' + version = '2.5.11' // https://github.com/asciidoctor/asciidoctorj/releases } asciidoctor.dependsOn cleanUserGuide diff --git a/docs/userguide/004_What_to_Check.adoc b/docs/userguide/004_What_to_Check.adoc index 473b02fab..f3078f4d9 100644 --- a/docs/userguide/004_What_to_Check.adoc +++ b/docs/userguide/004_What_to_Check.adoc @@ -4,7 +4,7 @@ The following section illustrates some typical checks you could do with ArchUnit === Package Dependency Checks -[plantuml, "package-deps-no-access"] +[plantuml, "package-deps-no-access", svg, opts=interactive] ---- skinparam componentStyle uml2 skinparam component { @@ -23,7 +23,7 @@ noClasses().that().resideInAPackage("..source..") .should().dependOnClassesThat().resideInAPackage("..foo..") ---- -[plantuml, "package-deps-only-access"] +[plantuml, "package-deps-only-access", svg, opts=interactive] ---- skinparam componentStyle uml2 skinparam component { @@ -47,8 +47,10 @@ classes().that().resideInAPackage("..foo..") === Class Dependency Checks -[plantuml, "class-naming-deps"] +[plantuml, "class-naming-deps", svg, opts=interactive] ---- +@startuml +hide empty members skinparam componentStyle uml2 skinparam component { @@ -69,6 +71,7 @@ note top on link #crimson: forbidden Bar --> FooBar #green note left on link #green: allowed +@enduml ---- [source,java] @@ -79,8 +82,11 @@ classes().that().haveNameMatching(".*Bar") === Class and Package Containment Checks -[plantuml, "class-package-contain"] +[plantuml, "class-package-contain", svg, opts=interactive] ---- +@startuml +hide empty members +set separator none skinparam componentStyle uml2 skinparam component { @@ -103,6 +109,7 @@ package com.wrong { note "resides in wrong package" as WrongPackage #crimson FooController .. WrongPackage +@enduml ---- [source,java] @@ -113,8 +120,10 @@ classes().that().haveSimpleNameStartingWith("Foo") === Inheritance Checks -[plantuml, "inheritance-naming-check"] +[plantuml, "inheritance-naming-check", svg, opts=interactive] ---- +@startuml +hide empty members skinparam componentStyle uml2 skinparam component { @@ -137,6 +146,7 @@ FtpConnection --|> Connection #green SshThing --|> Connection #crimson note right on link #crimson: Has wrong name +@enduml ---- [source,java] @@ -145,8 +155,11 @@ classes().that().implement(Connection.class) .should().haveSimpleNameEndingWith("Connection") ---- -[plantuml, "inheritance-access-check"] +[plantuml, "inheritance-access-check", svg, opts=interactive] ---- +@startuml +hide empty members +set separator none skinparam componentStyle uml2 skinparam component { @@ -173,6 +186,7 @@ ValidPersistenceUser --> EntityManager #green IllegalPersistenceUser --> EntityManager #crimson note right on link #crimson: Accessor resides in wrong package +@enduml ---- [source,java] @@ -183,8 +197,10 @@ classes().that().areAssignableTo(EntityManager.class) === Annotation Checks -[plantuml, "inheritance-annotation-check"] +[plantuml, "inheritance-annotation-check", svg, opts=interactive] ---- +@startuml +hide empty members skinparam componentStyle uml2 skinparam component { @@ -206,6 +222,7 @@ ValidPersistenceUser --> EntityManager #green IllegalPersistenceUser --> EntityManager #crimson note right on link #crimson: Accessor is not annotated with @Transactional +@enduml ---- [source,java] @@ -216,8 +233,11 @@ classes().that().areAssignableTo(EntityManager.class) === Layer Checks -[plantuml, "layer-check"] +[plantuml, "layer-check", svg, opts=interactive] ---- +@startuml +hide empty members +set separator none skinparam componentStyle uml2 skinparam component { @@ -253,6 +273,7 @@ note right on link #crimson: Access goes against layers SomePersistenceManager -up--> SomeServiceOne #crimson note right on link #crimson: Access goes against layers +@enduml ---- [source,java] @@ -270,8 +291,11 @@ layeredArchitecture() === Cycle Checks -[plantuml, "cycle-check"] +[plantuml, "cycle-check", svg, opts=interactive] ---- +@startuml +hide empty members +set separator none skinparam componentStyle uml2 skinparam component { @@ -301,6 +325,7 @@ ClassOneInModuleOne --> ClassTwoInModuleTwo #crimson ClassOneInModuleTwo --> ClassOneInModuleThree #crimson ClassTwoInModuleThree --> ClassOneInModuleOne #crimson note right on link #crimson: Combination of accesses forms cycle +@enduml ---- [source,java] diff --git a/docs/userguide/006_The_Core_API.adoc b/docs/userguide/006_The_Core_API.adoc index c5fdac403..1cb287c74 100644 --- a/docs/userguide/006_The_Core_API.adoc +++ b/docs/userguide/006_The_Core_API.adoc @@ -87,47 +87,103 @@ The domain objects represent Java code, thus the naming should be pretty straigh commonly, the `ClassFileImporter` imports instances of type `JavaClass`. A rough overview looks like this: -[plantuml, "domain-overview"] +[plantuml, "domain-overview", svg, opts=interactive] ---- -skinparam componentStyle uml2 - -skinparam component { - BorderColor #grey - BackgroundColor #white -} - -skinparam class { - BorderColor #grey - BackgroundColor #white -} - -class JavaPackage -class JavaClass -class JavaMember -class JavaField -class JavaCodeUnit -class ThrowsClause -class JavaConstructor -class JavaMethod -class JavaStaticInitializer - -class JavaFieldAccess -class JavaConstructorCall -class JavaMethodCall - -JavaPackage *--* "1..*" JavaClass : has -JavaClass *-- "0..*" JavaMember : has -JavaMember <|-- JavaField : extends -JavaMember <|-- JavaCodeUnit : extends -JavaCodeUnit <|-- JavaConstructor : extends -JavaCodeUnit <|-- JavaMethod : extends -JavaCodeUnit <|-- JavaStaticInitializer : extends -JavaConstructor *-- "1" ThrowsClause : has -JavaMethod *-- "1" ThrowsClause : has - -JavaCodeUnit *-- "0..*" JavaFieldAccess : has -JavaCodeUnit *-- "0..*" JavaMethodCall : has -JavaCodeUnit *-- "0..*" JavaConstructorCall : has +@startuml +skinparam hyperlinkUnderline false +hide empty members + +!function $getDomainLink($name, $prefix="") !return "[[https://javadoc.io/doc/com.tngtech.archunit/archunit/latest/com/tngtech/archunit/core/domain/" + $prefix + $name + ".html " + $name + "]]" +!function $getAccessTargetLink($name) !return $getDomainLink($name, "AccessTarget.") + +!unquoted procedure domainLink($name, $parameter="") + "$getDomainLink($name)$parameter " as $name +!endprocedure + +!unquoted procedure accessTargetLink($name) + "$getAccessTargetLink($name) " as $name +!endprocedure + +interface domainLink(JavaType) +interface domainLink(JavaParameterizedType) +class domainLink(JavaGenericArrayType) +class domainLink(JavaWildcardType) +class domainLink(JavaTypeVariable) + +class domainLink(JavaClasses)<>> + +class domainLink(JavaPackage) +class domainLink(JavaClass) +abstract class domainLink(JavaMember) + +class domainLink(JavaField) +abstract class domainLink(JavaCodeUnit) +class domainLink(JavaParameter) +class domainLink(JavaMethod) +class domainLink(JavaConstructor) +class domainLink(JavaStaticInitializer) + +class domainLink(JavaAnnotation, ) + +class domainLink(ThrowsClause, )<>> +class domainLink(ThrowsDeclaration) +class domainLink(ReferencedClassObject) +class domainLink(InstanceofCheck) +class domainLink(TryCatchBlock) +abstract class domainLink(JavaAccess, \n) +class domainLink(JavaFieldAccess) +abstract class domainLink(JavaCodeUnitAccess, \n) +abstract class domainLink(JavaCodeUnitReference, \n) +class domainLink(JavaMethodReference) +class domainLink(JavaConstructorReference) +abstract class domainLink(JavaCall, \n) +class domainLink(JavaMethodCall) +class domainLink(JavaConstructorCall) + +JavaClass ---u-|> JavaType +JavaParameterizedType --|> JavaType +JavaTypeVariable --|> JavaType +JavaGenericArrayType --|> JavaType +JavaWildcardType --|> JavaType + +JavaClasses -. JavaClass : contains > + +JavaPackage ||-{ "1..*" JavaClass : has > + +JavaParameter ||--{ "0..*" JavaAnnotation : has > +JavaPackage ||--{ "0..*" JavaAnnotation : has > +JavaClass ||--{ "0..*" JavaAnnotation : has > +JavaMember ||--{ "0..*" JavaAnnotation : has > + +JavaClass ||-{ "0..*" JavaMember : has > + +JavaMember <|-- JavaField +JavaMember <|--r- JavaCodeUnit + +JavaCodeUnit ||--{ "0..*" JavaParameter : has > +JavaCodeUnit <|-- JavaMethod +JavaCodeUnit <|--- JavaConstructor +JavaCodeUnit <|-- JavaStaticInitializer + +JavaCodeUnit ||--u{ "0..*" InstanceofCheck : has > +JavaCodeUnit ||--u{ "0..*" ReferencedClassObject : has > +JavaCodeUnit ||--u|| "1" ThrowsClause : has > +JavaCodeUnit ||--u{ "0..*" TryCatchBlock : has > +JavaCodeUnit ||--r-{ "0..*" JavaAccess : has > + +ThrowsClause ||-u-{ "0..*" ThrowsDeclaration : has > + +JavaAccess <|-- JavaFieldAccess : T =\n$getAccessTargetLink(FieldAccessTarget) +JavaAccess <|-- JavaCodeUnitAccess + +JavaCodeUnitAccess <|-- JavaCall +JavaCall <|-- JavaMethodCall : T =\n$getAccessTargetLink(MethodCallTarget) +JavaCall <|--- JavaConstructorCall : T=\n$getAccessTargetLink(ConstructorCallTarget) + +JavaCodeUnitAccess <|-- JavaCodeUnitReference +JavaCodeUnitReference <|-- JavaMethodReference : T=\n$getAccessTargetLink(MethodReferenceTarget) +JavaCodeUnitReference <|--- JavaConstructorReference : T=\n$getAccessTargetLink(ConstructorReferenceTarget) +@enduml ---- Most objects resemble the Java Reflection API, including inheritance relations. Thus a `JavaClass` @@ -151,8 +207,10 @@ For example, every `JavaField` allows to call `JavaField#getAccessesToSelf()` to code units within the graph that access this specific field. The resolution process through inheritance is not completely straight forward. Consider for example -[plantuml, "resolution-example"] +[plantuml, "resolution-example", svg, opts=interactive] ---- +@startuml +hide empty members skinparam componentStyle uml2 skinparam component { @@ -176,7 +234,7 @@ class SuperclassBeingAccessed { SuperclassBeingAccessed <|-- ClassBeingAccessed ClassAccessing o-- ClassBeingAccessed - +@enduml ---- The bytecode will record a field access from `ClassAccessing.accessField()` to @@ -188,8 +246,10 @@ member within another class. If a member is queried for `accessesToSelf()` thoug will resolve the necessary targets and determine, which member is represented by which target. The situation looks roughly like -[plantuml, "resolution-overview"] +[plantuml, "resolution-overview", svg, opts=interactive] ---- +@startuml +hide empty members skinparam componentStyle uml2 skinparam component { @@ -220,6 +280,7 @@ MethodCallTarget "1" -- "0..*" JavaMethod : resolves to JavaConstructorCall "1" *-- "1" ConstructorCallTarget : has ConstructorCallTarget "1" -- "0..1" JavaConstructor : resolves to +@enduml ---- Two things might seem strange at the first look. @@ -234,8 +295,10 @@ Second, why can there be more than one resolved methods for method calls? The reason for this is that a call target might indeed match several methods in those cases, for example: -[plantuml, "diamond-example"] +[plantuml, "diamond-example", svg, opts=interactive] ---- +@startuml +hide empty members skinparam componentStyle uml2 skinparam component { @@ -248,13 +311,13 @@ skinparam class { BackgroundColor #white } -class A <> { +interface A <> { void targetMethod() } -class B <> { +interface B <> { void targetMethod() } -class C <> { +abstract class C <> { } class D { void callTargetMethod() @@ -263,6 +326,7 @@ class D { A <|-- C : implements B <|-- C : implements D -right- C : calls targetMethod() +@enduml ---- While this situation will always be resolved in a specified way for a real program, diff --git a/docs/userguide/007_The_Lang_API.adoc b/docs/userguide/007_The_Lang_API.adoc index e472d3c80..acd98b6ae 100644 --- a/docs/userguide/007_The_Lang_API.adoc +++ b/docs/userguide/007_The_Lang_API.adoc @@ -246,7 +246,7 @@ detailed about fields, methods or constructors. A generic API will never be able every imaginable concept out of the box. Thus ArchUnit's rule API has at its foundation a more generic API that controls the types of objects that our concept targets. -[plantuml, "import-vs-lang"] +[plantuml, "import-vs-lang", svg, opts=interactive] ---- skinparam componentStyle uml2 diff --git a/docs/userguide/008_The_Library_API.adoc b/docs/userguide/008_The_Library_API.adoc index 1c706e6a6..4a3aeab37 100644 --- a/docs/userguide/008_The_Library_API.adoc +++ b/docs/userguide/008_The_Library_API.adoc @@ -55,8 +55,11 @@ More precisely, the following holds: contain dependencies on any `adapter` package. -[plantuml, "onion-architecture-check"] +[plantuml, "onion-architecture-check", svg, opts=interactive] ---- +@startuml +hide empty members +set separator none skinparam componentStyle uml2 skinparam class { @@ -112,6 +115,7 @@ note right on link #crimson: application services must not\nknow about any adapt Cli --> RestController #crimson note right on link #crimson: one adapter must not know\nabout any other adapter +@enduml ---- @@ -357,7 +361,7 @@ Diagrams supported have to be component diagrams and associate classes to compon The way this works is to use the respective package identifiers (compare `ArchConditions.onlyHaveDependenciesInAnyPackage(..)`) as stereotypes: -[plantuml, "simple-plantuml-archrule-example"] +[plantuml, "simple-plantuml-archrule-example", svg, opts=interactive] ---- [Some Source] <<..some.source..>> [Some Target] <<..some.target..>> as target @@ -600,7 +604,7 @@ The `CCD` of the system divided by the `CCD` of a balanced binary tree with the ===== Example -[plantuml,"lakos-example"] +[plantuml, "lakos-example", svg, opts=interactive] ---- skinparam componentStyle uml2 skinparam component { @@ -690,7 +694,7 @@ so it makes sense to only consider those classes to calculate the *A* value. The following provides some example where the `A` values assume some random factor of abstract classes within the respective component. -[plantuml,"martin-example"] +[plantuml, "martin-example", svg, opts=interactive] ---- skinparam componentStyle uml2 skinparam component { @@ -755,7 +759,7 @@ The metrics are composed from the following definitions: ===== Example -[plantuml,"dowalil-example"] +[plantuml, "dowalil-example", svg, opts=interactive] ---- skinparam componentStyle uml2 skinparam component {