Oleg has posted 2 posts at DZone. You can read more from them at their website. View Full User Profile

Bug-safety code: design pattern Null Safe

02.21.2013
| 4614 views |
  • submit to reddit
Contents:
I.  Describing the problem
II. Overview of existing solutions
III.The solution without aspects
IV. Solution using AspectJ
V.  Dynamic aspects
VI. Afterwords
VII.References and used documentation

I. Describing the problem

Programming on Java (and some other languages) programmers often deal with string variables and other types that represented object-boxed of simple types for example: Boolean, Integer, Double. There are bean (here in after as bean I mean class supports recommendations Java Beans):
public class SomeBean{
private String strField;
	
public void setStrField(String strField){
	   this.strField=strField;
}
		
public String getStrField(){
	   return  strField;
}	
// further declarations are omitted 
}
In my work the fields should be null-checked every time using this bean:
String s=someBean.getStrField();
if (s!=null){	
	if (!s.isEmpty()){		// s.length()>0 Java 5-
		doSomeThing();
	}
}
So do I with any field-object. If I have forgotten to do this, our application will be closed sooner or later because of unchecked exception NullPointerException. Of course by shifting mistakes in a system I can catch urgently this exception but it means very bad code. Performance with productivity would be wasted as the generation of exceptions equals to a few dozen simple commands. Such "null"-checking we have to do many times in the body of bean and around it. Sooner the bean is used and the application is more complex - more often we have to do "null"-check, and wherefore we have to do routine jobs. It means big possibility of missing something, in its turn this means increasing of probability dis-functions. As a developer I face a lot of time with such things. Further I introduce to your attention briefing of existing measures against null-dependencies, and the decision to avoid this problem based on aspects.

II. The overview of existing solutions

I supposed somebody have done it before and have overviewed the existing solutions.

1) annotation @NotNull from JSR-305 detailed in [1] and [2].
It posted before fields and methods returning objects. It helps to define IDE in the amount of source codes.
@NotNull
public Color notNullMethod(){
   	return null;
}

Disadvantages: annotation doesn't solve the problem and we have not to forget matching it in all weak code's points, as JSR-305 is inadequate to analyze complex code.

2) Checker Framework JSR-308 described in [3] [4].
Represents more developed method in comparison with JSR-305 because of introducing additional checking of code to analyze more complex code while "null"-checking, and gives lots of another useful annotations for checking concurrency.
Advantages: developed framework.
Disadvantages: annotation @NotNull doesn't gives the decision, we must not to forget to post annotation in all weal points

3) The Java Annotation Checker (JACK) [5] has the same merits and demerits as JSR-305

4) Project Coin, contains a lot of recommendations of improving Java (under Groovy influence), details in [6] [7].
Lets speak about the function:
public String getPostcode(Person person){
   if (person != null){
       Address address = person.getAddress();
	if (address != null){
	   return address.getPostcode();
	}
        }
    return null;
}
Project Coin offered to rewrite the function using NullSafe-navigation:
public String getPostcode(Person person){
    return person?.getAddress()?.getPostcode();
}
Unfortunately Oracle have not included that function abilities in the Java 7 so we have to use other solutions for Java.

5) Usage Groovy. Actually NullSafe-navigation described above founded from Groove [8].
Advantage: significant simplification of code
Disadvantage: we must to remember about all weak points

6) Apache Commons, StringUtils [9] and PropertyUtils [10]:
StringUtils.isBlank(null)= true
StringUtils.isBlank("")= true
StringUtils.isBlank(" ")= true
String firstName = (String) PropertyUtils.getSimpleProperty(employee, "firstName");
String city = (String)PropertyUtils.getNestedProperty(employee, "address(home).city");

Advantages: it makes more simple the procedure of checking
Disadvantages: we should check it all around it necessary. In this case we need some additional code for data handling of exclusions which were generated by PropertyUtils.

7) Static code analyzers FindBugs [11] and PMD [12] have plugins for most IDE.
Advantages: Very power and useful tools analyzing static code without pre-injection of annotations.
Disadvantages: It shows week points bit not gives a completed decisions for each situations.

III. Decision without utilizing Aspects

Usually data-reading of Null-checking happens ofter than procedure of value's storing. So the reading operation have to be optimized. In this case we can rewrite fields of beans interaction as below:
public class SomeBean{
private String strField="";
	
public void setStrField(String strField){
	   if (strField!=null){ this.strField=strField; }
}
		
public String getStrField(){
	  return  strField;
}
}

So we can ignore Null-values. In this case fields ought to be forceful ignored. All that facts together helps us to create null-safe code in which we need not to make numerous null-checking. This makes simple the code-producing and totally reduces probability to miss something.
Advantages: we needn't take care about null-checking for fields. It simplify code and increase performance.
Disadvantage: for each field we have to write initial code and null-cutting

