Archive

Posts Tagged ‘patterns’

Service Selector

November 2nd, 2009

Sometimes we may have more than one implementation and/or instance of a service to which we need to route requests. Routing may be controlled by a number of different factors, such as the request type, request arguments, runtime configuration, etc.

An implementation of such routing might look something like this:

public interface SomeService {
  void someMethod();
}

public class RoutingSomeService implements SomeService {

  private Map<String, SomeService> delegates = ...

  private String activeDelegateId = ...

  public void someMethod() {
    SomeService delegate = delegates.get(activeDelegateId);
    if (delegate != null) {
      delegate.someMethod();
    }
    else {
      // XXX: throw runtime exception???
    }
  }
}

The common elements here are:

  • A collection of delegate services
  • A mechanism (e.g. key) for identifying the appropriate delegate for a request

Using interfaces we can create a pattern for supporting different types of service selection:

public interface ServiceSelector<T> {

  T getService(Method method, Object[] args);
}

/**
 * A ServiceSelector that routes requests to the active service specified in an external configuration.
 */
public class ConfigurableServiceSelector implements ServiceSelector<SomeService> {

  private final Map<String, SomeService> services = ...

  private final Properties configuration = ...

  public SomeService getService(Method method, Object[] args) {
    return services.get(configuration.getProperty("someService.activeId"));
  }
}

/**
 * A ServiceSelector that supports routing of different methods based on an external configuration.
 */
public class MethodServiceSelector implements ServiceSelector<SomeService> {

  private final Map<Method, SomeService> services = ...

  public SomeService getService(Method method, Object[] args) {
    return services.get(method);
  }
}

...

So our implementation might then look like this:

public class RoutingSomeService implements SomeService {

  private ServiceSelector<SomeService> selector = ...

  public void someMethod() {
    SomeService delegate = selector.getService(getClass().getMethod("someMethod"), new Object[] {});
    if (delegate != null) {
      delegate.someMethod();
    }
    else {
      // XXX: throw runtime exception???
    }
}

As you can see this is actually quite an ugly piece of code. However, we can avoid writing this code altogether with the help of Java’s proxy support.

Ergo Proxy

The use of proxies allows us to avoid writing the boilerplate code for service delegation:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ServiceInvocationHandler implements InvocationHandler {

    private final ServiceSelector<?> selector;

    public ServiceInvocationHandler(ServiceSelector<?> selector) {
        this.selector = selector;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(selector.getService(method, args), args);
    }
}

public class ServiceConsumer {

  private final SomeService service;

  public ServiceConsumer(SomeService service) {
    this.service = service;
  }

  public static ServiceConsumer newInstance(SomeService service) {
    return new ServiceConsumer(service);
  }

  public static ServiceConsumer newInstance(ServiceSelector<SomeService> selector) {
    final InvocationHandler invocationHandler = new ServiceInvocationHandler(selector);
    return new ServiceConsumer((SomeService) Proxy.newProxyInstance(SomeService.class.getClassLoader(), new Class<?>[] {SomeService.class}, invocationHandler);
  }
}

Conclusion

Defining a standard interface for routing service requests provides us with a consistent and re-usable way of managing routing rules. Using proxies also ensures that service contracts are maintained and our client code doesn’t need to change.

Java ,

Uniform Caching

October 13th, 2009

Typically object caching in Java is managed by the container or framework in use. Occasionally however there is a need to manually cache domain-specific objects, whereby a java.util.Map implementation will not suffice.

Using the popular ehcache framework as an example, the following pattern is typically observed:

public class SomeClass {

  private final Cache cache = ...

  ...

  public void doSomethingWithObject(Object key) {
    SomeObject o = getSomeObject(key);
    o.doSomething();
  }

  public SomeObject getSomeObject(Object key) {
    SomeObject o = null;

    Element element = cache.get(key);
    if (element != null) {
      o = element.getValue();
    }
    else {
      o = load(key);
      cache.put(new Element(key, o));
    }
    return o;
  }

  private Object load(Object key) {
    ...
  }
}

The common aspects of this pattern are as follows:

  • Cache – the cache instance
  • Key – the unique key of the cachable object
  • load() – the mechanism for loading objects not in the cache

Key Uniformity

As most caching frameworks will allow any object to be used as a key, there is potential for different types of errors, such as a value specified as a key, mixing object types in a single cache, added to the wrong cache instance, etc. We can avoid some of these problems by enforcing a uniform approach to defining cache keys:

public enum CacheEntry {

