Contract4JBeans README
Contract4JBeans (Experimental "JavaBeans"-like form) README v0.3.0.0 February 20, 2006 v0.2.0.0 October 5, 2005 v0.2.0M1 August 15, 2005 Dean Wampler, Ph.D. https://aspectprogramming.com/contract4j Contents: ** Introduction ** What Is "Contract4JBeans"? --- How Does Contract4JBeans Support Design by Contract? --- How Do I Use Contract4JBeans? --- Show Me the Code! --- Invocation and Configuration of Contract4JBeans --- Debugging Tips ** TODO Items ** Notes for Each Release ** For Further Information... ** Introduction For the Copyright statement and a general overview of Contract4J and Design by Contract, see the <%= link_to 'Contract4J5 README', :action => 'c4j5'%>. ** What Is "Contract4JBeans"? After the initial version of Contract4J, a second, experimental version was developed that used a JavaBeans-like syntax for defining tests as regular Java methods. Hence, it is called Contract4JBeans. This version was developed as a way of avoiding some issues with the original annotation-based version (which have since been resolved). While Contract4JBeans demonstrates some interesting ideas, it is considered an inferior approach and it is not being developed further. The main flaws are: - Very poor runtime performance - The role of the tests as part of the component interface is more obscure than for the annotation approach used in Contract4J5. However, it may be of interest to AOP tool writers, so it will remain publicly available. To distinguish the two versions of Contract4J, the annotation version is called Contract4J5 and the "beans" version is called Contract4JBeans in the various READMEs and other documentation. --- How Does Contract4JBeans Support Design by Contract? Contract4JBeans and Contract4J5 take very different approaches to supporting DbC, each with strengths and weaknesses. I'm a long-time believer in DbC and wanted to use it in Java. A few years ago, I discovered the clever "Barter" project, which supports DbC in Java using XDoclet tags and AspectJ code generation to perform the tests as AspectJ "advice" (https://barter.sourceforge.net/). A problem with doclet-based approaches is that they are buried in the comments and hence decoupled from the runtime environment. Java 5 introduced annotations, which are like "javadoc tags for code". In particular, annotations are used to ascribe meta-information to the code and to support optional runtime exploitation of that information. Annotations are a logical tool for associating contract tests with code and Contract4J5 defines a set of annotations for specifying contracts in the form of executable statements. The details are discussed below. A contrived example suffices for now: @Contract public class MyClass { @Invar ("name != null") private String name; @Pre ("n != null") public void setName (String n) { name = n; } @Post ("$return != null") public String getName () { return name; } @Pre ("n > 10 && s != null") @Post ("$return != null") public String doIt (int n, String s) {...} ... } The "@Contract" annotation tells Contract4J5 that "MyClass" defines tests. The tests are: 1) The attribute "name" has an invariant test that it can never be null (after the object has been constructed...). 2) The "setName" method has a precondition that the value of the parameter cannot be null. 3) The "getName" method has a postcondition that it can never return null. 4) The "doIt" method has both a pre- and a postcondition test. Contract4J5 is described in more detail in the <%= link_to 'Contract4J5 README', :action => 'c4j5'%>. This approach, called "Contract4JBeans" or "ContractBeans" for short, is an experimental version that uses a different approach. It started with the need to remove some of the disadvantages of the first versions of Contract4J. This approach is considered experimental because it doesn't really offer a better approach and it will not be developed further. However, it has some interesting characteristics that may be useful for other tools that use AOP and AspectJ. Contract4JBeans introduces a JavaBeans-like convention for defining test as special methods that are discovered through runtime reflection and invoked at appropriate join points. Here is the previous Contract4J5 example rewritten in the Contract4JBeans style: public class MyClass { private String name; public boolean invarName() { return name != null; } public void setName (String n) { name = n; } public boolean preSetName (String n) { return n != null; } public String getName () { return name; } public boolean postGetName (String result) { return result != null; } public String doIt (int n, String s) {...} public boolean preDoIt (int n, String s) { return n > 10 && s != null; } public boolean postDoIt (String result, int n, String s) { return result != null; } ... } All test methods return true if the test passes or false if it doesn't. The invariant test for the "name" attribute is called "invarName". The precondition test for "setName" is called "preSetName". All precondition and invariant tests for methods take the same argument list as the method itself. Postcondition tests, like the ones for "getName" and "doIt" pass as the first argument the result of the method call (compare with the "$return" keyword used in Contract4J5 tests), followed by the same argument list passed to the method. Note that, unlike the annotation form, the test methods could actually appear anywhere in the class, not necessarily adjacent to the members they test, and the test methods could be called for other purposes. So, the contract tests are defined without annotations. This was not an original goal of the redesign, but a result of the naming convention. It has the benefit that Contract4JBeans can be used in pre-Java 5 environments. Further comparisons of these two Contract4J approaches can be found in the papers "The Challenges of Writing Reusable Aspects in AspectJ" that was presented as part of the Industry Track at at AOSD.06, March, 2006 and "Pattern-Based Interfaces and Contract4J" that was presented as part of the ACP4IS Workshop at AOSD.06. Contact me if you would like copies of these papers. ---- Inheritance Behavior of Contracts As discussed in the <%= link_to 'Contract4J5 README', :action => 'c4j5'%>, DbC has rules for proper behavior of inherited contracts. Contract4JBeans does not have any "native" support for the correct covariant or contravariant behavior, as appropriate, under inheritance. C4J does invoke base-class tests on derived class overrides, like method calls, but it relies on the test writer to implement tests appropriate. This is really no different than the regular steps required to write good classes and methods that obey the LSP and it reflects the fact that C4J can't really know how to satisfy all state requirements. The patterns that test writers should use are these. Only override a precondition test if you intend to loosen it. Call the parent test and if it fails, then test whether or not the "extra" allowed "states" are met (i.e., "or" the results). If so return true. Otherwise, return false. For postcondition tests, never relax the conditions, only strengthen them. Use similar coding logic; call the parent test and if it passes, then test if "state" is still within the tightened constraints (i.e., "and" the results). Here is a contrived example: public class Base { public String generateString (int n) { return Integer.toString(n); } public boolean preGenerateString (int n) { return n > 0; } public boolean postGenerateString (String result, int n) { return result != null && result.length() > 0; } ... } public class Derived extends Base { public String generateString (int n) { String result = super.generateString(n); return "Result: "+result; } public boolean preGenerateString (int n) { return super.preGenerateString(n) || n == 0; } public boolean postGenerateString (String result, int n) { return super.postGenerateString(result, n) && result.length() > 8; } ... } The "generateString" method will return a string that is "based in some way" on the input argument. The Base version simply converts to a string, while the Derived version adds a prefix. (I said this was contrived!) The Base method also has pre- and postconditions. The precondition says that the integer argument must be positive and the postcondition says that the returned string can't be null and it must not be empty. The Derived precondition relaxes the Base precondition, it allows the argument to be 0. Again, any clients using a Base will still assume a positive value is required so the precondition will still always pass. In contrast, the postcondition test is now tightened to require the returned value to be 9 or more characters long (consistent with the behavior of Derived.generateString). Since 9 > 0, clients using a Base reference to a Derived object will experience no surprises, as all returned strings will still satisfy the Base postcondition. Note that the Derived precondition test calls the parent's test and "or's" the result with a test of the extra allowed state, thereby expanding the allowed values. Of course, I could have simply written "return n >=0;", but then I would have to maintain the common subset of the test condition in two places. Similarly, the Derived postcondition test "and's" the parent's result with a test of the more constrained allowed states. WARNING: Unfortunately, there is currently a bug that prevents you from writing covariant postcondition tests. Using the example above, note that the method "postGenerateString()" is public and defined in both classes, meaning that it is virtual and overridden in the derived class. When the derived "generateString()" calls the parent method using "super", C4J will look for and find the corresponding base-class "postGenerateString()". However, when it invokes this method using Java reflection, if the object is actually of type Derived, the derived-class version will be invoked instead. This is because java.lang.reflect.Method.invoke() uses dynamic dispatch. However, if the derived-class postcondition test "tightens" the constraints, then the test may fail, even if the derived method *being tested* would make that test pass. The bug reflects a collision between normal Java method dispatch and the semantics of contract inheritance. It's a flaw of the Contract4JBeans approach. The ideal solution is to prevent the derived-class test from being used to test the parent-class method. This may not be possible, since Java reflection is used to find and invoke the tests. A fall back solution is to only allow tests to be run before and after the most derived version of the method under test. In other words, to ignore the tests when "super." methods are executed. This can be implemented by maintaining context information within Contract4JBeans code itself. Several unit tests in com.aspectprogramming.contract4jbeans.test.TestContractTestSubclass, including testContractTestClassContravariantPreCovariantPost1() and testContractTestClassContravariantPreCovariantPost1(), "pass" only because they explicitly anticipate this incorrect behavior and disallow postcondition covariance. --- How Do I Use Contract4JBeans? This section describes how to use Contract4JBeans. For information on using Contract4J5, the annotation form, see the README.txt in its distribution. This distribution contains ant files and examples of how to use Contract4JBeans. The examples are also used by the unit/acceptance test suite. ---- Installation: The following commands are Linux/Unix commands. They also work with Cygwin bash on Windows. Or, use WinZip or an equivalent utility to expand the compressed tar file. You will need Java 5, AspectJ 5, and JUnit 3.8.1. tar xvzf contract4jbeans_030.tar.gz You can use the installed "contract4jbeans.jar" file as is. If you want to rebuild it, use the ant driver script "build.sh" in this directory, edit the file and change the environment variable definitions to have the appropriate values for your environment or set up your environment so they are defined apropriately before invocation of the script. If you have the environment variables already defined, you can also invoke "ant" directly. The distribution has the following structure: build.sh - Unix/Linux build driver script build.bat - Windows build driver script build.xml - Ant build script src - The source code tree. src/com/aspectprogramming/contract4jbeans - The implementation code. test - The JUnit unit/acceptance tests, which also function as usage examples. src/com/aspectprogramming/contract4jbeans/test - The test classes. The files beginning with "Test*" are JUnit tests. The other classes are example classes used by the tests, which also provide usage examples. classes - where build artifacts (except the jars) are stored doc - Where Javadocs are written contract4jbeans.jar - The runtime deployment jar. It contains the build products from "src". contract4jbeans-test.jar - The jar containing the build products from "test". Not required as part of the normal runtime deployment. You can use the installed "contract4jbeans.jar" file as is (see below). To build Contract4J: 6a) ./build.sh all 6b) build.bat all or 6b) ant all You will probably need to edit environment variables defined in the scripts. Next, we'll look at code examples, then return to a discussion of invoking and configuring Contract4J. ---- Show Me the Code! Here is how to define Contract4JBeans tests in your code so that they can be discovered and executed at runtime. See the examples in contract4jbeans/test/com/aspectprogramming/contract4jbeans/test. The files beginning with "Test*" are JUnit tests, some of which define nested classes that illustrate usage. The other classes and aspects are supporting tools and also demonstrate how to define tests and use Contract4JBeans. Note that "Contract4JTestScope.aj" is a special example of an aspect you should define in your environment that tells Contract4JBeans the "scope" of where to look for tests. (For example, you don't want to include the JDK packages.) Let's look at some of the test examples. ----- Contract4JTestScope.aj: A marker interface is defined called "Contracts" that is used to indicate which classes define tests. It defines no members. Users can explicitly declare their classes to implement this interface (see, for example, contract4jbeans/test/com/aspectprogramming/contract4jbeans/test/TestProperties.java). However, the better way is to use AspectJ's intertype declaration (a.k.a. introduction) feature to make Contracts a "parent" of all the classes in your project's packages for which you intend to define tests. Contract4JTestScope.aj provides an example. The form of this file and similar files for your projects is as follows: import com.aspectprogramming.contract4jbeans.Contracts; public aspect Contract4JTestScope { declare parents: () implements Contracts; } Any unique name for the aspect will do. The " " is, for example, "com.aspectprogramming.contract4jbeans.test.ContractTest*" in Contract4JTestScope.aj. Any valid AspectJ type specification can be used. ----- ContractTestClass.java Let's look at the most complete example, ContractTestClass.java in the test directory. Many of the JUnit tests exercise the tests in this file. Here is a partial listing of the file, annotated with comments. ... // IContractTestClass defines some methods that this class and // others define; used primarily for implementation convenience // as well as for testing inheritance behavior. public class ContractTestClass implements IContractTestClass { // Make "name" protected so we can test what happens when // subclasses manipulate it directly. (Normally would be private.) protected String name; public void setName (String s) { name = s; } public String getName () { return name; } // The invariant test for "name", which calls a service // method "checkStr" that is used by other tests, too. // Class-wide and method-specific invariant tests (examples below) // are called before and after all non-priviate instance method calls. // Contracts are never tested for static methods, because they don't // modify an object's state. The are never tested within contract test // methods themselves, either. Field invariant tests are executed // after every read and write of a field, except they are not // called within the context ("cflowbelow") of constructors, since the // fields are only assumed to be properly initialized after the // constructor completes, at which point the invariant tests are // evaluated. public boolean invarName () { return checkStr (name); } // Contrived function for testing strings. protected static boolean checkStr (String s) { return (s != null && s.length() > 0); } /** * An instance method that has both pre- and postcondition tests. */ public String methodWithPrePost (int flag, String s) { setName (s); if (s != null && s.equals("force null")) { return null; } return String.format ("flag = %d, s = %s", flag, s); } /** * The precondition test for the method. Note that the argument list is * the same as "methodWithPrePost". */ public boolean preMethodWithPrePost (int flag, String s) { return (flag > 0 && s != null && s.length() > 0); } /** * The postcondition test for the method. Note that the argument list is * the same as "methodWithPrePost", except that the first argument is the * "result" returned by "methodWithPrePost". */ public boolean postMethodWithPrePost (String result, int flag, String s) { return (result != null && result.length() > (s.length() + 13)); } /** * Like methodWithPrePost, but with a void return value. */ public void voidMethodWithPrePost (int flag, String s) { setName (s); } public boolean preVoidMethodWithPrePost (int flag, String s) { return (flag > 0 && s != null && s.length() > 0); } /** * If the method returns void, the argument list to the postcondition * test does not have a leading argument for the return value! * Contrast with the previous case of non-void returns. */ public boolean postVoidMethodWithPrePost (int flag, String s) { return !name.equals("void"); } /** * An instance method that has an invariant test. */ public String methodWithInvar (int flag, String s) { return "methodWithInvar does nothing!"; } /** * The invariant test. The argument list matches the argument list of * the method being tested. */ public boolean invarMethodWithInvar (int flag, String s) { return flag > 0; } /** * Note that this method attempts to define a postcondition test on another * test method. In fact, it is ignored! */ public boolean postInvarMethodWithInvar (Boolean result, int flag, String s) { return !s.equals("force fail"); } /** * Tests on static methods are ignored, since static methods don't affect * object state. However, it's conceivable that contracts on these methods * would be useful, so this is a planned enhancement. * You'll see warning messages during compilation for this method. */ public static boolean staticMethodWithTests (int flag) { return true; } // never called! public static boolean preStaticMethodWithTests (int flag) {return false;} // never called! public static boolean postStaticMethodWithTests (boolean b, int flag) {return false;} // never called! public static boolean invarStaticMethodWithTests (int flag) {return false;} /** * Constructor with precondition, postcondition, and invariant tests. */ public ContractTestClass (String n) { setName (n); } /** * Note that since a constructor is called before the object actually exists, * since obviously the constructor is part of the object creation and * initialization process, it is necessary to make the test methods static! */ public static boolean preContractTestClass (String n) { return checkStr (n); } public static boolean postContractTestClass (String n) { return n != null && !n.equals("bad name"); } public static boolean invarContractTestClass (String n) { return n != null && !n.equals("bad invar name"); } /** * A class invariant test, which looks like an invariant test for a * default c'tor. You can tell the difference because this method * is not static, unlike c'tor methods. */ public boolean invarContractTestClass () { return name == null || !name.equals("bad class invar name"); } } ---- Details of Contract Specifications Here are the rules for using Contract4J, which clarify the examples just discussed. 1) Implement the "Contracts" Interface Either explicitly declare your classes to implement this interface (in the com.aspectprogramming.contract4jbeans package) or use an aspect with an intertype declaration: import com.aspectprogramming.contract4jbeans.Contracts; ... public class ... implements Contracts { ... } or import com.aspectprogramming.contract4jbeans.Contracts; public aspect { declare parents: ( ) implements Contracts; } Where is the name of a class with tests. is an arbitrary, but unique name for the aspect. is an AspectJ type specification, e.g., one or more packages and/or classes (See the AspectJ documentation for details). The advantage of the latter approach is that you can specify whole packages in the type specification, etc. For a complete example of the latter approach, see com/aspectprogramming/contract4j/test/Contract4JTestScope.aj in the distribution. 2) Define the Test Methods, Following the Method Signature Conventions The conventions for the test methods are as follows: a) Field (Attribute) Invariant Tests Only invariant tests can be defined for fields. These tests will be evaluated after every read or write of the field, except during constructor calls, where the field may not yet be initialized, and during other test methods. The evaluation is only done after access, not before, to allow "lazy initialization" and because the test only matters after access, just before the field is used by the "accessor". However, while test evaluation is not done during a constructor call, it is performed after the constructor call completes. Test Format: [!private] boolean invar () {...} The method should not be private, since the contract is part of the interface exposed to clients and/or derived class writers. It must return boolean. The name must be the field name, with the first character capitalized, prefixed with "invar". It must take no arguments. Example: For "private String name" field, public boolean invarName() {...} b) Instance Method Tests Methods may have pre-, post-, and invariant-condition tests that will be evaluated before, after, and "around" (both before and after) the method execution, respectively. Generally speaking, method tests are usually only "interesting" for non-private methods. Hence, tests defined for private methods are ignored. Any test methods that are defined to test other test methods (nesting) are ignored! (These restrictions are, in part, simplifications to reduce the design complexity and the overhead of test method discovery.) Precondition Test Format: [!private] boolean pre (arglist) {...} The method should not be private, since the contract is part of the interface exposed to clients and/or derived class writers. It must return boolean. The name must be the method name, with the first character capitalized, prefixed with "pre". It must take the same argument list as the method being tested. Postcondition Test Format: [!private] boolean post (Type result, arglist) {...} The method should not be private, since the contract is part of the interface exposed to clients and/or derived class writers. It must return boolean. The name must be the method name, with the first character capitalized, prefixed with "post". If the original method does not return void, then the test method must take an argument list where the first argument has the same return type as the method being tested (the argument name itself is arbitrary; "result" is just a convention). The rest of the argument list must be the same argument list as the method being tested. However, if the original method returns void, then the argument list for the test method must match the original method's argument list exactly. Invariant Condition Test Format: [!private] boolean invar (arglist) {...} The method should not be private, since the contract is part of the interface exposed to clients and/or derived class writers. It must return boolean. The name must be the method name, with the first character capitalized, prefixed with "invariant". It must take the same argument list as the method being tested. Example: For "public String foo (String name, int flag)" method, public boolean preFoo (String name, int flag) {...} public boolean postFoo (String result, String name, int flag) {...} public boolean invarFoo (String name, int flag) {...} For "public void bar (String name, int flag)" method, public boolean preBar (String name, int flag) {...} public boolean postBar (String name, int flag) {...} public boolean invarBar (String name, int flag) {...} c) Constructor Tests Constructors may have pre-, post-, and invariant-condition tests that will be evaluated before, after, and "around" (both before and after) the method execution, respectively. Unlike instance method tests, constructor tests can be defined for private constructors and they will be evaluated, since in some cases instance creation might only be done with a private constructor call (e.g., in factory patterns) and proper construction is important. Precondition Test Format: ... static boolean pre (arglist) {...} The method may be private, but it should only be private if the constructor is private. The method must be static, because the instance doesn't exist yet upon which to call an instance test method. It must return boolean. The name must be the class name, which is of course equivalent to the constructor name, prefixed with "pre". It must take the same argument list as the constructor being tested. Postcondition Test Format: ... static boolean post (arglist) {...} The method may be private, but it should only be private if the constructor is private. The method must be static, primarily for "symmetry" with precondition and invariant tests, even though after the constructor returns the instance does exist with which a call to an instance test method could be made! This "symmetry" simplifies the rules for constructor tests and thereby minimizes the potential for confusion. It must return boolean. The name must be the class name, which is of course equivalent to the constructor name, prefixed with "post". It must take the same argument list as the constructor being tested. (Note that because constructors never return a value, test methods always take the same argument list as the constructor being tested.) Invariant Condition Test Format: ... static boolean invar (arglist) {...} The method may be private, but it should only be private if the constructor is private. The method must be static, because before the constructor call the instance doesn't exist yet upon which to call an instance test method. It must return boolean. The name must be the class name, which is of course equivalent to the constructor name, prefixed with "invar". It must take the same argument list as the constructor being tested. Example: For "public MyClass (String name, int flag)" constructor, public static boolean preMyClass (String name, int flag) {...} public static boolean postMyClass (String name, int flag) {...} public static boolean invarMyClass (String name, int flag) {...} d) Class Invariant Tests Class invariant tests evaluate the state of an instance before and after every non-private instance method and constructor call, with the exception that they aren't evaluated before the constructor calls, since the object isn't initialized to a valid state yet, nor are they called before calls to "get" or "set" JavaBeans methods, since lazy evaluation may be used to initialize or modify the state. (Recall, however, that any class-level, field-level, or constructor-level invariant- or post-condition tests could test the state, so careful construction of tests is required.) Test Format: [!private] boolean invar () {...} The method should not be private, since the contract is part of the interface exposed to clients and/or derived class writers. It must return boolean. The name must be the class name, with the first character capitalized, prefixed with "invar". It must take no arguments. It would be easy to confuse class-level invariants with constructor invariants. You can tell the difference because constructor invariants are always declared static, since the instance doesn't exist before the constructor call, while the class-level tests are actually instance state tests. Don't confuse the word "class-level" with "static"; the tests are not static, "instance- independent" tests. Also, test methods for constructors that take arguments also take the same argument list, while class-level invariant tests always take no arguments. ----- Configuring the Behavior The behavior of Contract4JBeans can be configured through property files or at runtime using API calls. At startup, Contract4J will look for property files named "contract4j.properties", "contract4jbeans.properties", "contract4j.xml", and "contract4jbeans.xml". The XML files must follow the new Java 5 XML format for properties. Ifmore than one of these files is found, settings in the later ones override settings in the earlier files. The default file name can be overridden by defining the environment variable "$CONTRACT4J_PROPS_FILE" or the Java System property "com.aspectprogramming.contract4jbeans.properties.filename". (The latter value takes precedence.) Which ever name(s) is used, Contract4JBeans searches for matching files in the following directories, in order: $CONTRACT4J_HOME (environment variable - if defined) $HOME (environment variable) user.home (System property - usually equivalent to $HOME) user.dir (System property - the current working directory) com.aspectprogramming.contract4jbeans.properties.dirname (Sys. prop. - if defined) The last values read among all the files processed take precedence. In the discussion below about particular properties, they can be defined in any of these files. Note: For all properties currently defined, if a value is empty, it is ignored! 1) Turning Tests On and Off a) Build Time To completely disable contract checking, compile without "contract4jbeans.jar" in your path. This is recommended for "production" builds when you don't want any test overhead. b) Property Files To enable or disable test types in a property file, set the following properties: com.aspectprogramming.contract4jbeans.contracts = true|false com.aspectprogramming.contract4jbeans.pre = true|false com.aspectprogramming.contract4jbeans.post = true|false com.aspectprogramming.contract4jbeans.invar = true|false Where the property value must be "true" or "false" (actually all non-true values case ignored, are treated as false). The "contracts" property enables or disables all others, if present, unless the one or more of the more specific properties are specified, in which case it dominates. c) API Calls To disable one type of test, e.g., precondition tests, you can do this programmatically, by calling the following static method: import com.aspectprogramming.contract4jbeans.Contract4J; ... // Turn off precondition tests Contract4J.setEnabled (Contract4J.TestType.Pre, false); // Turn on precondition tests Contract4J.setEnabled (Contract4J.TestType.Pre, true); // Turn off postcondition tests Contract4J.setEnabled (Contract4J.TestType.Post, false); // Turn on postcondition tests Contract4J.setEnabled (Contract4J.TestType.Post, true); // Turn off invariant tests Contract4J.setEnabled (Contract4J.TestType.Invar, false); // Turn on invariant tests Contract4J.setEnabled (Contract4J.TestType.Invar, true); 2) Configuring "Reporters" By default, all output is written to using a simple logging-like interface called "Reporter", defined in com.aspectprogramming.contract4jbeans.Reporter. It supports 5 levels of output and the ability to set the message threshold. A default implementation writes to standard out or error with a threshold of "Warn". Separate Reporters can also be defined and configured for some of the other objects used by Contract4J (discussed below). a) Property Files To specify a different global Reporter class on the CLASSPATH, and/or to change the threshold in a property file, set the following properties: com.aspectprogramming.contract4jbeans.reporter = com.aspectprogramming.contract4jbeans.reporter.threshold = Where " " is a fully-qualified name of subclass of "Reporter" and where " " is one of: d, D, debug, Debug - debug level i, I, info, Info - info level w, W, warn, Warn - warning level e, E, error, Error - error level f, F, fatal, Fatal - fatal level b) API Calls To set the reporter or to change the threshold programmatically, use the following API calls: import com.aspectprogramming.contract4jbeans.*; ... Reporter r = ...; // Define instance appropriately... Contract4J.setReporter(r); ... Contract4J.getReporter().setThreshold(level); where "level" is one of Reporter.Level.Debug Reporter.Level.Info Reporter.Level.Warn Reporter.Level.Error Reporter.Level.Fatal If you implement your own Reporter, e.g. to use a logging API like Log4J, use WriterReporter.java as an example of how to proceed. If you just want to write to a particular output "stream" and that stream is a subclass of "java.io.Writer", you can do the following: import com.aspectprogramming.contract4jbeans.*; ... Writer myWriter = ...; WriterReporter wr = (WriterReporter) Contract4J.getContractEnforcer().getReporter(); wr.setWriters (myWriter); // use the same Writer for all levels or wr.setWriter (level1, myWriter); // use only for "level1" and "level2" wr.setWriter (level2, myWriter); Note that the default Reporter instance returned by this "getReporter()" call actually returns an object of the derived type "WriterReporter" (com.aspectprogramming.contract4jbeans.WriterReporter). The cast to WriterReporter assumes that Contract4J.getContractEnforcer().setReporter(...) hasn't been called with a different Reporter that is not of type WriterReporter. Note: there are no corresponding properties for these configuration options. 3) Configuring Test Discovery and Handling The interface TestMethodFinder specifies an abstraction for how test methods are found. TestMethodFinderHelper implements some of the methods and the rest of the methods are implemented with a default algorithm used by Contract4J in the DefaultTestMethodFinder class, a subclass of TestMethodFinderHelper. TestMethodFinder also uses the system default Reporter, but you can specify and configure a unique one, if you want. TestMethodFinder reports when a test method is found or not found. The threshold (a Reporter.Level value) is a configurable property. Similarly, ContractEnforcer specifies an abstraction for how test methods are invoked and what to do if the test fails. ContractEnforcerHelper implements some of the methods and the rest have a default algorithm implemented by DefaultContractEnforcer, a subclass of ContractEnforcerHelper. When test failures are reported, the stack trace can be printed. This is a configurable property. ContractEnforcer also uses the system default Reporter, but you can specify and configure a unique one, if you want. a) Property Files To specify different objects, to configure their properties, to change the Reporter objects used by them, and to change the properties of those reporters, set the following properties: com.aspectprogramming.contract4jbeans.contract.enforcer = com.aspectprogramming.contract4jbeans.contract.enforcer.show.stack.trace = true|false com.aspectprogramming.contract4jbeans.contract.enforcer.reporter = com.aspectprogramming.contract4jbeans.contract.enforcer.reporter.threshold = com.aspectprogramming.contract4jbeans.test.method.finder = com.aspectprogramming.contract4jbeans.test.method.finder.found.level = com.aspectprogramming.contract4jbeans.test.method.finder.not.found.level = com.aspectprogramming.contract4jbeans.test.method.finder.reporter = com.aspectprogramming.contract4jbeans.test.method.finder.reporter.threshold = The test.method.finder.found.level and test.method.finder.not.found.level are threshold levels for reporting a test method was or was not found, respectively. The default values are "Debug" and "Info", respectively, the rational being that you might be interested in knowing when a test method is missing and hence needs to be added, but you only want notification of a test found when debugging. Warning: If you change the threshold of a reporter, but you didn't define a unique instance separate from the global reporter, the change will actually be made to the global reporter! b) API Calls The instances of the "Default*" classes that are used by the Contract4J aspect can be replaced with alternative implementations. To set a new ContractEnforcer, use Contract4J.setContractEnforcer(...); To set a new TestMethodFinder, use Contract4J.getContractEnforcer().setTestMethodFinder(...); To set or configure the Reporter for either object, both support "getReporter()" and "setReporter()" methods. (See the warning in the previous subsection.) ----- How Contract4JBeans Works at Compile and Runtime All classes with tests must be compiled with AspectJ with "contract4jbeans.jar" in the CLASSPATH. Or, the jars containing precompiled Java files must be woven by AspectJ; put those jars in the "-inpath" option to ajc or the corresponding "inpath" or "inpathRef" options for the iajc ant task. During compilation, advice will be inserted before and after ALL non-private method calls, all constructor calls, and all field accesses in the classes that implement the Contracts marker interface, as described above. At runtime, when a join point with advice is executed, the advice uses Java and AspectJ reflection to find a test method that corresponds to the join point. The method must match the format specifications described above. If a matching method is not found in the same class, parent classes and interfaces[1] are searched. If a method is found, it is executed. If it returns false, an error message is reported, a com.aspectprogramming.contract4jbeans.ContractError (an unchecked exception) is thrown, and program execution is terminated. [1] AspectJ's intertype declaration mechanism can be used to insert "default" methods into interfaces. The test/example IContractTestClass2, IContractTestClass2_Contract.aj, and ContractTestClass2 illustrate how this can be done. IContractTestClass2.java A normal Java interface IContractTestClass2_Contract.aj Introduces test methods into classes implementing the interface. It cannot insert Constructor tests, however, since they must be static and AspectJ respects the Java requirement that interfaces can only declare instance methods ContractTestClass2.java A normal Java class that implements IContractTestClass2 and "picks up" the tests in the corresponding aspect. Summary and "Tips": 1) Every class defining tests must explicitly implement the Contracts interface or define an aspect that "introduces" this interface through the "declare parents:" construct. Otherwise, all tests in the class are ignored. 2) All tests must obey the specification conventions defined above. There are a few "gotchas" to watch. - Postcondition methods must declare as their first argument a parameter of the same type as the return type of the method they are testing. The rest of the argument list must match the list of the method under test. The value returned by the method will be passed as this first parameter. However, if the method returns void (including constructors), then no "return" parameter is declared. In this case, the test method's argument list must match the original method's argument list exactly. - Constructor test methods must be declared static, since the instance is being constructed during precondition and invariant test execution. While the instance does exist when postcondition tests are executed, for simplicity and "symmetry", these tests are also static. - Class invariant tests are instance state tests, not static (instance- independent) tests. So, they are not declared static. Hence, an invariant test for a default (no argument) constructor is static, while the class invariant test, which also takes no arguments, is an instance method. Note that these two methods will have the same name: "invar "! - You can only define one pre, post, and invariant test method for each method and constructor. You can define only one field invariant test per field and one class invariant test per class. This is a result of the specific method signature format rules; it is not possible to define multiple tests of a particular type unambiguously. - Test methods defined for other test methods, and for static and private instance methods are ignored. However, test methods for private constructors are supported. 3) There are several supported configuration options. The details are described above. - Which types of tests, pre, post, and/or invar, are enabled or disabled. - Setting the reporting (output) threshold. - Specifying the output "sink" where output is written (e.g., standard out/err, Log4J, etc.) - The class used to locate test methods. - The class used to execute test methods and handle failures. 4) Avoid writing test methods with "side effects", as they may be turned off, thereby changing the runtime behavior of your application! --- Invocation and Configuration of Contract4J Contract4JBeans has been tested on Linux, MacOS X, and Windows. To build it from scratch, the following third-party tools are required, along with corresponding "HOME" environment variable definitions needed by the ant build scripts: 1) JUnit 3.8.1 (JUNIT_HOME) 2) Java 1.5 (JAVA_HOME). 3) AspectJ 1.5 (ASPECTJ_HOME). 3) Ant 1.6.3 (ANT_HOME) Also define "CONTRACT4J_HOME" to be the directory ".../contract4jbeans_030/contract4j" where you installed it. For your convenience, you can use the build driver scripts "build.sh" or "build.bat". Edit the values of the environment variables defined in the scripts as appropriate or define them in your environment before invoking the scripts. Only Java and AspectJ are required if you simply use the built jars in the distribution. Make sure your build process either compiles with AspectJ or weaves precompiled jar or class files as described in the section "How Contract4JBeans Works at Compile and Runtime". ---- Contract4J5 The Contract4J5 and Contract4JBeans distributions are separate. both can be found by following the links at contract4j. ** TODO Items: Here is a brief list of some of the more important "todo" items, roughly in order of importance. --- V.0.3.0 1) Fix bug that prohibits covariant postcondition tests from working (see above). 2) Consider support for Java 1.4 and perhaps 1.3, since the bean-style interface uses no Java 5 constructs (e.g., annotations). However, the current implementation does use Java 5 constructs internally. 3) Exploit aspects to manage the "Reporter" objects, which are hard-coded in classes that use them. ** Notes for Each Release *** v0.3.0 February 20, 2006 Changing version labeling and package scheme as discussed previously. No other functional changes were made. *** v0.2.0 October 5, 2005 Implemented caching in the algorithm that searches for test methods, so this process isn't repeated over and over again. See src/com/aspectprogramming/contract4j/ContractMethodCache.aj for details and approximate timing numbers. *** v0.2.0M1 August 15, 2005 Contract4JBeans2 is a complete rewrite that introduces a new model for defining contracts and a simpler adoption process. The previously-required preprocessor step, running Sun's Annotation Processor Tool (APT), has been eliminated. Therefore, adoption of Contract4JBeans is a simpler process of including the "contract4J.jar" file in the ASPECTPATH for weaving (and optionally for compiling) and runtime deployment. Then, the developer writes DbC tests in plain Java. The new model for defining contracts does not use annotations, which allows its use in pre-Java 5 environments. Instead, a JavaBeans-like convention is used to define the tests, as discussed elsewhere in this README. This is a milestone release with a number of planned enhancements before the final release. See the TODO items above for details. ** For Further Information... contract4j/ is the home page for Contract4J. It is hosted by Aspect Programming, https://www.aspectprogramming.com/, where you'll find more information and whitepapers on Contract4J and Aspect-Oriented Software Development (AOSD), in general. The definitive site on AOSD is https://www.aosd.net. See https://www.eclipse.org/aspectj for information on AspectJ. Note that there are plans to incorporate Contract4J into the new standard library for AspectJ that will be introduced with the forthcoming, final AspectJ5 release. For alternative approaches to doing Design by Contract in Java, see the "Barter" project, which uses XDoclet and also generates AspectJ. Barter partially inspired Contract4J. https://barter.sourceforge.net/. There is a discussion group doing DbC in Java and possibly getting a future version of Java to support DbC natively. See https://dbc.dev.java.net/. Some more sophisticated approaches to program correctness include the J-LO tool for runtime checks of temporal assertions about the program. https://www-i2.informatik.rwth-aachen.de/Research/RV/JLO/ Another project is the Java Modeling Language (JML), which supports DbC for Java. https://www.cs.iastate.edu/~leavens/JML/ </pre>