Tag Archives: generics

Inferred exceptions in Java

Wow, I can’t believe it’s been 6 weeks since I last blogged. I still need to write up a review on Plumbr, after seeing it in action at Devoxx, but to ease me back into the writing game, here’s a small(-ish) but useful spot of Java.

It’s always nice to borrow and steal concepts and ideas from other languages. Scala’s Option is one idea I really like, so I wrote an implementation in Java. It wraps an object which may or may not be null, and provides some methods to work with in a more kinda-sorta functional way. For example, the isDefined method adds an object-oriented way of checking if a value is null. It is then used in other places, such as the getOrElse method, which basically says “give me what you’re wrapping, or a fallback if it’s null”.

public T getOrElse(T fallback)
{
    return isDefined() ? get() : fallback;
}

In practice, this would replace tradional Java, such as

public void foo()
{
    String s = dao.getValue();
    if (s == null)
    {
        s = "bar";
    }
    System.out.println(s);
}

with the more concise and OO

public void foo()
{
    Option<String> s = dao.getValue();
    System.out.println(s.getOrElse("bar"));
}

However, what if I want to do something other than get a fallback value – say, throw an exception? More to the point, what if I want to throw a specific type of exception – that is, both specific in use and not hard-coded into Option? This requires a spot of cunning, and a splash of type inference.

Because this is Java, we can start with a new factory – ExceptionFactory. This is a basic implementation that only creates exceptions constructed with a message, but you can of course expand the code as required.

public interface ExceptionFactory<E extends Exception>
{
    E create(String message);
}

Notice the <E extends Exception> – this is the key to how this works. Using the factory, we can now add a new method to Option:

public <E extends Exception> T getOrThrow(ExceptionFactory<E> exceptionFactory,
                                          String message) throws E
{
    if (isDefined())
    {
        return get();
    }
    else
    {
        throw exceptionFactory.create(message);
    }
}

Again, notice the throws E – this is inferred from the exception factory.

And that, believe it or not, is 90% of what it takes. The one irritation is the need to have exception factories. If you can stomach this, you’re all set. Let’s define a couple of custom exceptions to see this in action.

public class ExceptionA extends Exception
{
    public ExceptionA(String message)
    {
        super(message);
    }

    public static ExceptionFactory<ExceptionA> factory()
    {
        return new ExceptionFactory<ExceptionA>()
        {
            @Override
            public ExceptionA create(String message)
            {
                return new ExceptionA(message);
            }
        };
    }
}

And the suspiciously similar ExceptionB

public class ExceptionB extends Exception
{
    public ExceptionB(String message)
    {
        super(message);
    }

    public static ExceptionFactory<ExceptionB> factory()
    {
        return new ExceptionFactory<ExceptionB>()
        {
            @Override
            public ExceptionB create(String message)
            {
                return new ExceptionB(message);
            }
        };
    }
}

And finally, throw it all together:

public class GenericExceptionTest
{
    @Test(expected = ExceptionA.class)
    public void exceptionA_throw() throws ExceptionA
    {
        Option.option(null).getOrThrow(ExceptionA.factory(),
                                       "Some message pertinent to the situation");
    }

    @Test
    public void exceptionA_noThrow() throws ExceptionA
    {
        String s = Option.option("foo").getOrThrow(ExceptionA.factory(),
                                                   "Some message pertinent to the situation");
        Assert.assertEquals("foo",
                            s);
    }

    @Test(expected = ExceptionB.class)
    public void exceptionB_throw() throws ExceptionB
    {
        Option.option(null).getOrThrow(ExceptionB.factory(),
                                       "Some message pertinent to the situation");
    }

    @Test
    public void exceptionB_noThrow() throws ExceptionB
    {
        String s = Option.option("foo").getOrThrow(ExceptionB.factory(),
                                                   "Some message pertinent to the situation");
        Assert.assertEquals("foo",
                            s);
    }
}

The important thing to notice, as highlighted in bold above, is the exception declared in the method signature is specific – it’s not a common ancestor (Exception or Throwable). This means you can now use Options in your DAO layer, your service layer, wherever, and throw specific exceptions where and how you need.

Download source: You can get the source code and tests from here – genex

Sidenote
One other interesting thing that came out of writing this was the observation that it’s possible to do this:

public void foo()
{
    throw null;
}

public void bar()
{
    try
    {
        foo();
    }
    catch (NullPointerException e)
    {
        ...
    }
}

It goes without saying that this is not a good idea :)

play & objectify-led – binding uploaded data to application models the lazy way, part 2

In my previous post, I described an approach for binding data from uploaded files into entity instances. One important part was left out from this post – validation.

Since the play framework supports JPA as a persistence mechanism out of the box, and defining a constraint once beats defining it twice, it makes sense to re-use the JPA annotations to provide on-the-fly validation during the binding process.

First up, we need a class that describes the JPA constraints on a field. Keeping it simple, here we have nullable and maximum length along with the name of the field as given in the @be.objectify.led.Property annotation.

public class FieldDescriptor
{
    public String name;
    public boolean nullable = true;
    public int maxLength = -1;
}

Next, we need something that checks the model for @Property-annotated fields. Again, I’m keeping this simple by only looking at @Column annotations, but you can have @MaxSize, @OneToOne, @ManyToOne, @Required, etc. – any possible annotation. In fact, they don’t even need to be JPA annotations if you have some other way of indicating constraints.

