Using a Model Facade
Status: In Early Access
Sean
Brydon, Smitha
Kangath
Problem Description
Java EE 5 applications can use Java Persistence APIs to create domain
models. When designing code that will access your
entities in the model tier, the calling client code, such as a Servlet,
a JSP page or a JSF managed bean, will access the entity
APIs. With Java Persistence entities, the
clients may get exposed to transactions APIs, EntityManager APIs and
other
features of the peristence APIs which are necessary to access the
entities in the model. Also, the client code may need to
interact with several entities, and each entity will have a different
API and maybe different requirements for transactions and entity
manager operations. As your
application gets larger, the code can have a lot of APIs for clients of
the model to use and this
can get difficult to manage as the client code can become tightly
coupled to the entity model.
Solution
The Facade pattern defines a higher level class that
encapsulates and centralizes the interactions between calling clients
and the model entities and operations of the Java Persistence
APIs. It provides a single interface
for operations on a set of entities. A client using this
facade
does not have to be aware of all the details of the APIs of each entity
and does not have to be aware of any persistence mechanisms being used
when accessing the entity managers or other transaction managers.
Introducing a facade between the calling clients and the entities in
the model tier makes the code more loosely-coupled and hence easier to
maintain.
As code example 1
shows, the code with a facade is cleaner.
Without
using a facade, the client of the entities needs to know details about
the entity and you might have similiar code cut-and-pasted in other
places whenever access to the entity was needed. The code
after introducing a facade hides many of the details of the entities in
the model tier.
Code
Before |
Code After Introducing a Facade |
public class MyWebClient ... {
...
public void doGet(HttpServletRequest request,
HttpServletResponse response)
{
...
//processing a web request to
//add a new Item to the database
if (selectedURL.equals("additem.do")) {
String desc = request.getParameter("item_desc");
String name = request.getParameter("item_name");
...
Item item = new Item();
item.setName(name);
item.setDescription(desc);
//now access entities in model
tier
EntityManager em =
emf.createEntityManager();
try{
utx.begin();
em.joinTransaction();
em.persist(item);
utx.commit();
}
catch(Exception exe){
System.out.println("Error persisting item: "+exe);
try {
utx.rollback();
}
catch (Exception e) {}
}
finally {
em.close();
}
}
... |
public class MyWebClient { ...
...
public void doGet(HttpServletRequest request,
HttpServletResponse response)
{
...
//processing a web request to
//add a new Item to the database
if (selectedURL.equals("additem.do")) {
String desc = request.getParameter("item_desc");
String name = request.getParameter("item_name");
...
Item item = new Item();
item.setName(name);
item.setDescription(desc);
//now access entities in model tier
MyModelFacade myfacade = new
MyModelFacade();
myfacade.addItem(item);
}
|
Code Example 1:
Refactoring the Client Calling Code by Introducing a Facade to Access
the Entities in the Model Tier
When designing a facade, here are some design choices to
consider:
- What kind of class should the facade be? Should
the facade be a Web
component such as a Servlet, Servlet Filter, a JSP or what? Or should
it be a plain old Java Object? If you choose to use a session
bean then you can use some of the services of the the EJB
container. Alternatively you can choose a web-only architecture since
Java Persistence entities are not required to run in EJB
containers.
- Should you add an extra interface on top of the
facade to hide whether its implemented as a Web Component or
a SessionBean? An extra interface can make the code more
loosely-coupled
from the entities and model tier details, but requires an extra class.
- Do you want the facade to use annotations and dependency
injection? To use these features, the facade must be a web component or
an EJB component as plain-old Java objects can not use dependency
injection.
- How will you look up and obtain the facade? And will you
hold and reuse the facade? For session beans you can use annotations
(or
optionally JNDI lookups) to obtain the facade. For a web component as
the facade, the client will have to use another approach to obtain the
facade. Also, consider designing a facade so that it can be used in a
thread-safe manner and reused for multiple client requests.
- What operations should it handle? Should it encapsulate all
the
transactions code? Should it encapsulate all the EntityManager
interactions for the entities? Typically, it is best to have the facade
handle all the transactional code and entity manager interactions.
- What kind of objects should it return? Should it return
entity
beans to be used as POJOs by the client code. Or be very
loosely-coupled and return a value object that is not an entity? For
the simple
cases, the facade can return the Java Persistence entities in detached
mode. In this way, the entities can be used as plain-old-Java-objects.
If further loose-coupling is desired, consider adding objects that are
client-specific and return those instead of the entity objects.
- What kind of exceptions should be thrown from the facade?
The
facade
should avoid exposing the calling clients to persistence-related
exceptions.
- Is the facade expected to be accessed locally or through
web services or remote operations? Since the facade is usually located
in the same VM as the calling web component, the default should be to
expect the facade to be a local object. Only add support for
remoteness, if necessary.
Lets consider some of the choices we made in the facades we used. In
our example applications we show both a web and a session bean facade.
These
examples will be discussed in more details below.
Strategy 1: Using a Web Component Facade
One strategy to implement a
facade is to use a Web component as the facade class. This strategy is
useful if you want to have a web-only architecture and dont want to use
an EJB container or if you just want to keep the facade in the web
container. If your application is using a web-only
architecture and
has the web components accessing the entities then using a facade can
help
clean up the code.
This will discuss implementation details of build web facade
public class CatalogFacade implements ServletContextListener {
@PersistenceUnit(unitName="CatalogPu") private EntityManagerFactory emf;
@Resource UserTransaction utx;
public CatalogFacade(){}
public void contextDestroyed(ServletContextEvent sce) {
if (emf.isOpen()) {
emf.close();
}
}
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.setAttribute("CatalogFacade", this);
}
public void addItem(Item item) throws InvalidItemException {
EntityManager em = emf.createEntityManager();
if(item.getName().length() == 0)
throw new InvalidItemException("The item" + " name cannot be empty. Please specify a name for the item. ");
try {
utx.begin();
em.joinTransaction();
em.persist(item);
utx.commit();
} catch(Exception exe){
System.err.println("Error persisting item: "+exe);
try {
utx.rollback();
} catch (Exception e) {
throw new RuntimeException("Error persisting item: "+ e.getMessage(), e);
}
throw new RuntimeException("Error persisting item: "+ exe.getMessage(), exe);
} finally {
em.close();
}
}
public Item getItem(int itemID){
EntityManager em = emf.createEntityManager();
Item item = em.find(Item.class,itemID);
em.close();
return item;
}
public List- getAllItems(){
EntityManager em = emf.createEntityManager();
List- items = em.createQuery("SELECT OBJECT(i) FROM Item i").getResultList();
em.close();
return items;
}
- }
Code Example 2: Web
Component as a Facade to Entity Bean and Model
In code example 2, one thing to note is that we chose to have the
facade implement the ServletContextListener
interface. This allows it to use annotations which are
useful when accessing Java Persistence entities and related
services of the Java EE platform. Now lets look at how a web component
such as a
servlet would look this
facade up, and then use it. For example, in the addItem
method, the JTA APIs for transaction demarcation are needed to access
the entities. Also, the EntityManager APIs are needed to store the new
Item in the database. Having this code in one place in a facade class
is
better than cutting-and-pasting similar code all around your
application whenever entity access is required.
public class CatalogServlet extends HttpServlet {
private ServletContext context;
private CatalogFacade cf;
public void init(ServletConfig config)throws ServletException {
context = config.getServletContext();
cf = (CatalogFacade)context.getAttribute("CatalogFacade");
initPathMapping();
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
try {
if (selectedURL.equals("additem.do")) {
//get values from request and set in a
//new Item to then add to database
String desc = request.getParameter("item_desc");
String name = request.getParameter("item_name");
...
Item item = new Item();
item.setName(name);
item.setDescription(desc);
...
cf.addItem(item); //now call facade
...
}
}
Code Example 3: Web
Component Using a Model Facade to Access Java Persistence Entities
In code example 3, one thing to note is that the facade is obtained by
the init
method of the Servlet and then
stored as a field in the
Servlet so that it can be used and shared by other client requests.
When processing a request, the Servlet can then uses the facade and is
shielded from the details of the entities and any persistence work
required to access the entities.
Strategy 2: Using a Session Bean Facade
An alternative strategy for implementing a facade is to use a
SessionBean as the facade. In this design you will need to use the EJB
container as it offers support for SessionBeans. One advantage of this
strategy is that session beans can take advantage of container services
such as container-managed transactions. So your code does not need to
include any extra code to
manage the transactions using JTA APIs.
@Stateless
public class CatalogFacadeBean implements CatalogFacade
{
@PersistenceContext(unitName="CatalogPu")
private EntityManager em;
public void addItem(Item item) throws InvalidItemException {
if(item.getName().length() == 0)
throw new InvalidItemException("The
item" +
" name cannot be empty.
Please specify a name for the item. ");
em.persist(item);
}
public Item getItem(int itemID) {
Item item = em.find(Item.class,itemID);
return item;
}
public List<Item> getAllItems() {
List<Item> items =
em.createQuery("SELECT OBJECT(i) FROM Item i").getResultList();
return items;
}
}
Code Example 4: Session
Bean as a Facade to Entities and Model Tier
In code example 4,
notice there is no code to demarcate the
transactions. In this case, we decided to leverage the
container-managed
transaction services of the EJB container. The desired transaction
behavior can be specified by annotations (we used the defaults here) or
optionally can be specified in a deployment descriptor. Additionally in
this example, we decided to use the container-managed entity manager so
the code did not need to include any calls to the EntityManager APIs,
and instead the EJB container does the work. If you
choose to use application-managed entity managers and transaction, then
your code would still need to include code to manage those services
programmatically.
public
class CatalogServlet extends HttpServlet {
private Map nameSpace;
private ServletContext context;
@EJB(name="CatalogFacadeBean") private CatalogFacade
cf;
public void init(ServletConfig config) throws
ServletException {
context =
config.getServletContext();
initPathMapping();
}
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
...
try {
if (selectedURL.equals("additem.do")) {
//get values from request and place
them into new Item
String desc =
request.getParameter("item_desc");
String name =
request.getParameter("item_name");
...
Item item = new Item();
item.setName(name);
item.setDescription(desc);
...
//use facade to add new Item to
database
cf.addItem(item);
...
}
...
}
Code Example 5: Web
Component using the Session Bean Facade to Access Java Persistence
Entities
In Code Example 5,
the Servlet uses dependency injection to lookup and
obtain the facade with @EJB(name="CatalogFacadeBean")
.
Notice also that the facade is stored as a field so that it can be
shared and used by other client requests. Usage of the facade hides the
detail of the access to the model tier.
References
Here are some references to consider:
© Sun Microsystems 2006. All of the
material in The
Java BluePrints Solutions Catalog is copyright-protected
and may not be published in other works without express
written permission from Sun Microsystems.