1

I am running into a strange problem. I have an interface, whose implementations tend to be stateless. So, I want them to be singletons.

I get the implementation class names as strings. For example

String clazz = "com.foo.Bar";

I have a rules factory to obtain instances of IRule implementations.

public class RulesFactory {

    private static final Logger logger = LoggerFactory.getLogger(RulesFactory.class);

    @SuppressWarnings("unchecked")
    public static <T extends IRule> T getRuleInstance(String clazz) {
        try {
            Class<?> ruleObject = Class.forName(clazz);
            Method factoryMethod = ruleObject.getMethod("getInstance");
            return (T) factoryMethod.invoke(null);
        } catch (ClassNotFoundException e) {
            logger.error("ClassNotFoundException", e);
        } catch (IllegalAccessException e) {
            logger.error("IllegalAccessException", e);
        } catch (SecurityException e) {
            logger.error("SecurityException", e);
        } catch (NoSuchMethodException e) {
            logger.error("NoSuchMethodException", e);
        } catch (IllegalArgumentException e) {
            logger.error("IllegalArgumentException", e);
        } catch (InvocationTargetException e) {
            logger.error("InvocationTargetException", e);
        }
        return null;
    }
}

The above code throws NullPointerException if the class doesn't have a static getInstance() method. In Java 6 i can't use static methods in interfaces. I don't want to create multiple instances of IRule implementations. If I can enforce a static method and invoke that static method I will get the cashed instance. But I am unable to do this. How to solve this problem?

7
  • Another idea: have the implementations with constructors package protected. Have them in the same package as RulesFactory, then based on clazz create with "new" the implementation and store it in a map by class name. Always check the map before creating new instance. Commented Dec 4, 2014 at 7:44
  • Null is a valid value to pass to invoke if the method is static. So check your getInstance() methods. Commented Dec 4, 2014 at 7:45
  • @Kayaman Yes. It is valid. But how to enforce getInstance to be implemented by clients and that too as static method? Commented Dec 4, 2014 at 7:46
  • 2
    Enforce it with documentation and good programming practices. Commented Dec 4, 2014 at 7:48
  • 1
    @BRS seeing your reply to Kayaman I feel you should edit your question to put that as your concern and not the NPE. Commented Dec 4, 2014 at 7:49

2 Answers 2

1

There are several solutions with different pros and cons:

  1. Don't use static methods. If the method isn't static, you can add it to IRule and therefore enforce that the method exists.
  2. Check the qualifiers of factoryMethod and throw a descriptive exception when they aren't static

For solution #1, you need a Map<String,IRule>. When getRuleInstance() is called, check the map for an instance. If there is none, use the method from the interface to create one and put it into the map. This way, you can make the instances singletons.

At the same time, you can get all fields of the instance and make sure all of them are final to enforce the statelessness.

If your application is multi-threaded, make sure you use a concurrent map and synchronize properly.

Example code:

private Map<String, IRule> rules = Maps.newHashMap();

public static <T extends IRule> T getRuleInstance(String clazz) {
    try {
        synchronized( rules ) {
            IRule result = rules.get(clazz);
            if(null == result) {
                result = clazz.newInstance();
                rules.put(clazz, result);
            }
            @SuppressWarnings("unchecked")
            T tmp = (T) result;
            return tmp;
        }
    } catch (Exception e) {
        log( "Unable to create IRule for {}", clazz );
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

I think, it has become a 'chicken and egg' issue with choice one. Unless I have an instance I can't call invoke method, and then there is no point in calling getInstance(). Any ideas on how to resolve this?
Actually I am trying to instantiate the implementation using the Bill Pugh pattern. So, it is creating trouble that two instance are created. One for calling invoke and the other by calling getInstance().
for #1, call ruleObject.newInstance(). Actually, you should rename ruleObject to ruleType.
But, I am not using the interface right? ): Is there a solution?
There are two solutions: 1. You can use a new interface IRuleFactory which creates instances of IRule. This way, you can mix stateful and stateless rules (factories can either return the same instance or create new ones). 2. Instead of calling getInstance(), you create the instance in getRuleInstance() and put it in the map.
1

You are making your life unnecessary hard.

If you remember the great “enum singleton pattern” and require that all implementors use it, e.g.

public enum Foo implements IRule
{
  INSTANCE;

  // IRule implementation methods here

  public String toString() { return "the sole Foo instance"; }
}

the entire RulesFactory becomes as simple as:

private static final ConcurrentMap<String, IRule> instancesMap
                                             = new ConcurrentHashMap<String, IRule>();

public static IRule getRuleInstance(String clazz) {
  try {
      IRule iRuleInstance=instancesMap.get(clazz);
      if(iRuleInstance!=null) return iRuleInstance;
      Class<? extends IRule> ruleObject=Class.forName(clazz).asSubclass(IRule.class);
      IRule[] enumConstants=ruleObject.getEnumConstants();
      if(enumConstants==null || enumConstants.length!=1) {
        logger.error("InvalidClassException",
                     new InvalidClassException(clazz, "not a singleton enum"));
        return null;
      }
      iRuleInstance=enumConstants[0];
      instancesMap.put(clazz, iRuleInstance);
      return iRuleInstance;
    } catch (ClassNotFoundException e) {
        logger.error("ClassNotFoundException", e);
    }
    return null;
}

The great thing about the “enum singleton pattern” is that it already guarantees the singleton property, thus the code above doesn’t make any attempt to detect concurrent lookups as they are guaranteed to return the same single instance.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.