  SomeObject("org.mnode.example.someObject.%s");

  private String key;

  public String getKey(Object uid) {
    return String.format(key, uid);
  }
}

public class SomeClass {
  ...

  public SomeObject getSomeObject(Object uid) {
    SomeObject o = null;
    String key = CacheEntry.SomeObject.getKey(uid);
    ...
  }
}

As this approach enforces a key ‘namespace’ for specific object types, it also makes it easier to store mixed data in a single cache, thus simplifying the management of cached objects:

public class SomeClass {
  ...

  public <T> T get(CacheEntry entry, Object uid) {
    T o = null;
    String key = entry.getKey(uid);

    Element element = cache.get(key);
    if (element != null) {
      o = (T) element.getValue();
    }
    else {
      o = (T) load(key);
      cache.put(new Element(key, o));
    }
    return o;
  }
}

Object Loading

Different types of cached data will also usually require specific code for loading the data initially. We can refactor this to be defined as part of the CacheEntry:

interface Loader<T> {
  T load(Object...args);
}

public enum CacheEntry {

  SomeObject("org.mnode.example.someObject.%s", new Loader<SomeObject> {
    SomeObject load(Object...args) {
      Object uid = args[0];
      // load data from backing store..
      ...
    }
  });

  private String key;

  private Loader<?> loader;

  public String getKey(Object...args) {
    return String.format(key, args);
  }

  public Object load(Object...args) {
    loader.load(args);
  }
}

Using this combined object loader and key namespace support we can extract the caching logic to a generic adapter:

public class CacheAdapter {

  private final Cache cache;

  public CacheAdapter(Cache cache) {
    this.cache = cache;
  }

  public <T> T get(CacheEntry entry, Object...args) {
    T o = null;
    String key = entry.getKey(args);

    Element element = cache.get(key);
    if (element != null) {
      o = (T) element.getValue();
    }
    else {
      o = (T) entry.load(args);
      if (o != null) {
        cache.put(new Element(key, o));
      }
    }
    return o;
  }
}

public class SomeClass {

  private final CacheAdapter cache = ...

  public void doSomethingWithObject(Object uid) {
    SomeObject o = cache.get(CacheEntry.SomeObject, uid);
    o.doSomething();
  }

  public SomeObject getSomeObject(Object uid) {
    return cache.get(CacheEntry.SomeObject, uid);
  }
}

A Real Example

Caching XMPP VCard objects:

import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.packet.VCard;

public enum CacheEntry {

  VCard("vcard.%s", new Loader<VCard> {
    VCard load(Object...args) {
      String user = (String) args[0];
      XMPPConnection connection = (XMPPConnection) args[1];
      try {
        VCard card = new VCard();
        card.load(connection, user);
      } catch (XMPPException e) {
        return null;
      }
    }
  });
}

public class AvatarRepository {

  private final CacheAdapater vcardCache = ...

  private final XMPPConnection connection = ...

  public Image getAvatar(String user) {
    Image avatar = null;

    VCard vcard = vcardCache.get(CacheEntry.VCard, user, connection);
    if (vcard != null) {
      avatar = new ImageIcon(vcard.getAvatar()).getImage();
    }

    return avatar;
  }
}

Conclusion

By defining a key namespace and object loading mechanism for cachable data types we can improve the manageability of object caching in the following ways:

  • Improved support for mixed data type caching
  • Increased decoupling from the caching implementation
  • Uniformity in object loading and caching

Java , ,

Whiteboard Registry

October 8th, 2009

In OSGi using a publisher/subscriber design can be somewhat more complicated that traditional Java environments:

public class SomeBundleActivator implements BundleActivator {

  private SomeService service = ...

  private ServiceRegistration registration;

  public void start(BundleContext context) {
    registration = context.registerService(SomeService.class.getName(), service, null);
  }
  ...
}

public class AnotherBundleActivator implements BundleActivator {

  private SomeServiceSubscriber subscriber = ...

