Reduce Barriers to API Adoption

September 9, 2013

What is the one key aspect of making your APIs reusable? Simply put – reducing the barriers to adoption. It matters because your target audience – architects, developers, and project managers – are all trying to deliver value to their clients. Iteration after iteration. Here are specific strategies to reduce the adoption barriers:

  1. Make the public API as intention revealing as possible. Looking at the public interface it must be very clear and unambiguous what the purpose and scope is of the reusable capability.
  2. Provide Builder or abstract factory APIs to stitch together complex object graphs in a simple  fashion. Make it very easy to wire up the default behaviour and provide hooks to replace specific bits of behaviour that is varying. For example Рif saving to a file is a default, provide alternative implementations or an interface hook for your client to inject a custom one.
  3. Make it easy to integrate the capability with other complimentary APIs. These could be in-house, open-source, or vendor APIs – key is – if they are likely to co-exist in a solution, make it simple to wire them together. If your developers use Spring for dependency injection, make your components easy to work with when it. Ditto for in-house frameworks that your teams use heavily.
  4. For reusable assets that perform orchestration logic provide hooks for injecting custom logic before / after your API behaviour This will be useful for initializing / cleanup logic – specially with legacy components that may need a bespoke set of steps to start them or free up resources on shutdown.
  5. Provide Null and/or mocking friendly implementations for testability. Your clients need to test their code via automated tests and so do you as the provider – both these needs are met via testable providers. Make it easy for developers to use them with JUnit and make it hard for them to bypass the public APIs.

Using Spring & Java Annotations to Inject Reusable Capabilities

January 19, 2011

Want to inject reusable functionality but don’t want your consumers to go through complex set of steps to implement? Using annotations, a reusable capability can be injected at runtime – your clients can take advantage of common, reusable capabilities while avoiding the need to implement boiler plate code. Consumers can also be allowed to declaratively specify/alter the reusable asset’s behavior using this approach.

Here is an example using Java and Spring framework to print a test message before invoking the target method.

Declare an annotation – a simple annotation with a boolean flag:

package example.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
	 boolean log() default true;
}

Create a dynamic proxy – all this does is examines the boolean log method from the annotation and prints a test message:

package example.proxy;

import java.lang.reflect.*;
import example.annotation.Loggable;

public class ExampleDynamicProxy implements InvocationHandler {

	private Loggable loggable = null;
	private final Object targetObject;

	public ExampleDynamicProxy(Object target, Loggable loggable) {
		targetObject = target;
		this.loggable = loggable;
	}

	public Object invoke(Object target, Method method, Object[] arguments) throws Throwable {

		Object result = null;
		try {
			if (loggable.log()) {
			  System.out.println("message before invoking method...");
			}
			result = method.invoke(targetObject, arguments);
		} catch (InvocationTargetException ite) {
			throw ite.getTargetException();
		}
		return result;
	}
}

Create a spring bean post-processor – checks for annotation and creates a proxied version of bean:

package example.postprocessor;

import java.lang.reflect.Proxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import example.annotation.Loggable;
import example.proxy.ExampleDynamicProxy;

public class ExampleBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object arg0, String arg1)
			throws BeansException {
		return arg0;
	}

	@Override
	public Object postProcessAfterInitialization(Object arg0, String arg1)
			throws BeansException {

		Class clz = arg0.getClass();
		Class[] interfaces = clz.getInterfaces();

		if (interfaces != null && interfaces.length == 1
                        && _interfaces[0].isAnnotationPresent(Loggable.class)) {

			Class _interface = interfaces[0];
			ClassLoader cl = arg0.getClass().getClassLoader();
			Loggable loggable = (Loggable)_interface.getAnnotation(Loggable.class);
			Object proxy= Proxy.newProxyInstance(cl, new Class[] {_interface}, new ExampleDynamicProxy(arg0,loggable));

                        return proxy;
		}
		return arg0;
	}
}

Annotate an example interface as Loggable – this model can be used to inject lot of other reusable capabilities (error handling, notifications to JMS destinations, or even perform data transformations):

package example.bean;

import example.annotation.Loggable;

@Loggable
public interface Example {
	public String getGreeting();
}

Finally, each consuming interface will have an implementation and has to be defined as a bean along with the post-processor during spring initialization. This is a trivial example but was meant to illustrate the injection of reusable behavior via java annotations.


%d bloggers like this: