Red Hat
Aug 5, 2013
by Adam Warski

Veripacks already allows to specify and verify which classes should be visible outside of a package (in a package-transitive way) as well as require importing and import packages within a project. This makes it possible to eliminate some build modules, while still keeping their strict isolation (which can be checked by running Veripacks).

However, what if we would like to verify that a 3rd party library is used only in a specific portion of the code? For example, let’s say we want the Hibernate classes to be used only by the dao module, and we want this to be verified at build time. The usual approach could be:

  • create a dao-api build module
  • create a dao build module with a dao-api and Hibernate dependencies
  • implement the DAOs
  • add the dao-api module as a dependency to all other modules which use the DAOs
  • add the dao module as a dependency to the module which builds the distribution (e.g. war)

If we are using Maven, that’s quite a lot of xml to write and directories to create. Could it be simpler?

Veripacks 0.4 aims at making this exact case simpler, using package-level annotations. When creating an instance of Veripacks, it is now possible to specify which packages should require importing. To use classes from such packages, you just need to add an @Import annotation to the package – and Veripacks will check that everything is used only where allowed:

1
2
3
4
5
6
// test case which runs Veripacks
VeripacksBuilder
   .requireImportOf("org.hibernate")
   .build
   .verify("com.foo.project")
   .throwIfNotOk()
1
2
3
4
5
6
// package in which we want to use Hibernate classes
// package-info.java
@Import("org.hibernate")
package com.foo.project.db.dao;
 
import org.veripacks.Import;

Veripacks itself uses these annotations to constraint the usage of the ASM bytecode-reading library; see the VeripacksSelfTest class and the annotation on the org.veripacks.reader package.

The improved process of constraining usage of a 3rd party library code to a specific part of our code now is:

  • create a dao.api and dao.impl packages (these are just example names; Veripacks doesn’t require using any specific naming convention)
  • add Hibernate as a dependency to the project
  • implement the DAOs
  • @Import the dao.api package in packages which use the DAOs

That’s a lot less XML to write (none), a bit more package-info.java to write, and only three directories to create.

Note however, that for project-packages, you should still use the @RequiresImport annotation. The VeripacksBuilder.requireImportOf and .doNotRequireImportOf methods are only intended to be used with 3rd party packages.

The new release also contains two other features. Firstly, it is now possible to skip verification in a class using the @NotVerified annotation. This may be useful for bootstrap-like classes, where wiring of the class instances is done, and implementation-classes form various packages are used.

Secondly, when building Veripacks, you can provide an implementation of the CustomAccessDefinitionsReader trait, and specify metadata in a programmatic way. E.g. if the project has a com.[company].[project].[module].[submodule] package naming convention, and we would like all module-packages to always require import, instead of adding an @RequiresImport annotation to each such package, we can do:

1
2
3
4
5
6
7
VeripacksBuilder
  .withCustomAccessDefinitionReader(new CustomAccessDefinitionsReader {
    override def isRequiresImport(pkg: Pkg) = pkg.name.split(".").length == 4
  })
  .build
  .verify(List("com.[company].[project]"))
  .throwIfNotOk()

As always Veripacks is available in the Maven central repository, and the sources on GitHub under the Apache2 license. Have fun!

Adam