Investigating the limitations of Java annotations for input validation

2 downloads 0 Views 111KB Size Report
age of various frameworks, ranging from persistence ... to define validation tests for Java object properties .... 1) Use a list of predefined annotations, and.
1

Investigating the limitations of Java annotations for input validation Federico Mancini, Dag Hovland and Khalid A. Mughal

Abstract—Recently Java annotations have received a lot of attention as a possible way to simplify the usage of various frameworks, ranging from persistence and verification to security. In this paper we discuss the usage of annotations for input validation purposes. We investigate their advantages and more importantly their limitations in the design of validation tests. We report on our experience in the development of an annotation-based framework for input validation, discussing possible solutions and compromises that were necessary.

I. I NTRODUCTION Java annotations [1] allow meta-information to be attached to a program in a standard and structured way, such that it is possible to automate its processing. The reflection facility in Java makes it possible to retrieve annotations at run-time, and trigger special actions accordingly. This approach allows many aspects of an application to be specified in the application code, eliminating the need for external configuration files (e.g. XML) or inserting extra code in the application. It is not surprising that many frameworks have adopted annotations [2], [3], [4], [5]. In this paper we focus on the use of annotations to define validation tests for Java object properties and report on our experience in the implementation of a framework for this purpose [6]. Some related work can be found in [7], [8], [9], [3]. Our aim is to give an overview of possible solutions to technical and logical problems that arise with the use of annotations in this context. To begin with, we review systematically some typical issues and standard solutions related to the use of annotations in a framework. For the remainder of the paper we discuss some new challenges that we had to face when providing our framework with new features

that were not considered before. We will show and justify our approach to these problems by critically comparing different solutions we considered. It will become gradually clearer which the current limitations of annotations are, and how far they can be pushed for content validation purposes. The main discriminating factor we use to distinguish between good and bad solutions, is the influence a technical solution has on the usability and flexibility of the framework. We also give higher priority to the extensibility of the framework, i.e., the possibility for the user to define new validation tests in a flexible way and easily reuse the existing ones. We assume the reader has some familiarity with the basic concepts of annotations and reflection in Java 6 [10], but we start with a short description of our framework and a running example. This should be sufficient to follow the rest of the paper. II. W ORKING EXAMPLE In simple terms, content validation in Java means to check that the properties of an object satisfy certain constraints. Annotations can be used to associate validation tests to properties directly in the application code. For example, given an object representing the content of a web form for money transfer, we want to make sure that the property representing the amount to be transferred, is an integer in a certain range. In our framework, this can be done by applying an annotation called @IntRange to the method returning such an amount. The corresponding code is shown in Figure 1. At run-time the annotation and the value of the property will be extracted by means of reflection. This value will then be passed for validation to

2

public class WebForm{ private int Amount; ... @IntRange(min=1,max=100000) public int getAmount {return this.Amount;} } Fig. 1: Example of validation annotation.

the actual test represented by the annotation. Let us point out that in practice it can be possible to annotate directly a field of an object as they do in [3]:

public class WebForm{ private int AmountEuro; private int AmountCent; ... @IntRange(min=0,max=100000) @ComposedRange public int getAmountEuro {return this.AmountEuro;} @IntRange(min=0,max=99) @ComposedRange public int getAmountCent {return this.AmountCent;} } Fig. 2: Example of cross-validation.

@IntRange(min=1,max=100000) private int Amount; However we decided not to offer this possibility for a simple reason: if the field is private, the framework must first change its visibility to public by means of reflection, before to be able to retrieve its value. We consider a very bad practice to allow an external framework to tamper with the visibility of object properties. Our framework also offer validation of interdependent properties. In other words, it is possible to validate multiple properties that can be considered valid only if they satisfy some constraint simultaneously. In order to do this, we introduced cross-annotations. Cross-annotations can be used to tag two or more properties, so that they will be validated together. It is possible to give an example by extending the example in Figure 1. If the amount were split into two fields in the web form, i.e., Euro and Cent, and into two corresponding properties in the WebForm object, then we would need some more elaborated validation. In fact, if the range to check is now between 0.01 and 100000.00, it would not be enough to check separately the range of each field. For example, the value 100000.99 would not be a valid total amount, even though each field individually contains a valid amount. A further check that involves both properties simultaneously would be necessary. To do this we can define an annotation @ComposedRange, which can be applied to both these properties as shown in Figure

2. The test corresponding to this annotation checks that both values are greater than 0 and if one is exactly 100000, then the other must be exactly 0. To avoid confusion, we call the annotations that are used to validate single properties, as @IntRange, property-annotations. III. L ACK OF INHERITANCE The first issue when creating annotations for use with a specific framework is: how to distinguish the annotations used by the framework from other annotations at runtime? The problem is that an annotation cannot be extended by other annotations, i.e., there is no inheritance (or subtyping) relationship between annotations. To resolve this problem there are mainly two approaches: 1) Use a list of predefined annotations, and determine at runtime whether an annotation belongs to this list. 2) Define a meta-annotation that can be used as a marker. The first approach is convenient if the set of annotations used by the framework is fixed and not supposed to be extended by the user, as in the case of Struts validation annotations [11]. The latter is convenient when we want to let the user define custom annotations that can extend those defined

3

by the framework, as in our framework and the Hibernate Validator [3], which is based on [9]. We use actually two markers: @Validation to define property-annotations and @CrossValidation to define cross-annotations. Hence the declaration of @IntRange and @CrossRange would look like: @Validation public @interface IntRange{ int min(); int max(); } @CrossValidation public @interface CrossRange{ } IV. U SER DEFINED ANNOTATIONS Many problems discussed in this paper arise from the fact that we want to give the user the means to define custom annotations. In fact, this implies that the annotations must have some standard structure so that framework can process them without knowing their specific usage. One such problem was discussed in the previous section. However, there are more issues that have to be addressed. For instance, how to define the classes containing the actual validation tests in a standard way? and how to map them to the corresponding annotations? The first problem has an easy solution since we are considering standard Java classes: it is possible to define a common interface they must adhere to: public interface IPropertyTester { public boolean runTest(A an, I o) throws ValidationException; } A class implementing this interface is represented by a property-annotation of type A, which is, in turn, applied to methods returning objects of type I. For instance:

public boolean runTest(IntRange r, Integer v){ return( v >= r.min() && v

Suggest Documents