  public void start(BundleContext context) {
    ServiceLocator serviceLocator = new OsgiServiceLocator(context);
    SomeService service = serviceLocator.findService(ServiceName.SomeService);

    // XXX: what if service is not initialised here??
    service.subscribe(subscriber);

    // XXX: what if service is removed and/or restarted after here???
  }
  ...
}

To overcome these hurdles the Whiteboard Pattern prescribes registering listeners in the service registry as opposed to services, whereby services can publish events to available listeners at the time of the event.

public class SomeBundleActivator implements BundleActivator {

  private SomeService service = ...

  public void start(BundleContext context) {
    ServiceLocator serviceLocator = new OsgiServiceLocator(context);

    service.subscribe(new ServiceSubscriber() {
      public void onEvent(Event e) {
        List<ServiceSubsriber> subscribers = serviceLocator.findServices(ServiceName.ServiceSubscriber);
        for (ServiceSubscriber subscriber : subscribers) {
          subsriber.onEvent(e);
        }
      }
    });
  }
  ...
}

public class AnotherBundleActivator implements BundleActivator {

  private SomeServiceSubscriber subscriber = ...

  private ServiceRegistration registration;

  public void start(BundleContext context) {
    registration = context.registerService(ServiceSubscriber.class.getName(), subscriber, null);
  }
  ...
}

The problem with the Whiteboard Pattern is that it relies on the OSGi Service Registry to maintain the list of active subscribers. This means that publishers (i.e. services) must be either OSGi-aware, or events must be specifically handled and repeated to available subscribers.

The Whiteboard Registry

We can extend the Whiteboard Pattern further by creating a dedicated Registry that is responsible for wiring together specific publishers and subscribers:

public class SomeServiceSubscriberRegistry {

  private final List<SomeService> publishers;

  private final List<SomeServiceSubscriber> subscribers;
  ...
  public void registerPublisher(SomeService publisher) {
    for (SomeServiceSubscriber subscriber : subscribers) {
      publisher.subscribe(subscriber);
    }
  }

  public void unregisterPublisher(SomeService publisher) {
    ...
  }

  public void registerSubscriber(SomeServiceSubscriber subscriber) {
    for (SomeService publisher : publishers) {
      publisher.subscribe(subscriber);
    }
  }

  public void unregisterSubscriber(SomeServiceSubscriber subscriber) {
    ...
  }
}

Using this registry publishers and subscribers are wired together as they are made available:

public class WhiteboardRegistryBundleActivator implements BundleActivator {

  private final SomeServiceSubscriberRegistry registry = ...

  public void start(BundleContext context) {
    context.addServiceListener(new ServiceListener() {
      public void serviceChanged(ServiceEvent e) {
        if (e.getType() == ServiceEvent.REGISTERED) {
          registry.registerPublisher((SomeService) context.getService(e.getServiceReference()));
        }
        else if (e.getType() == ServiceEvent.UNREGISTERING) {
          registry.unregisterPublisher((SomeService) context.getService(e.getServiceReference()));
        }
      }
    }, "(objectClass=" + SomeService.class.getName + ")");

    context.addServiceListener(new ServiceListener() {
      public void serviceChanged(ServiceEvent e) {
        if (e.getType() == ServiceEvent.REGISTERED) {
          registry.registerSubscriber((ServiceSubscriber) context.getService(e.getServiceReference()));
        }
        else if (e.getType() == ServiceEvent.UNREGISTERING) {
          registry.unregisterSubscriber((ServiceSubscriber) context.getService(e.getServiceReference()));
        }
      }
    }, "(objectClass=" + ServiceSubscriber.class.getName + ")");
  }
  ...
}

public class SomeBundleActivator implements BundleActivator {

  private SomeService service = ...

  private ServiceRegistration registration;

  public void start(BundleContext context) {
    registration = context.registerService(SomeService.class.getName(), service, null);
  }
  ...
}

public class AnotherBundleActivator implements BundleActivator {

  private SomeServiceSubscriber subscriber = ...

  private ServiceRegistration registration;

  public void start(BundleContext context) {
    registration = context.registerService(ServiceSubscriber.class.getName(), subscriber, null);
  }
  ...
}

The wiring can be made even simpler by using a Dependency Injection framework such as Spring DM:

  ...
    <bean id="SomeServiceSubscriberRegistry" class="org.mnode.example.whiteboard.SomeServiceSubscriberRegistry"/>

