Tuesday, November 12, 2013

Null safe dereferencing for java?

Nulls blocking your property chain?

Something smells.  You just want a little data, instead you get that nagging NullPointerException.  Urrrgghh!

Let's say you have a hot date.  And you need to get the address of the hot chick your about to go out with.

i.e.
  pickUpDate(contacts.getHotChick().getAddress());

There is only one problem.  You forgot you're a nerd, and you don't know any hot chicks.

NullPointerException!!!

Try this "Null Safe Get Handling" approach.

Contacts contacts = (Contacts) NullSafeGetHandler.getProxy(myContacts);

Such that:
contacts.getHotChick().getAddress()  will return null, if there as no hot chick to be found.

But if you some how do know a hot chick, (probably your cousin), it will return her address.

Everything is expressed cleanly in a nag-free zone, save for the initial proxy wrapping.

How?

Basically the NullSafeGetHandler implements MethodHandler, an api for catching proxy method invocations.

This process goes like this:
  1. Create proxy of a real object
  2. Tell the proxy to handle all methods with names starting with "get"
  3. On "get" method invocation, return a real value if of a specified type, otherwise return a proxy of the return type.
This way the property chain will not be interrupted by nulls until the desired return type is reached.

Yes, it uses reflection.  Yes, you have to specify a return type.  But when all your doing is coding up a simple DTO or query parameters for a stored procedure for an O(1) operation, it'll do.

In most cases, I just want the first type from the "java.lang" package out of my property chain, so I implemented as so.  But you can customize it to your needs.

 public class NullSafeGetHandler implements MethodHandler {  
      private NullSafeGetHandler(Object source) {  
           super();  
           this.source = source;  
      }  
      /**  
       * Creates a proxy factory that produces proxy objects of which intercepts "get" prefixed methods.  
       *   
       * @param obj  
       * @return null safe proxy object  
       */  
      public static Object getProxy(Object obj) {  
           ProxyFactory factory = new ProxyFactory();  
           factory.setSuperclass(obj.getClass());  
           factory.setFilter(  
            new MethodFilter() {  
                          @Override  
                          public boolean isHandled(java.lang.reflect.Method method) {  
                               return Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("get");  
                          }  
            }  
                );  
           Object proxy = null;  
           try {  
                proxy = factory.create(new Class<?>[]{}, new Object[]{}, new NullSafeGetHandler(obj));  
           } catch (Throwable e) {  
                e.printStackTrace();  
           }  
           return proxy;  
      }  
      /**  
       * Checks if return type is from the java system package. If so the object returned from the calling  
       * method is returned.  
       *   
       * Else, a null safe proxy of the return type is returned;  
       *   
       * (non-Javadoc)  
       * @see javassist.util.proxy.MethodHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.reflect.Method, java.lang.Object[])  
       */  
      public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {  
           Object returnObject = thisMethod.invoke(source, args);  
           if(thisMethod.getReturnType().getPackage().getName().startsWith("java"))  
                return returnObject;  
           if(returnObject == null)  
                returnObject = thisMethod.getReturnType().newInstance();  
           return getProxy(returnObject);  
      }  
 }  

Thursday, August 11, 2011

Validation horrors! I mean errors!

Problem: Client needs to collect all validation errors from business layer before request completes.

Solution: Pass an error collection as a return object from service methods.

Problem: Error collection prevents more logical objects from being returned, as an error holds no true value in the domain context.

Solution: Create error collection during the client request that is passed as a parameter to service methods.

Problem: Client has to instantiate Errors collection. Also, the client is not forced to handle any broken business rules that may have occurred. Thus rendering validation efforts futile.

Final Solution: Return Errors only from "validate" methods. Have your service methods throw a runtime exception, of type ValidationException. This ValidationException should persist an immutable collection of errors.

Caveat: Every service call will need to individually catch validation exceptions.


class Errors implements iterable {
...

public void evaluate() {
if(this.hasErrors)
throw ValidationException(this);
}
}

class StuffService {
...

Stuff findStuff(StuffCriteria criteria) {
Errors errors = stuffValidator.validate(criteria);

errors.evaluate(); //throws ValidationException if errors present

return stuffDao.findStuff(criteria);
}
}

class StuffClientSearchForm {
...

onSubmit() {
...

try{
stuff1 = stuffService.findStuff(criteria1);
} catch (ValidationException ve) {
for(Error error in ve.getErrors())
addClientError(error.getMessage);
}

try{
stuff2 = stuffService.findStuff(criteria2);
} catch (ValidationException ve) {
for(Error error in ve.getErrors())
addClientError(error.getMessage);
}
...
}
}

Monday, May 9, 2011

Best data type for PRICE!

In attempting to persist U.S. currency formatted values from an H2 database via Hibernate and JPA, I've found using a primitive type of double, a variable precision and a scale of 2 finally gave me what I wanted.

BigDecimal result = new BigDecimal(129.99, 7).setScale(2);

BigDecimal(double value, int precision)