I'm a Java software developer, consultant and architect that focuses on enterprise applications, quality and performance. I gained interest in Java due to it's open nature and community support. Next to Java, I spend most of my time trying to stay up to date with everything that moves inside the software world like Scala and NoSQL db's. Jelle is a DZone MVB and is not an employee of DZone and has posted 12 posts at DZone. You can read more from them at their website. View Full User Profile

Java EE6 Alternatives, A flexible configuration for enterprise applications

06.21.2011
| 4926 views |
  • submit to reddit

Another great feature of java ee 6 is the use of alternatives. The basic idea is to let you specify an alternative for an injected object. Take this case for example.
We are developing our front-end of our application. The screens depend on a database connection, but the database isn’t ready for development. We provide an ‘alternative’ implementation for this time being.
You can specify a mock and inject that. With this approach you need to change the wiring of your class.

public interface CustomerService {
    List findAllCustomers();
}

Your implementation would look something like this.

public class CustomerServiceImpl implements CustomerService {
 
    @PersistenceContext
    private EntityManager em;
 
    @Override
    public List findAllCustomers(){
        Query query = em.createNamedQuery("Customer.findAll", Customer.class);
        return query.getResultList();
    }
}

And the mock implementation like this.

public class CustomerServiceMock implements CustomerService {
 
    @Override
    public List findAllCustomers() {
        return Arrays.asList(new Customer("Geraldo"), new Customer("Scruffy"), new Customer("Haddock"));
    }
 
}

In your class that is using this service, you can just inject it.

@Inject private CustomerService customerService;

When you try to run this, it will give an error:

‘Ambiguous dependencies for type [CustomerService] with qualifiers [@Default] at injection point [[field] @Inject private be.styledideas.blog.alternative.web.CustomerBackingBean.customerService]. Possible dependencies [[Managed Bean [class be.styledideas.blog.alternative.CustomerServiceMock] with qualifiers [@Any @Default], Managed Bean [class be.styledideas.blog.alternative.CustomerServiceImpl] with qualifiers [@Any @Default]]]. Please see server.log for more details.’

The container does not know what implementation should be injected. We will have to mark the mock implementation. We do this by adding the @Alternative annotation on top of it.

@Alternative
public class CustomerServiceMock implements CustomerService {
 
    @Override
    public List findAllCustomers() {
        return Arrays.asList(new Customer("Haddock"), new Customer("Geraldo"), new Customer("Scruffy"));
    }
}

When we now start our server, there is only one implementation that qualifies for this injection point, i.e. the CustomerServiceImpl class.
This isn’t what we want to achieve, we want to be able to inject our mock for the time we cannot connect to the database.
In our beans.xml file (CDI standard) we add this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    <alternatives>
        <class>be.styledideas.blog.alternative.CustomerServiceMock</class>
    </alternatives>
</beans>

Whenever we run our code, we will have a mock implementation where we use the customer service. Due this is configured in an xml file, we can change this without having to do a recompile of our application.
Next up, decorators.

 

From http://styledideas.be/blog/2011/06/07/java-ee6-alternatives-a-flexible-configuration-for-enterprise-applications/

Published at DZone with permission of Jelle Victoor, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Nicolas Frankel replied on Tue, 2011/06/21 - 3:21am

Hi Jelle,

Thanks for this informative post. I've a question: you state that you don't need to recompile the code to use an alternative (or not) but you still have to repackage since the beans.xml file is inside the archive, right?

In this case, what's the advantage of using CDI?

Jelle Victoor replied on Tue, 2011/06/21 - 4:48am in response to: Nicolas Frankel

You don't necessarily have to repackage your project. You can configure your application server to point to the correct beans.xml outside of your application. This way you can adjust the content of the beans.xml, without a repackage.

Nicolas Frankel replied on Tue, 2011/06/21 - 5:52am in response to: Jelle Victoor

That seems interesting because I thought, perhaps wrongly, that the beans.xml had to be part of the application. Can you point me to the specific paragraph of the specs that describes such an externalization? Thanks.

Jelle Victoor replied on Tue, 2011/06/21 - 5:59am

If you can adjust your classpath, you can let your classpath point to a different META-INF directory. see http://download.oracle.com/javaee/6/tutorial/doc/gjbnz.html Haven't got time to test this, but seems to be pretty easy to configure.

Nicolas Frankel replied on Tue, 2011/06/21 - 6:56am in response to: Jelle Victoor

You got me lost: the link you provide is the general CDI documentation. What you provide is a hack (and a dirty one) that infers not only that the developer has the rights to change the application server configuration but also that changing the classpath of a deployed application is desirable.

I'm sorry but CDI alternatives do not offer the agility I'm looking for during my development process.

Wujek Srujek replied on Tue, 2011/06/21 - 7:06am

Your beans.xml must be a part of your jar with CDI beans. There may be multiple such jars (in our case: a war, ejb-jar, multiple jars with CDI beans), so multiple beans.xml. How on earth are you planning to change your beans.xml just once? You can (if at all, I am not so sure) do it for a toy application with only one such CDI jar. Anything above 'Hello, World' with CDI _does not work this way_. You do have to repackage the project. It is easier then recompiling it - beans.xml is just a text file, but still, you have to interfere with the app.

Josh Marotti replied on Tue, 2011/06/21 - 9:06am

How is this different than spring?  Even better, we don't need to annotate any alternatives in spring, just inject the impl we want.  I don't see how this is a 'great thing'?

Wujek Srujek replied on Tue, 2011/06/21 - 10:05am

Me neither. And the 'workarounds' are not even viable.

Rogerio Liesenfeld replied on Tue, 2011/06/21 - 4:02pm

I agree that the selection of "alternatives" in Java EE 6 is great, but this is a bad example, which abuses DI.

If the only "alternative implementation" for CustomerService is a *mock* implementation, then use of a mocking tool would be more appropriate. It would also allow the CustomerServiceImpl to take the place of the CustomerService interface, since there is hardly a need for multiple implementations of such an interface in production code.

Fab Mars replied on Wed, 2011/06/29 - 4:25pm

Let's take an example: imagine you provide with an authentication service for e-commerce. You know, the little page that asks for personal information after you entered your credit card number in the merchant site?

Some banks want their cardholders to use passwords, some card readers, some digipasses, some even want some SMS-based one-time passwords. Your product has the 4 implementations ready.

With each new bank you work with, you checkout the code, configure the right alternative, repackage the app, deliver, and tap yourself in the back!

-That is the purpose of Alternatives-

 

BTW classpath/classloader tweaks won't work, won't be portable and won't get you far. If it works, just buy a lottery ticket, that's your lucky day.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.