EJB 3.0 introduced the @EJB dependency injection which is used in local or remote clients to obtain access to the session beans (In EJB 3.0 only session beans have a client view). However, this dependency injection can be used only in web components whose life cycle is managed by the Java EE web container and session beans. For other clients, the access to the EJB components need to be obtained via JNDI lookup.
Struts Clients
Web components available in Struts such as Struts action classes are not standard Java EE components and hence their life-cycle is not managed by the container. The @EJB dependency injection cannot be used in Struts Action classes unless the Struts-DI library is included to allow Struts 1.x Action classes to use it. Guice is used for dependency injection in Struts 2 Action classes.
Standard JNDI Names
Until EJB 3.1, the JNDI names for enterprise beans was not a standard. The Java EE application server vendors followed their own JNDI naming convention. As a result, the enterprise bean clients were not portable across the Java EE application servers. The EJB 3.1 specification defines the JNDI names to access a bean from global, application and module levels as a standard.
Accessing EJB Components from presentation-tier
Though, the session beans can be accessed in standard Java EE web components by @EJB dependency injection, it is a good practice not to include them directly in the web components.
A Business Delegate can be introduced between the presentation-tier components and the business-tier components as shown in Figure 1 to call the session beans’ methods. A Service Locator is used to obtain access to the Session beans.
Figure 1. UML Class Diagram – Using a Business Delegate to access Session beans
The AdminDelegate (Listing 6) is a local POJO client to the Session Facade. The AdminFacadeBean (Listing 3) which is a client to Application Service is a stateless session bean with no-interface view and remote client view. The BeanLocator (Listing 4) is a POJO used to get client access to the session bean using JNDI lookup. It can be used to lookup both local and remote session beans.
The Application Service layer is implemented by EmployeeManagementBean and RegionManagementBean (Listing 2) stateless session beans. The EmployeeManagementBean and RegionManagementBean perform CRUD operations on the Employee and Region (Listing 1) entities respectively. All session beans have no-interface view since they will be packaged in the same application as the presentation-tier components. The stateless session beans also provide remote client view.
If these session beans are packaged in admin-ejb.jar (no module name is specified in ejb-jar.xml) and the enterprise application is packaged in adminApp.ear (no application name is specified in application.xml), the global JNDI name to access AdminFacadeBean bean in local client is “java:global/adminApp/admin-ejb/AdminFacadeBean”. The JNDI name for remote client access is “java:global/adminApp/admin-ejb/AdminFacadeBean!com.cosmos.admin.AdminFacade”.
For loose coupling, the BeanLocator uses a config.properties (Listing 5) file which defines the application and module file names for the bean. It also defines the name of the remote client for a session bean.
The business logic along with the object relationships is implemented in Employee and Region entities are they not anemic domain model. The entities are used instead of separate DTO classes in the presentation-tier components as both business-tier and presentation-tier components are packaged in the same application and run on same application server. DTO classes will be needed for remote presentation-tier components (either the presentation-tier and business-tier components are deployed in different application on the same application server or they are deployed on different application servers).
@Entity
@NamedQuery(name=”getByName”,
query=”Select r from Region r where r.name = :name”)
public class Region implements Serializable {
@Id private long id;
@Column(unique=true, nullable=false)
private String name;
@OneToMany(mappedBy=”region”)
private Set employees = new HashSet();
public void setId(long id) { this.id = id; }
public long getId() { return id; }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public void setEmployees(Set employees) {
this.employees = employees;
}
public Set getEmployees() { return employees; }
public boolean hasEmployees() {
return !getEmployees().isEmpty();
}
}
Listing 1. Region entity
@Stateless
@LocalBean
@Remote(RegionManagement.class)
public class RegionManagementBean implements RegionManagement {
@PersistenceContext
private EntityManager entityManager;
public void add(Region region) throws AdminException {
try {
entityManager.persist(region);
}
catch(EJBException exp) {
throw new AdminException(”Region with name = “ + name
+ “ already exists”);
}
}
public void update(Region region) throws AdminException {
try {
entityManager.merge(region);
}
catch(EJBException exp) {
throw new AdminException(”Region with name = “ + name
+ “ already exists”);
}
}
public void delete(Region region) throws AdminException {
if(region.hasEmployees()) {
throw new AdminException(”Region cannot be deleted ” +
”as it has employees”);
}
entityManager.remove(region);
}
public Region getById(long id) throws EntityNotFoundException {
Region region = entityManager.find(Region.class, id);
if(region == null) {
throw new EntityNotFoundException(”Region with id = “ +
id +“ does not exists”);
}
}
...
}
Listing 2. RegionManagementBean
@Stateless
@LocalBean
@Remote(AdminFacade.class)
public class AdminFacadeBean implements AdminFacade {
@EJB
private RegionManagementBean regionManagementBean;
@EJB
private EmployeeManagementBean employeeManagementBean;
public void addRegion(Region region) throws AdminException {
regionManagementBean.add(region);
}
public void updateRegion(Region region) throws AdminException {
regionManagementBean.update(region);
}
public void deleteRegion(long id) throws
AdminException, EntityNotFoundException {
Region region = getRegionById(id);
regionManagementBean.delete(region);
}
public Region getRegionById(long id) throws
EntityNotFoundException {
region = regionManagementBean.getById(id);
}
...
}
Listing 3. AdminFacadeBean
public class BeanLocator {
private static BeanLocator instance =
new BeanLocator();
private static final String APPLICATION = “-application“;
private static final String MODULE = “-module“;
private static final String REMOTE_CLIENT = “-remoteClient“;
private static final String CONFIG = “config“;
private ResourceBundle resourceBundle;
private Context context;
public static BeanLocator getInstance() {
return instance;
}
private BeanLocator() {
resourceBundle = ResourceBundle.getBundle(CONFIG);
context = new InitialContext();
}
public Object lookup(String beanName) throws NamingException {
return lookup(beanName,false);
}
public Object lookup(String beanName,boolean remote) throws
NamingException {
String application = resourceBundle.getString(beanName +
APPLICATION);
String module = resourceBundle.getString(beanName +
MODULE);
String jndi = “java:global/“ + application + “/” + module + “/” +
beanName;
if(remote) {
String remoteClient = resourceBundle.getString(
beanName + REMOTE_CLIENT);
jndi += “!“ + remoteClient;
}
return context.lookup(jndi);
}
}
Listing 4. BeanLocator
AdminFacadeBean-application=adminApp
AdminFacadeBean-module=admin-ejb
AdminFacadeBean-remoteClient=com.cosmos.admin.AdminFacade
Listing 5. config.properties
public class AdminDelegate {
private static AdminDelegate instance = new AdminDelegate();
private BeanLocator beanLocator;
private AdminFacadeBean adminFacade;
private final String ADMIN_FACADE_BEAN = “AdminFacadeBean”;
private AdminDelegate() {
beanLocator = beanLocator.getInstance();
try {
adminFacade = (AdminFacadeBean)
beanLocator.lookup(ADMIN_FACADE_BEAN,
false);
}
catch(NamingException exp) {
AdminException _exp = new AdminException(
“Cannot instantiate AdminDelegate”);
_exp.initCause(exp);
throw _exp;
}
}
private static AdminDelegate getInstance() {
return instance;
}
public void addRegion(Region region) throws AdminException {
adminFacade.addRegion(region);
}
public void updateRegion(Region region) throws AdminException {
adminFacade.updateRegion(region);
}
public void deleteRegion(long id) throws AdminException,
EntityNotFoundException {
adminFacade.deleteRegion(id);
}
...
}
Listing 6. AdminDelegate