    <osgi:reference id="SomeServiceWiring" interface="org.mnode.example.whiteboard.SomeService">
      <osgi:listener ref="SomeServiceSubscriberRegistry" bind-method="registerPublisher" unbind-method="unregisterPublisher" />

    <osgi:reference id="ServiceSubscriberWiring" interface="org.mnode.example.whiteboard.ServiceSubscriber">
      <osgi:listener ref="SomeServiceSubscriberRegistry" bind-method="registerSubscriber" unbind-method="unregisterSubscriber" />
  ...

Conclusion

By creating a Whiteboard Registry that is dedicated to wiring services and service subscribers, we can bring the benefits of the Whiteboard Pattern to event listener models and other publisher/subscriber frameworks that are not OSGi-aware.

Java , , ,

OSGi Service Locator

September 30th, 2009

The Service Locator pattern is a well-established mechanism for accessing local and remote services in a consistent manner:

public interface ServiceLocator {

    <T> T findService(String serviceName) throws ServiceNotAvailableException;
}

Using a structured service name interface we can improve uniformity and reduce the potential for typos:

public enum ServiceName {

  SomeService("SomeService");

  private final String filter;
  ...

  /**
    * @return a filter string used to identify the service classification/location.
    */
  String getFilter();
}

public interface ServiceLocator {

    <T> T findService(ServiceName serviceName) throws ServiceNotAvailableException;
}

In an OSGi environment, the recommended approach for retrieving services is via the org.osgi.util.tracker.ServiceTracker class:

...
  BundleContext context = ...
  ServiceTracker tracker = new ServiceTracker(context, SomeService.class.getName(), null);
  tracker.open();

  SomeService service = (SomeService) tracker.getService();
...

We can combine these two patterns to provide a more familiar and manageable approach to locating services:

import java.util.HashMap;
import java.util.Map;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

public class OsgiServiceLocator implements ServiceLocator {

    private final BundleContext context;

    private final Map<ServiceName, ServiceTracker> serviceTrackers;

    /**
     * @param context the bundle context in which to find services
     */
    public OsgiServiceLocator(BundleContext context) {
        this.context = context;
        serviceTrackers = new HashMap<ServiceName, ServiceTracker>();
    }

    @SuppressWarnings("unchecked")
    public <T> T findService(ServiceName serviceName) throws ServiceNotAvailableException {
        ServiceTracker tracker = serviceTrackers.get(serviceName);
        if (tracker == null) {
            synchronized (serviceTrackers) {
                tracker = serviceTrackers.get(serviceName);
                if (tracker == null) {
                    tracker = new ServiceTracker(context, context.createFilter(serviceName.getFilter()), null);
                    tracker.open();
                    serviceTrackers.put(serviceName, tracker);
                }
            }
        }
        final T service = (T) tracker.getService();
        if (service == null) {
            throw new ServiceNotAvailableException("Service matching [" + serviceName.getFilter() + "] not found.");
        }
        return service;
    }

    /**
     * Clean up resources.
     */
    public void reset() {
        for (ServiceTracker tracker : serviceTrackers.values()) {
            tracker.close();
        }
        serviceTrackers.clear();
    }
}

An example usage might be something like this:

import org.osgi.framework.Constants;

public enum ServiceName {

  SomeService("(" + Constants.OBJECTCLASS + "=" + SomeService.class.toString() + ")");
  ...
}

...
  BundleContext context = ...
  ServiceLocator serviceLocator = new OsgiServiceLocator(context);

  SomeService service = serviceLocator.findService(ServiceName.SomeService);
...

Conclusion

By implementing the Service Locator pattern in an OSGi context we provide consistency and familiarity for code that is not OSGi-aware. This reduces the dependency on OSGi and improves the maintainability of our code.

Java , ,

Uniform Logging

September 25th, 2009

Application logging always seems to become one of those code smells, typically regarding duplication of code, or conversely, non-uniform log messages.

There are many different ways to log a message in Java, but variations on the following pattern are common:

public class SomeClass {

  private static final Log LOG = LogFactory.getLog(SomeClass.class);

  ...

  public void someMethod() {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Some message - someObject is: " + someObject);
    }

    try {
      ...
    } catch (SomeException e) {
      LOG.error("Unexpected error: " + e.getMessage(), e);
    }
  }
}

The following information can be extracted from this pattern:

