In this subsection we create the Traditional contract, which doesn't allow a given client to overdraw a given account. Notice that this is not the same as preventing the balance of an account of becoming negative, because not all owners of an account are required to interact with it through the Traditional contract. This contract therefore allows great flexibility, because making overdrafts possible or not can be decided separately for each customer-account pair.
For this example we open the already provided contract.
In the File menu, select Add->Add Contract.... In the file dialog box, select the Traditional.ctr file in the source directory. A contract node is added to the project pane on the left, and the message pane shows that it has been successfully compiled.
Right-click on the Traditional node, and select the Edit Textual command to open an edit window with the contract's source.
Simple contracts like Traditional have the following syntax, where ... denotes repetition and [] surrounds optional elements:
contractcontractNameparticipantsparticipantName:participantClass;...coordinationrule1Name: when * ->>methodCall [&& (JavaBooleanExpression)...] [with (JavaBooleanExpression)failure {JavaCode}][do {JavaCode}];rule1Name:...end contract
As can be seen in the Traditional.ctr file, Java-like comments are
also possible. A contract has a name and two sections:
the participants section lists one or more participants, and the coordination
section contains one or more rules. Each participant must be of a class that has
been previously added to the project. Each rule has a trigger (after when),
an optional guard (after with), and an optional body (after do). A
trigger is a call to a method of one of the participants and (possibly)
additional conditions. The Java
code in the body is executed if the trigger occurs and the guard is true. If the
trigger occurs and the guard is false, then the code after failure
will execute. That code should end either by throwing an exception declared in
the signature of the trigger method or by returning a value. If the guard is omitted, it is the same as writing true;
if the body is omitted, the method call is executed. The compiler only checks if
the recipient of the method call is one of the participants,
if the method is implemented by the participant class and
if the arguments have exactly the same names as the parameters defined in the method.
The last requirement is needed for technical reasons, in particular for the way contracts are compiled into Java classes. Everything else (i.e., the Java boolean expressions and the Java code) is passed verbatim to the Java class generated for the contract. However, CDE checks if the Java code that appears in contract definitions is syntactically correct.
For the concrete case at hand, Traditional is a contract with two
participants, a customer and an account. The contract has a single rule that
will be executed when the withdrawal method of the participating account is
called by the participating customer. Calls from other customers are not
intercepted because they are outside the scope of this contract. If we wanted to
check the withdrawals of any customer, then the only participant would be the
account and the condition (customer == c) would be omitted. To
prevent overdrafts, which is the purpose of this
contract, the guard checks that the amount to be withdrawn is not greater than
the current account balance. If the customer tries to withdraw more money than
available, an exception will be raised. The body is omitted and as such no additional
actions are taken: only the message that triggered the rule is processed. In
other words, the Traditional contract only superposes a further condition on the
withdrawal message to be executed, it does not change it nor does it add any
behaviour.
Since the contract introduced an error condition (namely, overdrafts) that
was not dealt with in the Account component, we have to add it to
the exception types:
Open the file src/Bank/AccountExceptionTypes.java.
Add the line public static final String LIMIT_EXCEEDED = "the
withdraw limit was exceeded";
Close the file. A dialog appears asking you whether you want to save the changes. Choose "Yes".
To practice how to change and re-compile a contract, and to see some error messages (which will inevitably occur when you write your first own contracts), we now introduce on purpose some errors into the Traditional contract:
Choose Save As... from the File menu. In the file dialog, give the name Traditional.original.ctr. Click Save. This will save a copy of the file, so that you later can restore the original. The edit window remains "attached" to the Traditional.ctr file, as can be seen from the path name in the lower right corner of the edit window.
Change "customer : Customer" to "client
: Client", "account.withdraw(amount, c)" to "account.withdrawal(amount,
client)",
and "account.getBalance()" to "account.balance()".
Compile the file by choosing Compile->Contract Compiler from the Project menu. The compilation command automatically saves the file.
Two error messages appear in the output window. Clicking on an error message
highlights the corresponding line in the edit window. The first error message states that the Client
class is unknown in this project, and hence contracts cannot use it. The second
message indicates that withdrawal is not a valid method of the Account
class. Notice however that balance is not a valid method either,
but it has not been detected, because rule guards are not compiled, they are
copied verbatim to the generated Java file. For the same
reason, no error is issued for the undefined customer variable used
in the trigger.
Now change "withdrawal" to "withdraw" and compile again.
This time the compiler detects that the second parameter of withdraw has not the
same name as in the signature of withdraw in the Account
class. To restore the original file proceed as follows:
Close the Traditional contract edit window.
Open the Traditional.original.ctr file.
Save it as Traditional.ctr.
Select File->Add->Add Contract... and choose the Traditional.ctr file.
After successfully adding the contract to the project, it is time to generate the Java files that actually implement the contract. There are several ways to do it:
Right-click on the Traditional node in the project tree and choose Generate from the pop-up menu.
Click on the Traditional node to select it and then either:
Press Ctrl-F9.
Click the generation button (
)
in the toolbar.
Select Generate from the Project menu.
A message will appear saying that the contract has been generated, and the Traditional node will have a child node Generated Files with one file for the contract and one file for each participant. Again, you can view the files if you wish but don't edit them.