2

How can I create a Checkstyle rule to limit interactions between different root packages?

I have the following 3 root packages:

  • models
  • views
  • controllers

(They are not something like com.mycompany.myproject.models. They are root packages.)

I wanted to disallow access from models to views and from views to models (and some others).

I try to use the ImportControl-Checker from Checkstyle:

  • Try 1: Use one single import-control.xml. Problem: I can provide only one Root-XML-Element (<import-control pkg="models">) and this contains only one Package (but I want to have more than one).
  • Try 2: Use several import-control.xml. Problem: If I import more than one in checkstyle-config.xml, neither seems to work (there is no error, it just looks like I didn't define neither). My definition in import-control.xml:

    <module name="ImportControl">
      <property name="id" value="ImportControlViews"/>
      <property name="file" value="${basedir}/project/import-control/views.xml"/>
    </module>
    <module name="ImportControl">
      <property name="id" value="ImportControlModels"/>
      <property name="file" value="${basedir}/project/import-control/models.xml"/>
    </module>
    
Itchy
  • 2,263
  • 28
  • 41

2 Answers2

1

Unfortunately, what you want is very hard to do using the ImportControl check out of the box.
Here's why:

You already found out why your option 1 cannot work: There can only be one root package.

Option 2 is possible, but laborious. Let me go into some depth. I used the following two import control files, which disallow using models from views and views from models:

<!DOCTYPE import-control PUBLIC "-//Puppy Crawl//DTD Import Control 1.1//EN"
    "http://www.puppycrawl.com/dtds/import_control_1_1.dtd">
<import-control pkg="views">
    <allow pkg="views" />
    <disallow pkg="models" />
</import-control>
<!DOCTYPE import-control PUBLIC "-//Puppy Crawl//DTD Import Control 1.1//EN"
    "http://www.puppycrawl.com/dtds/import_control_1_1.dtd">
<import-control pkg="models">
    <allow pkg="models" />
    <disallow pkg="views" />
</import-control>

In my test setup, this basically worked, but there is a drawback: Every class gets a Checkstyle warning that the Import control file does not handle this package. This is because the ImportControl check expects all packages to reside under a common root (verified by looking at the Checkstyle 5.6 sources). So in the models package, you get the warning from the check instance configured for the views package, and vice versa.
There is also the added problem that the ImportControl check only works on the import statements, but does not find fully qualified references used directly in the code.

So, what can you do?

  • Change your app so that you have a common root. This is best practice and generally a good idea.
  • Implement a custom check as a subclass of ImportControlCheck which adds an option for enabling/disabling the "Import control file does not handle this package" message, and otherwise go with your option 2.
  • If you are using Eclipse, there is also a third solution. You could use the advanced configuration dialog that the Checkstyle Eclipse plugin provides in order to restrict the ImportControl instances to their respective files. This would also eliminate the "Import control file does not handle this package" messages.
barfuin
  • 16,865
  • 10
  • 85
  • 132
1

You can achieve this through a mix of your build tool config and checkstyle config. For example, with gradle, you would place the sources in a separate source set, and use

checkStyleModels {
    excludes = ['views/**']
    configFile file('checkstyle-models.xml')
}
checkStyleViews {
    excludes = ['models/**']
    configFile file('checkstyle-views.xml')
}

And then disallow views in the checkstyle-models.xml. Both config can have then a different root without problem.

jherek
  • 135
  • 5