In order to get an impression of how this component works, we will start with an example showing the capabilities of this package.
To get started you need at least the jar of this component and the dependent i18n.jar in your classpath.
You already know contract based programming if you've ever implemented a java method: You specify a contract that defines which types of parameters are accepted by your method and you specify the type of tbe return value. This is how a method might look like that calcalutes speed:
public float calculateSpeed(float distance, float time, String timeUnit) { float speed; if (timeUnit.equals("s")) speed = distance / time; else if (timeUnit.equals("min")) speed = distance*60 / time; else speed = distance*3600 / time; return speed; }
So your method will accept two floats that pass the distance and the time to the method. Additionally the method requires the unit in which the time was measured. You can tell this by using the appropriated types, that reflect the desired values.
This example is incomplete, as no error handling is done. What happens if the method will be called with time=0? What if the user enters an invalid unit? As this part is not much fun, many programmers don't care much about handling errors in an appropriate way and by doing so cause much pain.
In the java world, you cannot declare which value ranges are valid for the parameters of your method. You might only accept values larger than zero for the time and only "s", "min" and "h" for the measuring unit.
Imagine that you want to provide a simple program, that allows the user to enter these three parameters and you want to provide the calculated speed. To calculate the speed is the very easy task, but as you might already know it is a lot more work to handle all possible errors, to display the user in a localized way which values are accepted and so on.
This is the moment where the contract component comes into play! You have to declare the possible values that are accepted by your method and the result that will be provided. This additional effort will be donated by handling all the rest. Let's have a look at the sources of the SpeedCalculator that is provided as an example in the sources of this component:
public class SpeedCalculator implements Processor { public final static String SPEED = "speed"; private final static String DISTANCE = "distance"; private final static String TIME = "time"; private final static String UNIT = "unit"; private final static String SECONDS = "s"; private final static String MINUTES = "min"; private final static String HOURS = "h"; ParameterDescriptor[] parameterDescriptors = new ParameterDescriptor[]{ new ParameterDescriptor(DISTANCE, new ParameterMessage("computeSpeed/parameter/distance"), new NumberConstraints( new Integer(0), null, true)), new ParameterDescriptor(UNIT, new ParameterMessage("computeSpeed/parameter/unit"), new StringConstraints( new String[]{SECONDS, MINUTES, HOURS})), new ParameterDescriptor(TIME, new ParameterMessage("computeSpeed/parameter/time"), NumberConstraints.POSITIVE)}; ResultDescriptor[] resultDescriptors = new ResultDescriptor[]{new ResultDescriptor( StateDescriptor.OK_DESCRIPTOR, new ResultEntryDescriptor[]{new ResultEntryDescriptor(SPEED, new LocalizedMessage("computeSpeed/result/speed"), new NumberConstraints(new Float(0.1), new Integer(Integer.MAX_VALUE)))})}; public Result process(Map parameters, Context context) { float distance = ((Number)parameters.get(DISTANCE)).floatValue(); float time = ((Number)parameters.get(TIME)).floatValue(); String timeUnit = (String)parameters.get(UNIT); float speed; if (timeUnit.equals("s")) speed = distance / time; else if (timeUnit.equals("min")) speed = distance*60 / time; else speed = distance*3600 / time; return new Result(StateDescriptor.OK, SPEED, new Float(speed)); } public ParameterDescriptor[] getParameterDescriptors() { return parameterDescriptors; } public ResultDescriptor[] getResultDescriptors() { return resultDescriptors; } }
You'll be shocked at this moment as you have to write down a lot more lines of code than in the classical java method example. But on the other hand you'll gain a lot! Launch the example that is included in this component by invoking the contrac-example.jar:
java -jar contract-example.jar
This example is using the InteractiveMailWrapper to build a multilingual application that handles the user input and ensures that the implemented method will only be called, when all input parameters match the defined needs.
As you can see in the example, you have to define constraints for all the parameters that your method will accept. You even have the possibility to provide a default value that will be used when no parameter value is provided. This means, that the parameter is optional.
You also provide constraints for the results that your processor provides.