Recipe 20.1. Validating Parameters Passed to a MethodProblemYou want to validate the parameters passed to a method. SolutionCreate an aspect that modularizes the parameter checking logic. Declare a pointcut to capture the execution of the method where the parameters are to be checked. The pointcut should expose the parameters to the corresponding advice so it can perform the checks. Depending on the result of the parameter checking, the advice will proceed with the methods execution or safely override the method whenever the parameters are not suitable. DiscussionValidation of method parameters is a chore every developer has to be concerned with whenever he has no control over how his clients will use a component. Even when the developer is writing the clients that will use a particular component, a useful practice would be to the values passed to a method are acceptable before working with those values. This checking often confuses the logic within a method by introducing initial checking code that has little relationship to the job that the method is to complete. By using AspectJ, you can check a method's parameters separately from the core business logic of a method to make the solution cleaner. The main method on a Java application is a common place where the parameters being supplied cannot be controlled and have to be checked before the application can continue execution. The normal means of achieving these checks is to code the test logic using an if statement into the main method and to produce an error message if the parameters provided on the command line do not meet the requirements of the application. Example 20-1 shows a typical main method with the traditional parameter validation and checking support code highlighted. Example 20-1. Interleaving traditional command-line argument checking with the business logic of the main methodpackage com.oreilly.aspectjcookbook; import java.net.URL; import java.net.MalformedURLException; public class TraditionalMainApplication { private static final String COMMAND_LINE_USAGE = "TraditionalMainAppliction usage :\n\n" + "\tjava TraditionalMainApplication <url>"; public static void main(String[] args) { if (args.length == 1) { try { // Assuming that the first argument supplied is a url // Concentrating on the business logic, not validation which // is handled by the aspect. URL url = new URL(args[0]); System.out.println("Application Started, doing stuff with " + url); } catch (MalformedURLException mue) { System.err.println(COMMAND_LINE_USAGE); System.err.println("Please enter a valid URL for <url>"); } } else { System.err.println(COMMAND_LINE_USAGE); } } } First, the traditionalMainApplication class declares the COMMAND_LINE_USAGE constant that contains a message that will be displayed to the user if a problem occurs with the arguments passed to the main method. Then an if statement within the main method checks that the right number of arguments have been supplied; if not, the error message is displayed. Finally, one of the parameters is a URL, and since this could be invalid, this is checked by a try/catch block. All of this occurs before the main method can do anything useful such as starting the application. Refactoring the checking logic into a separate method moves the problem to another part of the same class and doesn't offer a satisfactory solution. AspectJ can remove these messy beginnings to your application by creating an aspect such as the one shown in Example 20-2. Example 20-2. Parameter checking using an aspectpackage com.oreilly.aspectjcookbook; import java.net.URL; import java.net.MalformedURLException; public aspect VerifyMethodArgsAspect { private static final String COMMAND_LINE_USAGE = "MyAppliction usage :\n\n" + "\tjava MainApplication <url>"; public pointcut captureMain(String[] arguments) : execution(void MainApplication.main(String[])) && args(arguments); public pointcut createURLCalledinMainMethod( ) : call(java.net.URL.new(..)) && withincode(public void MainApplication.main(String[])); void around(String[] arguments) : captureMain(arguments) { if (arguments.length == 1) { // Test that the host and port are valid try { URL url = new URL(arguments[0]); proceed(arguments); } catch(MalformedURLException mfe) { System.err.println(COMMAND_LINE_USAGE); System.err.println("Please enter a valid URL for <url>"); } } else { System.err.println(COMMAND_LINE_USAGE); } } // If necessary soften the exception that would normally have been raised // if the url parameter was badly formed, but only in the validated main // method declare soft : MalformedURLException : createURLCalledinMainMethod( ); } The VerifyMethodArgsAspect aspect declares the captureMain(String[]) pointcut that interrupts the execution of the main method and provides the arguments to the corresponding advice. The around( ) advice then checks the number of parameters and that the URL parameter is valid before calling proceed( ) with the original arguments if everything checks out. If any problems occur with the arguments then the error messages are displayed in the same way as before. Using an aspect to modularize the parameter checking logic leaves your main method looking much neater without losing any of that important checking behavior, as shown in Example 20-3. Example 20-3. Providing the parameter checking logic via an aspect leaves the main method to focus on its jobpackage com.oreilly.aspectjcookbook; import java.net.URL; public class MainApplication { public static void main(String[] args) { // Assuming that the first argument supplied is a url // Concentrating on the business logic, not validation which // is handled by the aspect. URL url = new URL(args[0]); System.out.println("Application Started, doing stuff with " + url); } } The main method logic still uses the URL argument as before, but because the aspect has provided the necessary checking to ensure a problem will not occur, it is reasonably safe to use the declare soft statement to save the main method from having to explicitly handle the MalformedURLException. The createURLCalledinMainMethod() pointcut captures where the exception would normally be raisedi.e., when a java.net.URL instance is created and within the main methodand this is the only join point where the MalformedURLException is to be softened. Without this softening of the exception, the main method would have to handle the possibility of a MalformedURLException being thrown even with the aspect based checking. See AlsoThe execution(Signature) pointcut is described in Recipe 4.4; The args([TypePatterns || Identifiers]) pointcut is covered in Recipe 11.3; the around( ) form of advice is shown in Recipe 13.4; softening exceptions is discussed in Recipe 16.5. |