Macker Guide

Access Rules

Allow / Deny

Class A references class B if A extends, implements, contains, aggregates, takes, returns, or otherwise depends on B. Macker can allow and deny such references between classes by pattern, using the <access-rule> tag.

The allow/deny construct is very similar to the include/exclude construct, except that it applies to pairs of classes. An <access-rule> contains <allow> and <deny> rules, each of which may specify a <from> and a <to> pattern, and an optional <message> to display when the rule is broken:

<access-rule>
    <message>The UI layer may not directly access the DB layer</message>
    <deny>
        <from pattern="ui-layer" />
        <to pattern="db-layer" />
    </deny>
</access-rule>

If you omit a <from> or a <to>, Macker presumes the rule applies from or to anything. Here, for example, is a rule which forbids any primary class from using the outdated Vector:

<access-rule>
    <deny>
        <to class="java.lang.Vector" />
    </deny>
</access-rule>

(If you want to see Macker really mad, try putting <deny/> in an access rule!)

The <from> and <to> tags are secretly anonymous <pattern> tags -- you can nest include/exclude rules inside them.

<access-rule>
    <deny>
        <to>
            <include class="java.util.Vector" />
            <include class="java.util.Hashtable" />
            <include class="java.util.Enumeration" />
        </to>
    </deny>
</access-rule>

Nesting allow/deny tags works just as nesting include/exclude does: the inner rules apply only if the outer one did. One common idiom is to specicfy that only a specific set of classes are allowed to use a certain API or layer by first ruling out everything, then adding back in the privileged classes. Here, for example, is a rule that says only the DB layer is allowed to use Java's database APIs:

<access-rule>
    <message>Only the DB layer may access database APIs</message>
    <deny>
        <to>
            <include class="java.sql.**" />
            <include class="javax.sql.**" />
            <include class="javax.jdo.**" />
        </to>
        <allow>
            <from pattern="db-layer" />
        </allow>
    </deny>
</access-rule>

Macker defines two special patterns, named from and to, which match the two ends of a reference in an access rule. You can use these in combination with filters to make self-referential rules such as "a class may not reference its own subclasses".

Rule Severity

Not all access rules need be hard and fast -- some rules can generate a warning, but still leave Macker considering rules checking to have passed. Here, for example, is a warning from Effective Java expressed in Mackerese:

<access-rule severity="warning">
    <deny>
        <to> <include class="java.lang.ThreadGroup" /> </to>
    </deny>
</access-rule>

You can override the severity of any rule, or of an entire ruleset:

<ruleset name="Recommendations" severity="warning">
    ...rules here will all be warnings, unless overridden...
</ruleset>

The severity attribute takes the following values:
SeverityPrint
message?
Fail rule
checking?
Default for...
erroryesyes<access-rule>
warningyesno
infoyesno<message>
debugnono
You can override the printing and failing behavior of the different severities -- to fail on warnings, for example, or print debug messages. For more information, read about the Ant task.

Input Classes and Subsets

Access rules do not apply to all relationships between every class in your classpath. Rather, a <from> tag will only match from a set of primary classes, which you specify when you invoke Macker.

You can make a particular ruleset apply only to a particular subset of the input classes by placing a <subset> pattern immediately inside a <ruleset>.

Here, for example, is a ruleset which ignores test classes:

<ruleset>
    <pattern name="not-test">
        <exclude class="**Test**" />
    </pattern>
    
    <ruleset name="App rules (excludes tests)">
        <subset pattern="not-test" />
        <!-- rules go here -->
    </ruleset>
</ruleset>

Note that subsets do not affect the set of all possible classes for pattern matching -- only the set of primary classes. This limits only what <from> and <foreach> (explained later) will match.

Limitations

Ideally, Macker's notion of "reference" would cover all the kinds of relationships that Java itself would check if B were private and inaccessible to A. This is almost the case; Macker can detect that class A references class B if:

(A future release of Macker will allow access rules to distinguish these different types of references.)

There are, however, some cases which Macker simply cannot check, because the information just isn't there in the bytecode. Unused imports won't show up. The type of a local variable will not show up if it is unused or only assigned to, and nobody invokes any methods on it. Usage of primitive types is generally difficult to detect (some operations on arrays of primitives do show up). For obvious reasons, dynamic loading of classes (Class.forName and friends) is beyond Macker's reach. It is also possible that the compiler will simply optimize away certain kinds of trivial usage of a class.

As long as Macker operates on compiled classes, there's nothing we can do about any of this. Now it's questionable whether you'd want some of these cases to show up -- in most cases, working on class files gives a much more accurate picture of runtime dependencies than source -- but it's a moot question. Macker doesn't look at your source.

SourceForge Logo        innig