IV. Solution by AspectJ

For not making null-cutting in each case we use aspects to provide thorough functionality. Obviously we need to operate at field's levels operations with fields must be inside of beans and thorough functionality should be total otherwise it has no sense.
For including NullSafe in fields and classes lets introduce annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface NullSafe {
	boolean getter() default true;
}

@NullSafe(getter=false) - we need for simplifying and optimizing if of course the developer has a time and isn't lazy for initialization fields in beans. In this case only record in field would be under aspects' influence.
For easy future modification aspect was written with utilizing AspectJ 5 (annotated) and it needs Java 5+.
Please see references [13],[14],[15].
@Aspect
public class NullSafeAspect {
	
	@Pointcut(value="within(@NullSafe *)")	// support annotation by class
	public void byClass() {}
	
	@Pointcut(value="!within(@NullSafe(getter=false) *)")
	public void byClassNoGetter() {}
	
	@Pointcut(value="get(@NullSafe Long||Integer||Double||Float||Short||Byte||java.math.BigDecimal *)")
	public void fieldGetNumerics() {}
	
	@Pointcut(value="!get(@NullSafe(getter=false) * *)")
	public void fieldNoGetter() {}
	
	@Pointcut(value="get(Long||Integer||Double||Float||Short||Byte||java.math.BigDecimal *)")
	public void classGetNumerics() {}
	
	@Pointcut(value="set(Long||Integer||Double||Float||Short||Byte||java.math.BigDecimal *)")
	public void classSetNumerics() {}
	
	@Pointcut(value="set(@NullSafe * *)")	// all field-annotated 
	public void fieldSetAllSupported() {}
	
	@Pointcut(value="classSetNumerics() || set(String||Boolean||Character *)")
	public void classSetAllSupported() {}
	
	@Pointcut(value="fieldGetNumerics() || get(@NullSafe String||Boolean||Character *)")
	public void fieldGetAllSupported() {}
	
	@Pointcut(value="classGetNumerics() || get(String||Boolean||Character *)")
	public void classGetAllSupported() {}
	
	
	@Around(value="(fieldSetAllSupported() || byClass() && classSetAllSupported()) && args(v)")
	public void aroundSet(ProceedingJoinPoint jp, Object v) throws Throwable{
		toLogSetter(jp, v);
		if (v!=null){ jp.proceed();}
	}
	
	@Around(value="get(@NullSafe String *) && fieldNoGetter() || byClass() && get(String *) && byClassNoGetter()")
	public String aroundGetString(ProceedingJoinPoint jp) throws Throwable{		
    	String v=(String)jp.proceed();    	
    	if (v==null){return "";}
    	return v;    	
    }
	
	private Field getField(JoinPoint jp){
		try{
			Signature sig=jp.getStaticPart().getSignature();
			Field field=sig.getDeclaringType().getDeclaredField(sig.getName());
			field.setAccessible(true);
			return field;
		}catch(Exception e){			
		}
		return null;
	}

	private Object getBean(JoinPoint jp){
		try {
			Field field=getField(jp);
			if (field==null){return null;}
			Object obj=field.getType().newInstance();
			field.set(jp.getTarget(),obj);
			return obj;
		}catch(Exception e){
			stackTraceToLog(e);
		}
		return null;
	}

	@Around(value="!fieldGetAllSupported() && get(@NullSafe * *) && fieldNoGetter() && byClassNoGetter()")
	public Object aroundGetBean(ProceedingJoinPoint jp) throws Throwable{		
		Object v=jp.proceed();
	    	if (v==null){
    			return getBean(jp);
    		}
    		return v;
    	}

	private Object getNumeric(JoinPoint jp){		
		try {			
			Field field=getField(jp);
			if (field==null){return null;}			
			Object obj=field.getType().getDeclaredConstructor(String.class).newInstance("0");
			field.set(jp.getTarget(),obj);
			return obj;
		}catch(Exception e){
			stackTraceToLog(e);
		}
		return null;
	}

	@Around(value="fieldGetNumerics() && fieldNoGetter() || byClass() && classGetNumerics() && byClassNoGetter()")
	public Object aroundGetNumerics(ProceedingJoinPoint jp) throws Throwable{
    	Object v=jp.proceed();
    	if (v==null){
    		return getNumeric(jp);
    	}
    	return v;
    }

}
The example of utilization of annotation :
@NullSafe
public class SomeClassTestByClass extends SomeClass {
...
}

@NullSafe
private String strField;	
@NullSafe
private HashMap<Integer,HashMap<String , String>> map;
@NullSafe(getter=false)
private ArrayList<String> listString=new ArrayList<String>();

