Gateway
Usually patterns provide indirections and abstractions, invoker and invokee are separated. This pattern acts opposite, exposes feature that is internal by its nature. Persistence Contexts are internal features of any EJB. The aim is to externalize it, to make it remote, a part of distributed environment.
This pattern was presented in Adam Bien’s Real World Java EE Patterns – Rethinking Best Practices and contributions belong to the author. The name Gateway comes from this publication.
Persistence Context is a first level cache. It can be TRANSACTION scoped or EXTENDED. TRANSACTION means that Persistence Context is propagated with active transaction i.e. you can invoke several business methods on several EJBs, each using its own Container Manged Entity Manager, and there is a single shared Persistence Context, Persistence Context ends with transaction end. Unfortunately Persistence Context is not propagated with remote calls. Each remote call use its own Persistence Context despite of transaction and transaction demarcation, unless EJB Container resolves the Diamond Problem but you should not make such assumption.
There is a way to use Persistence Context in remote invocations effectively and flexibly. EXTENDED Persistence Context is not controlled by a transaction, expands on multiple transactions. The only legal place where you can use Container Managed EXTENDED Persistence Context is Stateful Session Bean. You can read data from database without active transaction and data would be cached in Persistence Context. You can modify objects in Persistence Context without active transaction. But modification are flushed to database only in an active transaction. If a transaction ends, a flush is enforced. EXTENDED Persistence Context starts with Stateful Session Bean creation and ends with its destruction.
@Stateful public class Gateway implements GatewayRemote { @PersistenceContext(PersistenceContextType.EXTENDED) private EntityManager em; public Long create(Order o){ em.persist(o); return o.getId(); } public void update(Order o){ em.merge(o); } public void remove(Long id){ Order o = em.find(Order.class,id); em.remove(o); } @Remove public void close(){ } }
This is a very simple example. This Gateway is just Entity Manager exposition and is identified with Persistence Context.
In original Gateway was interrelated with an instance of Entity, was identified with an instance of Entity.
@Stateful public class Gateway implements GatewayRemote { @PersistenceContext(PersistenceContextType.EXTENDED) private EntityManager em; private Order current; public Long create(Order o){ em.persist(o); current = o; return o.getId(); } public void update(Order o){ current = em.merge(o); } public void remove(){ em.remove(current); current = null; } public Order get(){ return current; } public void load(Long id){ current = em.find(Order.class,id); } public void addItem(Item i){ current.getItems().add(i); } public void removeItem(Item i){ i = em.merge(i); current.getItems().remove(i); } public void clearItems(){ current.setItems(new ArrayList()); } @Remove public void close(){ } }
And this is more convincing example of server side state usage. It should be clear that flushes to database are performed on the end of a transaction and transactions here are controlled by a client. You can control it manually with methods:
@Stateful @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public class Gateway implements GatewayRemote { ... @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void save(){ } @Remove @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void saveAndClose(){ } public void abort(){ } }
This is a matter of decision where to put a business logic. In the previous examples a business logic is solely client side. A business logic can also be partially client side and server side. For example server side can have general methods, optimization method and helper methods. In case of only server side business logic i.e. EJB component is integral, independent and encapsulated service, the Gateway pattern is not applicable.