Naming rules for Maven modules and packages
The post demonstrates that jQAssistant not only gathers structural information about Java packages or classes but also includes „all the other things around“. Let’s stay with Maven and give another example on how a rule can be created ensuring better „accessibility“ to the code base for developers: The relation between the groupId and artifactId provided by a Maven module and the package or type names it contains.
Usually all Maven modules of the same project share the same groupId and each of them declares its own artifactId. Let’s have look at the example structure from the mentioned post…
parent |-module1 | |-submodule1 | |-submodule2 | |-module2 |-submodule2 |-submodule2
…and translate this to module names according to the pattern „groupId:artifactId:packaging“ where groupId is „com.buschmais.acme“:
com.buschmais.acme:parent:pom com.buschmais.acme:module1:pom com.buschmais.acme:module1.submodule1:jar com.buschmais.acme:module1.submodule2:jar com.buschmais.acme:module2:pom com.buschmais.acme:module2.submodule1:jar com.buschmais.acme:module2.submodule2:jar
In large projects the number of Maven modules may increase very fast – it’s quite easy to reach 100 or more. This brings a problem to IDEs and developers: it’s quite hard to keep all modules open at the same time – a minor change in a class can lead to incremental builds taking minutes blocking all other actions.
But how can a developer quickly identify the right module containing a class mentioned in a stacktrace from a production system? This is quite easy if the following rule is established:
Each Java type must be located in a package that has a name starting with a prefix consisting of the groupId and artifactId of the Maven module it is located in.
Sounds complicated? Let’s illustrate this with an example:
If a class is located in the module „com.buschmais.acme:module1.submodule1:jar“
then its package name must start with „com.buschmais.acme.module1.submodule1“, e.g. „com.buschmais.acme.module1.submodule1.api.MyService“.
This rule can be translated into the following jQAssistant constraint:
<constraint id="naming:PackageNameContainsModuleName"> <description>Verifies that all package containing classes contain the group (groupId) and name (artifactId) of the Maven module.</description> <cypher><![CDATA[ MATCH // create a regular expression for each artifact (module:Maven)-[:CREATES]->(artifact:Artifact) WITH artifact, artifact.group + "\\." + artifact.name + "\\..*" as prefixPattern MATCH // match type names against the regular expression (artifact)-[:CONTAINS]->(type:Type) WHERE NOT type.fqn =~ prefixPattern RETURN type ]]></cypher> </constraint>
According to the jQAssistant data model a Maven module creates artifacts (e.g. jar and test-jar). For each artifact the first part of the query creates a regular expression which is used in the second part for matching the fully qualified names of all types it contains – if a type name does not match it will be returned by the query and thus reported as a violation of the constraint.