views:

130

answers:

2

I'm fairly new to EJBs and full blown application servers like JBoss, having written and worked with special purpose standalone Java applications for most of my career, with limited use of JEE. I'm wondering about the best way to adapt a commonly used design pattern to EJB3 and JBoss: the static factory pattern. In fact this is Item #1 in Joshua Bloch's Effective Java book (2nd edition)

I'm currently working with the following factory:

public class CredentialsProcessorFactory {
    private static final Log log = LogFactory.getLog(CredentialsProcessorFactory.class);
    private static Map<CredentialsType, CredentialsProcessor> PROCESSORS = 
        new HashMap<CredentialsType, CredentialsProcessor>();

    static {
        PROCESSORS.put(CredentialsType.CSV, new CSVCredentialsProcessor());
    }

    private CredentialsProcessorFactory() {}

    public static CredentialsProcessor getProcessor(CredentialsType type) {
       CredentialsProcessor p = PROCESSORS.get(type);
       if(p == null)
           throw new IllegalArgumentException("No CredentialsProcessor registered for type " + type.toString());
       return p;
}

However, in the implementation classes of CredentialsProcessor, I require injected resources such as a PersistenceContext, so I have made the CredentialsProcessor interface a @Local interface, and each of the impl's marked with @Stateless. Now I can look them up in JNDI and use the injected resources.

But now I have a disconnect because I am not using the factory anymore. My first thought was to change the getProcessor(CredentialsType) method to do a JNDI lookup and return the SLSB instance that is required, but then I need to configure and pass the proper qualified JNDI name. Before I go down that path, I wanted to do more research on accepted practices.

How is this design pattern treated in EJB3 / JEE?-

A: 

With EJB3, you generally don't need to do JNDI lookups. EJB3 supports dependency injection of EJBs and several other types of resources. If your bean attribute is annotated with @EJB, the dependency will automatically be injected by the container.

Not having to do a JNDI lookup means you can test your EJB like a POJO for unit testing purposes. You can just manually inject mock implementations of dependencies and test them without having to deploy them in a container.

Ken Liu
I've been reading that in the EJB spec. How would you go about that with the factory class I have above? First, the factory would have to be an EJB so that annotations got intercepted right? Then how would I make the choice of different implementations based on an enum, or a string, like above?
purecharger
A: 

When you start playing with factories and "real" Java POJO code, you basically need rely on JNDI.

The dependency injection only works on the managed components of an EJB server, and that's basically Servlets and EJBs.

When you're talking generic java code that wants to refer to EJBs, they need to lookup the resources themselves via JNDI.

The best thing to do, in that case, is simply write a wrapper lookup class filled with static functions to do your JNDI lookups, rather than calling JNDI directly in each implementation. And then use that in your implementations.

That's just a generic overall rule.

Now, for your specific case, consider this.

You have:

static {
    PROCESSORS.put(CredentialsType.CSV, new CSVCredentialsProcessor());
}

That's no different from:

static {
    PROCESSORS.put(CredentialsType.CSV, "java:comp/env/ejb/CSVCredentialProcessorSessionBean");
}

Then, in your getProcessor() code:

Context c = new InitialContext();
return (CredentialsProcessor) c.lookup(PROCESSORS.get(type));

See, basically, the code is identical, and your factory interface is the same to the clients.

You have to "hard code" the JNDI lookup key, but you're "hard coding" the class names now anyway, so how is this different?

There's some potential portability concerns across containers, in that everyone seems to like to use different JNDI identifiers for bean names. Most of that can be tweaked in the deployment, but if not, then you can pull these keys out in to a config file, or whatever.

In JEE 6, there are guaranteed portable names. If you're not porting containers today, then don't worry about this at all.

So, basically, it's not really a disconnect at all.

Will Hartung