Further explanations are given about the aspect. The annotation @Pointcut includes the description common one Within – means action inside class, in this case any class noted by @NullSafe. It supported numeric types Long,Integer,Double,Float,Short,Byte,BigDecimal, which are initialized via equal pattern. Also supported String,Boolean,Character. All class-boxes mentioned above included in term which are suspected of classes of aspect.
Any bean or class with constructor without parameters can be annotated via fields. Annotation of class means complete support for all kinds of fields. All this fields are initializing by reflection. Initialized in code our fields help us to increase performance without using getters only with @NullSafe(getter=false). Set() and get() - in point-cuts are responsible for records and reading fields of bean including operations inside fields. @Advice is responsible of point-cut actions. I don't believe that utilizing of @Before for set() is a good advise as for code performances. To prevent it we need to call exception. This is inconvenient for good performance.
By default aspect Singleton was created. All the processing is about the only one method in project for every one type. The complete example with tests is in the NullSafe-project in [18].
Advantage: thorough functionality only if it is necessary.
Disadvantages: we still need post annotations in classes and fields but thorough functionality for every classes without limits only using @NullSafe is incorrect solution.

V. Dynamic aspects

Usually Aspects present the modification of code and it operated while compiling. That is why efficiency and performance of program can't be under complex description of cut points influence. AspectJ is able to produce dynamic modifying byte-code with changing loader. We need it in case using aspects for byte-code which has no sources. In this case it is necessary to get rid of annotations and add mentions about classes in aspect NullSafeAspect (or create new aspect that would extends). As such technique is very specific for different kind of servers and mediums, I don't give a details in this article. Further about using dynamic aspects you can learn from [16] [17].

VI. Afterwords

Thank you for paying attention. This article was my effort with another point of view to having deal with wide-known problem. Possibly using null-values is necessary for specific algorithm or databases but in this case as for me is is often when we can have doubts whether algorithm or database is optimized. Approach described in my article could be usual measure for creating more compact and bugs-resisted code, and to became one of the design patterns on Java.

VII. References and used documentation*

1. http://jcp.org/en/jsr/detail?id=305
2. http://www.jetbrains.com/idea/documentation/howto.html
3. http://jcp.org/en/jsr/detail?id=308
4. http://types.cs.washington.edu/checker-framework/current/checkers-manual.html
5. http://homepages.ecs.vuw.ac.nz/~djp/JACK/
6. https://blogs.oracle.com/darcy/entry/project_coin_final_five
7. http://metoojava.wordpress.com/2010/11/15/java-7-awesome-features/
8. http://groovy.codehaus.org/Operators
9. http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/StringUtils.html
10. http://commons.apache.org/beanutils/apidocs/org/apache/commons/beanutils/package-summary.html#package_description
11. http://findbugs.sourceforge.net/
12. http://pmd.sourceforge.net/
13. http://eclipse.org/aspectj/doc/released/adk15notebook/index.html
14. http://eclipse.org/aspectj/doc/released/progguide/index.html
15. AspectJ in Action. Second Edition. ISBN 978-1-933988-05-4
16. http://www.eclipse.org/aspectj/doc/next/devguide/ltw.html
17. http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-aj-ltw
18. https://sourceforge.net/projects/nullsafe/files/
(*all the references given above were available during the writing the article)
02/2013 Oleg Nikolaenko
Published at DZone with permission of its author, Oleg Nikolaenko.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jeroen Van Dijk replied on Thu, 2013/02/21 - 3:37am

I agree with the @NotNull, with other annotations like @Valid you can have a lot of things have checked and can be assure of getting well-formed data into your function.
Part III is a small catch though:
public class SomeBean {
   private String strField = "";

   public void setStrField(String strField) {
     if(strField!=null){ this.strField=strField; }
   }
   publicString getStrField()   {
     return strField;
   }
}
consider this:
someBean.setStrField("set to something");
someBean.setStrField(null);
Assert.assertTrue("String isn't empty", StringUtils.isEmty(someBean.getStrField()));
Instead of changing the setter, you can also of course alter the getter, by if null, return blank string, and then you wont have this problem. But initializing your String properties with a default empty String and start your Collection properties with an empty one is a very good idea in your (hibernate?) model objects. I rather have empty collections than all those null checks. And if I still have to do, then I use this function often:
public static @NotNull Collection<T> nullSafeGet(@NullableCollection<T> collection) {
   if(CollectionUtils.isEmpty(collection)) {
     return new ArrayList<>();
   }
   return collection;
}
Tnx for the post about AspectJ solution, I want to read some more about that now! :-D

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.