public class ConstraintsParser
{
    public static Map<String, FieldDescriptor> parseConstraints(Class clazz)
    {
        Map<String, FieldDescriptor> fieldDescriptors = new HahsMap<String, FieldDescriptor>();

        for (Field field : clazz.getDeclaredFields())
        {
            if (field.isAnnotationPresent(Property.class))
            {
                FieldDescriptor fieldDescriptor = new FieldDescriptor();
                Property ledProperty = field.getAnnotation(Property.class);
                fieldDescriptor.name = ledProperty.value();

                if (field.isAnnotationPresent(Column.class))
                {
                    Column column = field.getAnnotation(Column.class);
                    fieldDescriptor.nullable = column.nullable();
                    fieldDescriptor.maxLength = column.length();
                }               

                fieldDescriptors.put(fieldDescriptor.name, 
                                           fieldDescriptor);
            }
        }

        return fieldDescriptors;
    }
}

In order to validate values during binding, we need to implement a be.objectify.led.factory.ValidationFunction.

    public class MyValidationFunction implements ValidationFunction
    {
        private Map<String, FieldDescriptor> fieldDescriptors;

        public MyValidationFunction(Map<String, FieldDescriptor> fieldDescriptors)
        {
            this.fieldDescriptors = fieldDescriptors;
        }

        public void validate(String propertyName,
                                   String propertyValue) throws ValidationException
        {
            FieldDescriptor fieldDescriptor = fieldDescriptors.get(propertyName);
            if (!fieldDescriptor.nullable && StringUtils.isEmpty(propertyValue))
            {
                throw new ValidationException(propertyName,
                                                          String.format("% is required", propertyName));
            }

            if (fieldDescriptor.maxLength != -1 && propertyValue.length() > fieldDescriptor.maxLength)
            {
                throw new ValidationException(propertyName,
                                                          String.format("%s has a maximum length of [%d] but was [%d]", 
                                                                             fieldDescriptor.maxLength,
                                                                             propertyValue.length()));
        }
    }
}

Finally, going back to the ExcelBinder class from the previous post we change the bind method to add the validation.

    public static <T> List bind(File file,
                                              Class<T> modelType) throws ValidationException
        {
            jxl.Workbook workbook = jxl.Workbook.getWorkbook(file);
            jxl.Sheet sheet = workbook.getSheet(0);
            List<String> headerNames = getHeaderNames(workbook);

            Map<String, FieldDescriptor> fieldDescriptorMap = ConstraintsParser.parseConstraints(modelType);
            ValidationFunction validator = new MyValidationFunction(fieldDescriptorMap);

            List objects = new ArrayList();
            // iterate over each non-header row and make it into an object
            for (int i = 1; i < sheet.getRows(); i++)
            {
                PropertyContext propertyContext = getPropertyContext(sheet,
                                                                                           i,
                                                                                           headerNames);
                PropertySetter propertySetter = new PropertySetter(context);
                T t = modelType.newInstance();
                propertySetter.process(t,
                                               validator);
                objects.add(t);
            }
            return objects;
        }

With this, we've added a generic way of validating the input without having to resort to anything as boring as writing it by hand and it will always be in sync with your model!

Storing multiple object types in Java maps with type safety

Java’s generics have undoubtedly made life easier in some cases by enforcing type safety on collections. One collection that frequently does not benefit from them is maps used in a heterogeneous way – that is, a java.util.Map (or equivalent) that contains several non-compatible values. Assuming the keys are of the same type, it’s still not possible to type the value of the map as anything other than a java.lang.Object – exactly what it would have been prior to generics.

Looking through the Jetbrains documentation on its API for IntelliJ plugin development, it’s interesting to note their workaround for enforcing type safety in a map containing heterogeneous values:

  • DataKey<T> – the access point to the map
  • DataContext – an interface that can be used to wrap a map

This approach externalises the type-safety of the map’s content by shifting the casts to DataKey.

The interesting part of DataKey, from this viewpoint, is

@Nullable
public T getData(@NotNull DataContext dataContext)
{
    return (T) dataContext.getData(myName);
}

Read access is via the key, and not directly on the map itself, e.g. for a key

DataKey<Foo> FOO = DataKey.create("foo")

a type-safe call without a cast can be made on the wrapped via:

Foo foo = MyKeys.FOO.getData(dataContext);

The map-based implementations of DataContext just does a regular lookup with the key provided by DataKey:

public class MyDataContext implements DataContext
{
    private final Map<String, ?> data = new HashMap<String, ?>();
    @Nullable Object getData(@NonNls String dataId)
    {
        return data.get(dataId);
    }
}

and the returned object is cast to the correct type on the way out of DataKey. Simple and elegant.

It’s worth noting that both DataKey and DataContext are read-only in function – there’s a getData(), but no setData().   It’s easy enough to extend the concept to make sure that what goes into the map is of the same type as what comes out by adding a couple of extra methods, one on DataKey:

    public void setData(DataContext dataContext, T value)
    {
        dataContext.setData(getName(), value);
    }

and another on DataContext:

    public void setData(String key, Object value)
    {
        data.put(key, value);
    }

I’ve seen a lot of code where keys to access values from maps have been declared as static final Strings. Using DataKey, you could replace these declarations with DataKeys that specify the key to access the value and the type of the value itself, so

public static final String USER = "user";
...
User user = (User)map.get(USER);

would become

public static final DataKey<User> USER = DataKey.create("user");
...
User user = USER.getData(dataContext);