  • Category – classification for log entries
  • Level – the severity of a log entry
  • Message – a log entry message
  • Message arguments – variable components of a log message
  • Exception – an exception for logging a stack trace

Message Uniformity

One problem with this pattern is that we tend to duplicate the same message strings when logging similar scenarios (e.g. unexpected exceptions).

Messages are also generally constructed by concatenating messages and message arguments – a practice that generally should be avoided if possible. We can solve these issues using message templates:

import java.text.MessageFormat;

public class SomeClass {

  private static final String UNEXPECTED_ERROR_MESSAGE = "Unexpected error: {0}";

  ...

  public void someMethod() {

    try {
      ...
    } catch (SomeException e) {
      LOG.error(MessageFormat.format(UNEXPECTED_ERROR_MESSAGE, e.getMessage()), e);
    }
  }
}

Inconsistent log messages also result from having the message strings defined across multiple classes. Using message templates we can refactor these messages to be defined in a single location:

public enum LogEntry {
  UnexpectedError("Unexpected Error: {0}");

  private final String message;

  public String getMessage(Object...args) {
    return MessageFormat.format(message, args);
  }
}

public class SomeClass {

  ...

  public void someMethod() {

    try {
      ...
    } catch (SomeException e) {
      LOG.error(LogEntry.UnexpectedError.getMessage(e.getMessage()), e);
    }
  }
}

Log Levels

In the majority of cases, log entries that share the same message will also be logged at the same level. By associating a default log level with a log entry we can enforce uniformity of log levels:

public enum LogLevel {
  Trace, Debug, Info, Warn, Error;
}

public enum LogEntry {
  UnexpectedError("Unexpected Error: {0}", LogLevel.Error);

  ...

  private final LogLevel level;

  public LogLevel getLevel() {
    return level;
  }
}

public class LogAdapter {
  private final Log log;

  ...

  public void log(LogEntry entry, Object...args) {
    if (entry.getLevel() == LogLevel.Error) {
      log.error(entry.getMessage(args));
    }
    else if (entry.getLevel() == LogLevel.Warn) {
      log.warn(entry.getMessage(args));
    }
    else ...
  }

  public void log(LogEntry entry, Throwable e, Object...args) {
    if (entry.getLevel() == LogLevel.Error) {
      log.error(entry.getMessage(args), e);
    }
    else if (entry.getLevel() == LogLevel.Warn) {
      log.warn(entry.getMessage(args), e);
    }
    else ...
  }
}

public class SomeClass {

  private static final LogAdapater LOG = new LogAdapter(LogFactory.getLog(SomeClass.class));
  ...

  public void someMethod() {

    try {
      ...
    } catch (SomeException e) {
      LOG.log(LogEntry.UnexpectedError, e, e.getMessage());
    }
  }
}

To avoid the expensive construction of frequently logged message strings we use conditionals to check if a log level is enabled prior to message construction:

  ...
  if (LOG.isDebugEnabled()) {
    LOG.debug("Some message - someObject status is: " + someObject.expensiveMethod());
  }
  ...

Such conditionals are prone to error however, especially if the log level is changed:

  ...
  if (LOG.isDebugEnabled()) {
    LOG.warn("Some message - someObject status is: " + someObject.expensiveMethod());
  }
  ...

Uniform logging can help to avoid such mistakes:

public enum LogEntry {
  SomeObjectStatus("Some message - someObject status is: {0}", LogLevel.Debug);
  ...
}

public class LogAdapter {
  ...

  public boolean isLoggable(LogEntry entry) {
    if (entry.getLevel() == LogLevel.Debug) {
      return log.isDebugEnabled();
    }
    ...
  }

  public void log(LogEntry entry, Object...args) {
    if (isLoggable(entry) {
      ...
    }
  }

  public void log(LogEntry entry, Throwable e, Object...args) {
    if (isLoggable(entry) {
      ...
    }
  }
}

public class SomeClass {
  ...

  public void someMethod() {
    if (LOG.isLoggable(SomeObjectStatus) {
      LOG.log(SomeObjectStatus, someObject.expensiveMethod());
    }
  }
}

Note that a level check conditional is only required whereby an expensive method must be called to retrieve message arguments. The expense of the message string construction is handled by the LogAdapter.

Conclusion

Logging can be a repetitive, expensive and often error prone exercise. By centralising the log entries we reduce code duplication and potential for bugs through uniformity and re-use.

Java , ,