Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Lean service architectures with Java EE 6

Elements and patterns of a lean SOA

  • Print
  • Feedback

Page 6 of 6

Listing 5. Generic DAO implemented as a service

@Stateless
@Local(CrudService.class)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class CrudServiceBean implements CrudService {
   
    @PersistenceContext
    EntityManager em;
    
    public <T> T create(T t) {
        this.em.persist(t);
        this.em.flush();
        this.em.refresh(t);
        return t;
    }

    @SuppressWarnings("unchecked")
    public <T> T find(Class<T> type,Object id) {
       return (T) this.em.find(type, id);
    }

    public void delete(Object t) {
       Object ref = this.em.getReference(t.getClass(), t);
       this.em.remove(ref);
    }

    public <T> T update(T t) {
        return (T)this.em.merge(t);
    }

    public List findWithNamedQuery(String namedQueryName){
        return this.em.createNamedQuery(namedQueryName).getResultList();
    }
    
    public List findWithNamedQuery(String namedQueryName, Map<String,Object> parameters){
        return findWithNamedQuery(namedQueryName, parameters, 0);
    }

    public List findWithNamedQuery(String queryName, int resultLimit) {
        return this.em.createNamedQuery(queryName).
                setMaxResults(resultLimit).
                getResultList();    
    }

    public List findByNativeQuery(String sql, Class type) {
        return this.em.createNativeQuery(sql, type).getResultList();
    }
    
   public List findWithNamedQuery(String namedQueryName, Map<String,Object> parameters,int resultLimit){
        Set<Entry<String, Object>> rawParameters = parameters.entrySet();
        Query query = this.em.createNamedQuery(namedQueryName);
        if(resultLimit > 0)
            query.setMaxResults(resultLimit);
        for (Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }
}

More challenging are the query methods. You can't know the number of query parameters and their names when you develop the DAO service, so you must provide them in a generic way. Listing 6 shows the creation of query parameters in a fluent way with the Builder pattern.

Listing 6. The builder for query parameters

public class QueryParameter {
    
    private Map<String,Object> parameters = null;
    
    private QueryParameter(String name,Object value){
        this.parameters = new HashMap<String,Object>();
        this.parameters.put(name, value);
    }
    public static QueryParameter with(String name,Object value){
        return new QueryParameter(name, value);
    }
    public QueryParameter and(String name,Object value){
        this.parameters.put(name, value);
        return this;
    }
    public Map<String,Object> parameters(){
        return this.parameters;
    }
}

The most suitable data structure for the transportation of query parameters is a java.util.Map, but it's rather inconvenient to create. The simple QueryParameter builder makes the construction of the query parameters more convenient and fluent. Listing 7 shows the use of the QueryParameter builder to construct the parameters.

Listing 7. The fluent way to construct a query parameter

import static ...dataservice.QueryParameter.*;

   public List<Order> findOrdersForCustomer(int customerId){
        return this.crudService.findWithNamedQuery(Order.findByCustomerId,
                with("customerId", customerId).
                parameters());

The static with() method creates a new QueryParameter instance. It enables a static import and makes the qualification with the QueryParameter superfluous. The and() method just puts the parameters into the internal Map and returns the QueryParameter again, which makes the construction fluent. Finally, the method parameters return only the internal Map, which can be directly passed to the CrudService.

A touch of pragmatism

Most service components are not complicated and consist mainly of basic data operations. In these cases the facade would just delegate to a service, which in turn delegates to the javax.persistence.EntityManager or a generic CrudService to perform its persistence tasks. Otherwise you will end up getting two dumb delegates: the facade would only delegate to the service, and the service to the CrudService (aka DAO). Delegates without additional responsibilities are just dead code. They only increase the code complexity and make maintenance more tedious. You could fight this bloat by making the service layer optional. The facade would manage the persistence and delegate the calls to the EntityManager. This approach violates the separation-of-concerns principle, but it's a very pragmatic one. It is still possible to factor reusable logic from the facade into the services with minimal effort.

If you really want to encapsulate the data access in a dedicated layer, you can easily implement it once and reuse the generic access in different projects. Such a generic DAO does not belong to a particular business component. It would violate the cohesion. In our case it is deployed in a technical component with the name dataservice.

Diagramming robustness

The architecture I've described in this article can be expressed with a robustness diagram consisting of Entity, Control, and Boundary (as defined on the Agile Modeling site). The original definition of the Entity-Control-Boundary (ECB) architectural pattern matches perfectly with our pattern language. A domain structure is an Entity, the Control is a service, and the Boundary is realized with a facade. In simpler cases the facade and service can collapse, and a service would be realized only as a facade's method in that case. Figure 3 shows the service component visualized with a robustness diagram:

Figure 3: The service component in a robustness diagram

Figure 3. The service component in a robustness diagram

We could even replace our custom naming in the internal-component layer with the names of the ECB elements. (The naming of Java EE components is mainly influenced by the Core J2EE Patterns, so the terms facade, service, and domain object are more typical in an "enterprise" Java context.)

As lean as possible, but not leaner

It would be hard to streamline the SOA architecture presented in this article further without sacrificing its maintainability. I know of no other framework that lets you express a service-based architecture in such a lean and straightforward manner. No XML configuration, external libraries, or specific frameworks are needed. The deployment consists of only a single JAR file with a short persistence.xml like the example shown in Listing 5.

Listing 5. persistence.xml -- the only XML configuration needed

<persistence>
  <persistence-unit name="prod" transaction-type="JTA">
    <jta-data-source>jdbc/sample</jta-data-source>
  </persistence-unit>
</persistence>

The whole architecture is based on few annotated Java classes with pure interfaces (that is, interfaces that are not dependent on the EJB API). With EJB 3.1 you could even remove the interfaces and inject the bean implementation directly. Services and DAOs could be deployed without any interfaces, which would halve the total number of files and make the component significantly leaner. However, you would lose the ability to mock out the service and DAO implementation easily for plain unit tests.

About the author

Consultant and author Adam Bien is an Expert Group member for the Java EE 6, EJB 3.1, and JPA 2.0 JSRs; a NetBeans Dream Team Member; and a Java Champion. He has edited several books about Java and J2EE technology. He is an architect and developer on Java EE 5 projects and participates in several open source projects as committer and owner. He is working on Pragmatic Java EE - Rethinking the Best Practices. Visit Adam's blog.

Read more about Enterprise Java in JavaWorld's Enterprise Java section.

  • Print
  • Feedback

Resources

More from JavaWorld