Level: Introductory Daniel Berg (danberg@us.ibm.com), Senior Software Engineer, IBM, Rational SOA tools Narinder Makin (nmakin@us.ibm.com), Software Architect, IBM Rational SOA tools, IBM Ritchie Schacher (schacher@us.ibm.com), Software Architect, IBM Rational SOA tools, IBM
15 Jun 2005 Updated 14 Nov 2005 This article is the first of a multi-part series that introduces a new feature in Rational Application Developer for WebSphere Software, which automates the generation of the Value Object and Session Facade patterns for EJB development. The generated code uses Service Data Objects (SDOs) as the Value Objects for data transfer, thus enabling coarse-grained services and the use of standards-based programming. The article gives you an overview of how to use the feature to generate code, and how to write a client application using the programming model.
Introduction
The Value Object and Session Facade patterns for EJB (Enterprise JavaBean) development are well-known and widely used as a means to simplify or optimize programming with EJBs. This article is the first of a multi-part series that introduces a new feature in IBM® Rational® Application Developer (RAD) for IBM® WebSphere® Software, Version 6.0, which automates the generation of these patterns. Moreover, the generated code uses Service Data Objects (SDOs) as the Value Objects for data transfer, thus enabling coarse-grained services and the use of standards-based programming. This first article gives you an overview of how to use the feature to generate code, and how to write a client application using the programming model. More detailed and advanced usages will be presented in the subsequent parts of this series.
Value Object pattern
Container-managed entity beans are shared server-side business objects that are transactional. For a remote client to directly manipulate these objects, remote interfaces would need to be exposed for the entity beans. The data is accessed by clients via remote calls, which means that obtaining the necessary persistent data can be resource-intensive given the network overhead. Therefore, it is typically not a good idea for a non server-side client to communicate directly with these business objects.
Since the EJB 2.0 specification, it is best practice to only access the entity beans locally through local interfaces. Local interfaces by definition are not remoteable. Therefore, in order to provide remote access to the persistent beans, remote clients should access the local entities by passing data through methods exposed on a session bean with a remote interface, thus encapsulating access to the entities. In order to pass persistent data between a remote client and the server side session bean, Value Objects must be defined for the underlying local CMP (Container Managed Persistence) entities. The Value Object (also known as a Transfer Object, Data Transfer Object (DTO), or Cargo bean) is a standard Java bean with fields for each persistent attribute of the CMP bean. Methods must exist to synchronize the data between the Value Object and CMP bean. In some implementations, these methods will exist on the CMP bean itself.
Benefits
One of the key benefits of using a Value Object is that non-server side clients
can access persistent data without the network overhead that is observed when
accessing the same data via a container managed entity bean. This means that
a client can obtain and set each individual property without a remote invocation
(RMI) call. With a remote CMP bean, every access to a persistent property by
a remote client is done via RMI. This can be extremely expensive.
Existing technologies and limitations
As already indicated, a Value Object is any arbitrary Java bean. There is nothing
inherently special about the bean itself. However, there are a couple of well-known
technologies that create Value Objects: Data Access beans from IBM® WebSphere®
Studio, and XDoclet annotation based value-objects (see Resources)
from Source Forge. Both technologies generate very similar objects, so in this
article we will focus on Data Access beans.
Example
From the shipped example, there is an EmployeeData access bean that was generated
for the Employee CMP entity bean. Below is a snippet of the EmployeeData access
bean file showing only the ssn field and accessors (since they
are all the same generation pattern).
public class EmployeeData extends AbstractEntityData {
/**
* @generated
*/
private java.lang.Integer ssn;
/**
* @generated
*/
private boolean isssnDirty = false;
...
/**
* getIsssnDirty
* @generated
*/
public boolean getIsssnDirty() {
return this.isssnDirty;
}
/**
* getSsn
* @generated
*/
public java.lang.Integer getSsn() {
return this.ssn;
}
/**
* setSsn
* @generated
*/
public void setSsn(java.lang.Integer value) {
this.ssn = value;
this.isssnDirty = true;
this.isDirty = true;
}
...
/**
* Store
* @generated
*/
public interface Store extends AbstractEntityData.Store {
/**
* getSsn
* @generated
*/
public java.lang.Integer getSsn();
/**
* setSsn
* @generated
*/
public void setSsn(java.lang.Integer value);
...
}
/**
* EmployeeData
* @generated
*/
public EmployeeData() {
super();
}
/**
* EmployeeData
* @generated
*/
public EmployeeData(EmployeeData.Store initializer) {
super(initializer);
initialize(initializer);
}
/**
* initialize
* @generated
*/
protected void initialize(EmployeeData.Store initializer) {
this.ssn = initializer.getSsn();
this.dateOfBirth = initializer.getDateOfBirth();
this.firstName = initializer.getFirstName();
this.lastName = initializer.getLastName();
}
/**
* copyTo
* @generated
*/
public void copyTo(EmployeeData.Store target) {
if (!this.isDirty)
return;
if (this.isssnDirty)
target.setSsn(this.ssn);
if (this.isdateOfBirthDirty)
target.setDateOfBirth(this.dateOfBirth);
if (this.isfirstNameDirty)
target.setFirstName(this.firstName);
if (this.islastNameDirty)
target.setLastName(this.lastName);
}
} |
Note that there is an inner interface named Store, which has the public getter
and setter methods for each persistent
property. This is used to initialize the EmployeeData bean as well as to copy
the persistent property values to a particular target, as seen in the copyTo(...)
method. In this case the Employee CMP bean implements the EmployeeData.Store
interface. The following three methods are generated on the Employee CMP bean
class to create a new EmployeeData access object, and to synchronize data to
and from the access object and the entity bean itself.
/**
* getEmployeeData
*/
public ejbs.EmployeeData getEmployeeData() {
return new ejbs.EmployeeData(this);
}
/**
* setEmployeeData
*/
public void setEmployeeData(ejbs.EmployeeData data)
throws com.ibm.etools.ejb.client.runtime.FieldChangedException {
data.copyTo(this);
if (!data.getIsssnDirty()) {
if (this.getSsn() != null && data.getSsn() != null) {
if (!this.getSsn().equals(data.getSsn())) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
} else if (!(this.getSsn() == null && data.getSsn() == null)) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
}
if (!data.getIsdateOfBirthDirty()) {
if (this.getDateOfBirth() != null && data.getDateOfBirth() != null) {
if (!this.getDateOfBirth().equals(data.getDateOfBirth())) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
} else if (!(this.getDateOfBirth() == null && data.getDateOfBirth() == null)) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
}
if (!data.getIsfirstNameDirty()) {
if (this.getFirstName() != null && data.getFirstName() != null) {
if (!this.getFirstName().equals(data.getFirstName())) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
} else if (!(this.getFirstName() == null && data.getFirstName() == null)) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
}
if (!data.getIslastNameDirty()) {
if (this.getLastName() != null && data.getLastName() != null) {
if (!this.getLastName().equals(data.getLastName())) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
} else if (!(this.getLastName() == null && data.getLastName() == null)) {
throw new com.ibm.etools.ejb.client.runtime.FieldChangedException();
}
}
}
/**
* syncEmployeeData
*/
public ejbs.EmployeeData syncEmployeeData(ejbs.EmployeeData data) {
data.copyTo(this);
return this.getEmployeeData();
} |
Limitations and drawbacks
Even though the Value Object pattern itself is a good pattern, the current
technologies do have a few limitations:
- Using the Value Object requires that you modify the entity bean class include
methods for obtaining and synchronizing the Value Object.
- Most Value Object implementations only have persistent fields from the CMP
entity, and they typically do not deal with persistent relationships (that
is, CMR fields).
- The Value Objects are derived from CMP bean instances. Therefore, if a Value
Object is a projection of the entire entity bean (for example,containing only
a subset of the persistent fields on the bean), all the attributes are still
read into the entity bean itself on the server.
Session Facade pattern
The Session Facade pattern in a nutshell provides an interface for clients
to interact with business objects without needing a tight coupling to the actual
implementation of the business objects. So the Session Facade provides coarse-grained
services to clients that typically return Value Objects and accept Value Objects
as arguments.
Benefits
The key benefit of the Session Facade pattern is the decoupling of the client
and the underlying business objects. This allows the application designer the
freedom to change the underlying business objects without changing the APIs
(Application Programming Interface) presented to the client within the facade.
The facade also provides coarse-grained services to clients in terms of Value
Objects. Thus clients have access to persistent data in a disconnected fashion.
Existing technologies and limitations
WebSphere Studio does not currently generate a Session Facade, but it is easy
enough to code a Session bean and add APIs for interacting with the container
managed entities to manipulate persistent data via data access beans. XDoclet
has an annotation named @ejb.remote-facade which is used to create a
session bean for interacting with a single container managed entity. However,
this does not follow the true Session Facade pattern, since it does not work
with Value Objects. It basically generates lookup code for the entity bean.
Example
It is easy enough to create a Session Facade. The example with this article
has a hand-coded EmployeeDataAccessFacade which uses the EmployeeData access
bean for transferring data to and from the Employee container managed entity.
The facade has create, read, update, and delete (CRUD) methods for interacting
with EmployeeData access beans, as shown following.
private EmployeeLocalHome getEmployeeHome() {
if (employeeHome == null) {
try {
Context ctx = new InitialContext();
Object home = ctx.lookup("java:comp/env/ejb/Employee");
employeeHome = (EmployeeLocalHome) PortableRemoteObject.narrow(home,
EmployeeLocalHome.class);
} catch (ClassCastException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return employeeHome;
}
public EmployeeData[] getEmployees() {
Collection col = null;
try {
col = getEmployeeHome().findAll();
} catch (FinderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (col == null)
return new EmployeeData[0];
EmployeeData[] data = new EmployeeData[col.size()];
Iterator it = col.iterator();
for (int i = 0; it.hasNext(); i++) {
EmployeeLocal employee = (EmployeeLocal) it.next();
data[i] = employee.getEmployeeData();
}
return data;
}
public EmployeeData getEmployeeByKey(Integer ssn) {
EmployeeLocal employee = null;
try {
employee = getEmployeeHome().findByPrimaryKey(ssn);
} catch (FinderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (employee != null)
return employee.getEmployeeData();
return null;
}
public void createEmployee(EmployeeData data) {
EmployeeLocal employee = null;
try {
employee = getEmployeeHome().create(data.getSsn());
} catch (CreateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (employee != null)
employee.syncEmployeeData(data);
}
public void updateEmployee(EmployeeData data) {
EmployeeLocal employee = null;
try {
employee = getEmployeeHome().findByPrimaryKey(data.getSsn());
} catch (FinderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (employee != null)
employee.syncEmployeeData(data);
}
public void deleteEmployee(EmployeeData data) {
EmployeeLocal employee = null;
try {
employee = getEmployeeHome().findByPrimaryKey(data.getSsn());
} catch (FinderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (employee != null)
try {
employee.remove();
} catch (EJBException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (RemoveException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} |
Limitations and Drawbacks
The current technology is correct but it has some drawbacks:
- In order to get a Value Object, you must look up the container managed entity.
- You must coordinate all reads and updates of the persistent data through
the container managed entity.
- Only one type of Value Object is operated upon within each service
call.
Using RAD to build Session Facades
New in RAD Version 6 is the capability to define and generate Session Facades
and Value Objects for container managed entity beans using annotations on the
bean class.
Benefits and differences
There are several key differences from the RAD Session Facade pattern
and the pattern described above. These differences provide a distinct
advantage over the current technologies.
Service Data Objects
The first noticeable difference is that the Session Facade deals with specialized
Value Objects called Service Data Objects (SDOs). The Service Data Object specification
is a joint specification developed by IBM and BEA to provide a more advanced
Value Object pattern. The SDOs generated in RAD Version 6 are based on the open
source implementation available at Eclipse. SDOs can have relationships to other
SDOs, and these relationships are automatically maintained by the runtime. The
SDOs are also part of a Data Graph that can record all changes made to all SDOs
within the graph, so that the exact changes are known at the time of being persisted
to a data store.
No impact to entity bean files
One very obvious difference with the RAD Session Facade is its use of SDO objects
rather than typical Value Objects. In addition, the CMP entity bean files are
not modified to return and synchronize persistent data to and from the SDO objects.
In fact, the Session Facade generated within RAD Version 6 does not even use the entity
beans. This article will provide more information on this topic in subsequent
sections.
CMP entities are bypassed entirely
As already discussed in this article, the typical Session Facade pattern will
look up a particular Home interface and call a finder method in order to return
a collection of entity instances. This collection can then be iterated such
that a Value Object is obtained for each entity bean instance. The implementation
of the RAD Session Facade does not look up any Home interface, and thus
does not find any CMP entity instances. The SDO instances are populated by an
object called an EJB mediator. The EJB mediator is responsible for reading data
from the database using a modified EJB QL syntax, and for processing changes
to a DataGraph that result in updates to the database. It should be noted
that the RAD Session Facade pattern using the EJB mediator does not read CMP
data from a persistent cache at runtime. The data is read directly from the
database. You can find more information regarding caching and the use of
CMP entities in the Application considerations
section.
Multiple SDOs per facade
The tooling support for defining a Session Facade in RAD Version 6 allows you
to add a particular SDO object to the facade to be managed. In current technologies
that support the generation of the Session Facade, there is typically one Session
Facade per entity bean. In RAD Version 6, you have the capability to add multiple
SDO objects from one CMP entity to a particular Session Facade, as well as add
SDOs from other CMP entity beans to the same Session Facade. This way Session
Facades can be defined to best fit the application.
SDO query support
In RAD Version 6 there is also support to define queries in terms of the SDOs
that you wish to manage by a Session Facade. You can write these queries using
annotations on a CMP entity bean class, and simplify the syntax of the query
so it is not necessary to define the shape (that is, select the fields), since
this information is already known based on the selected SDO object. You can
write them to query upon the properties of the selected SDO or any of its related
SDO objects. More details about SDO-based queries will be provided in part two
of this series.
User modification to generated code
In the RAD Version 6 EJB Session Facade over SDOs pattern, you can modify the
generated code, and your code will be preserved and merged on subsequent generations.
Earlier solutions, such as access beans, did not have such a mechanism. This
will be discussed further in a future part of this series.
When to use
Now that we've described the benefits of using the RAD Version 6 based Session
Facade over SDO pattern, let's discuss when you would want to consider using
this in your EJB and J2EE (Java 2 Platform, Enterprise Edition) applications.
When you need disconnected data access
If you are using the data from your entity beans from a remote client -- such
as a J2EE application client, or from a web page in a web application used to
display the data -- you will want a mechanism to iterate over a collection of
objects without invoking a remote call for each object. This pattern will enable
such data access. With improved technology, however, you now have this access
using SDOs as the data transfer objects. As mentioned above, this enables many
things:
- The introduction of relationships to a graph of transferred objects
- Inverse maintenance and referential integrity of the related objects
- Automatic serialization
- Meta-model capabilities for reflecting and setting values for attributes
and relationships on the SDOs
When you need coarse grained updates
Introducing relationships to the pattern -- as well as the robust support for
associations in the SDO specification, and the Eclipse Modeling Framework (EMF)-based
implementation of SDOs -- enables some very interesting applications that require
coarse grained services. For example, you can read a collection of CMP-based
SDOs, along with their related objects, as part of a single database query.
Those familiar with the preload support of WebSphere Application Server will
appreciate that the preload is an alternate, existing method for optimizing
reads of a collection of related objects. The RAD-based Session Facade, however,
takes this one step further: by using SDOs and a datagraph, you can transfer
the graph of resulting SDOs in a single remote call. Moreover, you can make
updates to the graph, including changing the values of the objects, adding new
objects, and removing existing objects, in a disconnected manner. Furthermore,
you can commit the changes to the graph as a single transaction with a single
remote call, thus resulting in single transfer of the entire modified graph.
When you need projections and selections of the underlying entity data
By design, when you read a CMP entity bean into the EJB container, all the
fields of the entity bean are populated. However, there are many times when
you will only be working with a small subset, or projection, of the fields.
For example, suppose you are generating a report that displays a list of employees
by firstName and lastName, but you do not need the
employees ssn or salary fields. This pattern will
allow you to create lightweight SDOs (with only those fields in which
you are interested) for a given application. Moreover, in contrast to earlier
implementations, this Session Facade will not read all the CMP fields from the
database, but rather only the fields contributed to the SDOs. As you will see
later, this is because the underlying implementation performs direct reads to
the data source and bypasses container managed persistence, thus optimizing
the performance of the coarse grained updates described above.
In other cases you may wish to create deep SDOs, which bring in some
of the CMR fields of a collection of entities, as well as the CMP fields. You
can use this facility to model these SDOs to perform the reads and updates to
the data in an optimal manner.
Additionally, there are scenarios where -- in the process of performing a coarse
grained transaction, as described previously -- you will want to filter the
returned data based on a complex set of search criteria (or in simple parlance,
with a lot of where clauses). As you will see later, this facility
provides the ability to create complex queries which can go n levels
deep into a set of related CMP-based SDOs, adding where clauses
at any level of the graph of objects. These queries will be executed at runtime
in an optimal manner, thus facilitating the coarse grained updates pattern.
Sample Application
Scenario
Let's create an application scenario for a Company to illustrate the steps involved
in using this feature. The following class diagram depicts the entities and
their relationships that are used in the application. This class diagram is
created using the visual editor in RAD, as shown in Figure 1.
| |
Figure 1: Class diagram
|
Project Interchange
The examples are prepared for you in a project interchange ZIP file. To add the examples, import the project interchange Company_PI.zip into your workspace. As a result, the following projects (seen in Figure 2, following) are imported into the workspace:
- CompanyEAR -- This is the Enterprise Application project that contains references to all the JAR and module projects to compose a J2EE EAR file.
- CompanyEJB -- This is the EJB module that contains the related CMP EJBs. The classdiagram.dnx describes the EJBs present in the project and their relationships.
- CompanyEJBClient -- This is the EJB client JAR project, that contains all the client interfaces for Company EJB and any required classes. Company EJB depends on CompanyEJBClient; any client application (for instance, a Web application or J2EE Application Client) would also depend on CompanyEJBClient.
| |
Figure 2: Imported example projects
|
Note that the sample contains the hand-coded facade, as described in previous sections. It does not contain the generated facade or the application client, which you will create using the steps in the following sections. For reference, a completed solution is contained in the project interchange file Company_SolutionPI.zip.
Using the wizard
The Session Facade for any CMP2.x entity bean can be created using a wizard, which you can open by using an action on the entity bean. This action is available from the entity bean in the Project Explorer, and also on the visualized entity bean in the visual editor, as illustrated in Figure 3.
| |
Figure 3: Create Session Bean Facade action on visualized entity bean
|
Let's create a Session Facade on the Employee entity bean as EmployeeFacade
using the Create Session Bean Facade action, as shown in Figure 4. The
name of the facade in the wizard page is based on the selected entity bean (the
default is EmployeeFacade). The folder for the generated output is preset
to gen/src folder, where all the generated content resides. The name of the
package for the Session Facade is derived from the package name of the entity
bean. All the entity beans present in the module are listed here. You may select
any CMP 2.x entity bean other than the Employee to create the facade on.
| |
Figure 4: Create Session Bean Facade wizard
|
Generated output
When the wizard finishes, annotations are injected in the entity bean Java files. These annotations define the model for the generation of the target Session Facade and the static SDO java classes. The meta-model for the tag-sets for the Session Facade annotations consists of the ws.sbf and ws.sdo namespaces. More information on these tags-sets can be found in the product documentation. From the menu bar, select Help -> Help Contents, and navigate through the table of contents as shown in Figure 5 below.
| |
Figure 5: Annotation tag reference help
|
When a facade is created in a project, that action adds a framework utility (sbf-runtime.jar) to the EAR projects that contain the EJB module, and to the Java JAR dependencies of the EJB client JAR project. The contents of this JAR will be required by the generated facade that results.
As in the example, value-object and session-facade annotation contributions are made to the Employee entity bean Java file. The value-object annotation tag is defined as @ws.sdo.value-object for an Employee bean class.
The SDO name defaults to Employee, which is derived from the name of
the bean. The value of the read-only attribute signifies whether the operations
defined on the Employee value-object are for read-only purposes:
* @ws.sdo.value-object
* name="Employee"
* read-only="false" |
The session-facade annotation for the EmployeeFacade is defined using a @ws.sbf.session-facade
tag. The name attribute of the tag defines the name of the session bean. The value-objects
attribute defines the list of value-objects that are added to, and thus managed
by, the Session Facade:
* @ws.sbf.session-facade
* name="EmployeeFacade"
* value-objects="Employee" |
Figure 6 depicts how, if the project in which this Session Facade is created
does not have annotation support enabled, the Session Facade creation action
also enables the annotation support on the EJB project by adding an annotation
builder and a data object builder to the project.
| |
Figure 6: Annotation support for EJB project
|
Once the project is built -- either when you Save if auto-build is turned on, or when you invoke an explicit build for the project -- the above annotations are processed. For each of the @ws.sdo.value-object
annotations, a static SDO Java class is created that contains all the attributes
that are added to the defined value-object, along with get/set methods.
These generated SDO classes implement the DataObject interface as pure SDO classes.
As in this example, the ejbs.sdo.Employee Java class is created in the client
EJB project.
Additionally, for each of the session-facade annotations, a session bean is
created with the bean name as the value of the name attribute of the tag. Lastly,
a root SDO Java class is created for each of the value-object values that is
added to the Session Facade tags value-objects attribute. For example (as shown
in Figure 7), the SDO ejbs.sdo.EmployeeRoot is created, corresponding to the
Employee top-level SDO that was contributed to the facade. This root class acts
as a container for all of the other SDO objects that are created at runtime,
and is used at runtime for creating a data mediator for persistence.
Best of all, as you will see in a later article, you are not limited to the
generated code. A mechanism exists where you can add user code to the generated
output.
| |
Figure 7: SDO package
|
The EmployeeFacade session bean contains the CRUD APIs to manage
the instances of the Employee SDO, which has been added to the facade using
the value-objects attribute. The methods it creates (shown in Figure 8) are
getAllEmployeeObjects, getEmployeeByKey, createEmployee,
updateEmployee, and deleteEmployee . Advanced APIs,
such as getEmployeeRoot and applyEmployeeRootChanges,
are also created that provide more control to manage the Employee SDO in the
client code for the facade. The generated code in these APIs access the database
via the EJB mediator using the EJB-QL syntaxed queries.
| |
Figure 8: Session Facade Bean
|
The session bean Java class that is created with all the CRUD methods is also annotated with the XDoclet style @ejb annotation tagset. Once these annotations are processed, the session bean is added to the EJB deployment descriptor.
/**
* @ejb.bean
* name="EmployeeFacade"
* view-type="both"
* type="Stateless"
* transaction-type="Container"
*<!-- begin-user-doc -->
*<!-- end-user-doc -->
* This is a system generated session bean facade source file.
* Any modifications to the tags should be done in the
* primary source Entity Bean file for this facade.
* @see EmployeeBean
* @generated
*/
public class EmployeeFacadeBean implements SessionBean {
|
Figure 9 illustrates that the remote and home interfaces
are also created for the session bean, and they contain all the methods that
have been promoted to the interface using the @ejb.interface tags.
/**
* Return an array of "Employee" value-objects from the database.
* @ejb.interface-method
* view-type="both"
* @generated
*/
public Employee[] getAllEmployeeObjects() throws FindException { |
| |
Figure 9: Session bean addition
|
An example client application
Now that you've seen how to create a Session Bean Facade, we're going to show
you in the following sections how to build a client application and use the
client programming model to use the facade. We will build a J2EE application
client and create a main class, and then use the facade to read an employee,
modify some values, and update the object.
Creating the client example
The first thing we will do is create an application client project. From the
CompanyEAR project, in the project explorer, select New - > Application
Client Project, as shown in Figure 10.
| |
Figure 10: The New -> Application Client action
|
Name the project CompanyAppClient. Click the Show Advanced button,
and verify that the settings are like those in Figure 11.
| |
Figure 11: Application Client project settings
|
Next, select a dependency from the app client module to CompanyEJBClient.jar.
Then press Finish. Locate the file Main.java -- in the default package,
it's in the appClientModule folder in the new project -- and double-click it
to open the Java editor.
Using the client factory
Next we will add methods to the Main class to create and initialize a new Employee.
Notice that as part of the code generation process, a convenience factory was
generated for creating new root SDOs. This factory is part of the SDO packages
generated in the gen/src folder of the EJB client project. As described previously,
there is one root SDO generated for each top-level SDO associated with the Session
Facade, using the value-objects attribute in the @ws.sbf.session-facade
annotation. And for each root SDO, there is one create method generated on the
client factory, as shown in Figure 12.
| |
Figure 12: The client factory interface
|
Why is this necessary? The reason is that -- as part of RMI serialization
of SDOs -- the SDOs must be contained within a DataGraph. The DataGraph contains
all the necessary code to serialize and deserialize the objects contained within.
In fact, one of the things you'll want to be aware of as you code with SDOs
is that, if you transfer one SDO, you're actually transferring all the SDOs
contained in the same DataGraph. For example, if you obtain an array of Employee
SDOs using the generated facade, and you update each employee, you only need
to apply changes once to apply changes for all the Employees. We'll discuss
this more in future articles.
Given the above background, you would need to write at least a few lines of
code for the following actions:
- Creating a new data graph
- Creating a new root object
- Creating a new employee object
- Adding the root to the datagraph
- Adding the employee object to the root
Fortunately, the tools have made this much easier with the generation of the
client factory and the APIs on the containing SDOs themselves (for example,
roots).
You can copy the code below and paste it into the editor. Then press Ctrl+Shift+O
to add the necessary import statements.
private Employee createNewEmployeeWithGraph() {
//Obtain the single instance of the client factory
SdoClientFactory factory = SdoClientFactory.INSTANCE;
//Create the root SDO used for transport. It will be associated with
//a datagraph
EmployeeRoot root = factory.createNewEmptyEmployeeRoot();
//Use the generated API on the root to create a new employee. It will automatically
//add the new object to the parent, "EmployeeRoot"
return root.createEmployee();
}
private void setValues(Employee employee) {
employee.setFirstName("Ritchie");
employee.setLastName("Schacher");
employee.setSsn(new Integer(123456789));
employee.setDateOfBirth(new Date(2000, 0, 1));
} |
As you can see from the comments in the code, the generated APIs hide some
of the details, and allow a cleaner programming model for creating new SDOs
and adding them to their containing SDOs. Thus after execution of this code
the new Employee SDO is ready for transfer.
Referencing the facade EJB
Now let's add code to lookup the facade EJB so we can use it to
program with SDOs.
Add the following instance variable and method to your Main class:
private EmployeeFacadeRemote facade;
private EmployeeFacadeRemote getFacade() {
} |
Next, place the cursor in the method body, and use the EJB snippets support
to create the EmployeeFacade bean, as shown in Figure 13.
| |
Figure 13: EJB create action in Snippets View
|
From the snippets wizard, you will need to add a new reference to the bean.
Add a return statement and lazy initialization to the method body so that it now looks like the following:
private EmployeeFacadeRemote getFacade() {
if (facade == null)
facade = createEmployeeFacadeRemote();
return facade;
} |
Using the facade interface
Next, we will use the methods above to create a new Employee, insert it using
the facade, find the employee, modify its values, and finally use the facade
to update the changes to the persistence data store.
For starters, let's hook the pieces together, to insert a new Employee
into the database. Copy the code below to your main class:
private void testNewEmployeeCreation() throws Exception {
EmployeeFacadeRemote facade = getFacade();
Employee employee = createNewEmployeeWithGraph();
setValues(employee);
facade.createEmployee(employee);
System.out.println("Employee creation successful. This was easy!!");
} |
As you can see, the above method will obtain a facade instance,
create a new employee, set the values on the employee, and use the
facade to insert the SDO. Now let's add a method below to find an
Employee, change its values, and update it:
private void testEmployeeUpdate() throws Exception {
EmployeeFacadeRemote facade = getFacade();
Employee employee = facade.getEmployeeByKey(new Integer(123456789));
if (employee == null) {
System.out.println("Employee not found :-(");
return;
}
System.out.println("Employee found. Name = "+employee.getFirstName()+"
"+employee.getLastName());
employee.setFirstName("Ritchard");
facade.updateEmployee(employee);
System.out.println("Employee has been updated.");
} |
Finally, let's tie it all together in the main method of the main
class, so it can be tested:
public static void main(String[] args) {
Main tester = new Main();
try {
tester.testNewEmployeeCreation();
tester.testEmployeeUpdate();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} |
And now you're ready. The client you just developed uses the generated facade
to create an instance of the Employee using SDOs, and also finds and updates
that instance. It is left as an exercise for you to run this on the WebSphere
Application Server version 6.0.
Application considerations
At this point in the article a brief introduction to the RAD session facade
pattern has been described, and this pattern appears similar to the J2EE core
Session Facade pattern; however, it does have some subtle differences. When
developing an application using the RAD Session Facade, you need to be aware
how the facade may impact the output.
EJB mediator
The SDO specification talks about data mediator services (DMS). A DMS
is responsible for constructing the SDOs and the DataGraph for which the SDOs
are contained. It constructs the SDOs from data that is obtained from a specific
target, such as a database. The SDO specification does not force any particular
APIs on a DMS, because how a DMS obtains its data can be very different across
different DMS implementations. For example, you may have a DMS that acquires
data from a database using SQL statements, or you may have a DMS that retrieves
data from a Web Service which takes SOAP calls.
The RAD Session Facade retrieves and updates its SDOs via an EJB mediator DMS,
included in WebSphere Application Server Version 6. The EJB mediator uses EJB
QL query statements (based on an extended syntax), and the mapping information
from the CMP entity beans to a back-end database, to dynamically generate SQL
statements to execute against the database. The RAD Session Facade will generate
these EJB QL queries for the EJB mediator based on the shape of managed SDOs.
Since the EJB mediator is using EJB QL queries, it experiences the same limitations
that would be seen generating an EJB QL finder for a CMP bean -- the biggest
limitation being that using EJB relationship roles in the key shape of the CMP
entity may not work.
Another important difference with the RAD Session Facade pattern is the fact
that the CMP persistent cache is not used when populating the SDO instances
with data. This means that the data is obtained from the database each time,
and that updates are made directly to the database (the persistent data cache
is not updated). As a user using this pattern you should be aware of the consequences
of this design pattern. This means that if you attempt to access data via both
a CMP entity and an SDO from the CMP entity directly, there is a chance you
will not get the same data. More information for dealing with this situation
can be found in the Coexistence with CMP persistence
and caching section. Even though the Session Facade pattern described
here does not have the benefit of using the persistent cache it does make up
for it in performance increases for reading and updating an entire data tree
of related SDOs, as well as the ease of querying over a collection of related
SDOs (more information regarding the query benefit will be discussed in Part
2 of this series).
Can I delete the CMP entities now?
The simple answer to this question is no. The reason you cannot delete
the CMP entity beans was touched upon in the paragraph above. The CMP entity
beans cannot be deleted since the EJB mediator will read the CMP mapping information
at runtime to dynamically generate the necessary SQL for a given EJB QL query.
Without the CMP entity beans and the mapping information, the EJB mediator will
fail to operate.
It may seem odd then to keep the CMP entity beans, since the RAD Session Facade
does not access them. Keep in mind that the CMP entity beans represent your
persistent object model, and the SDOs defined for them are just projections
and selections upon this data. Granted an SDO can contain all of the persistent
data for a CMP entity bean but it does not have to. It is also not a requirement
to expose all persistent data with an SDO. For example you may have a private
CMP entity bean with only a local client view that is accessed and updated via
a session bean. It is possible that the RAD Session Facade is updated to interact
with these private CMP entity beans using data from an SDO data graph. Even
if a CMP entity does have an SDO defined, it wouldn't necessarily make sense
to obtain the SDO when code is added to the RAD session facade to operate directly
with the data of the CMP entity bean. For example, you may decide to add logic
in your Session Facade to perform a complicated computation on a CMP entity,
and have it persist the result due to some values retrieved in an SDO data graph.
In this case it would be better to access the CMP entity bean directly.
Coexistence with CMP persistence and
caching
You need to take care when dealing with SDOs and the CMP entity beans from
which they are defined. The problem is that the SDO contains disconnected data
and the CMP entity bean contains shared, cached, container managed persistent
data. So, if you have an SDO that contains modified data and you attempt to
use the corresponding CMP entity bean, the values may not be the same. This
is especially true when dealing with the SDO and CMP entity bean within the
same transaction (in other words, within the same Session Facade method). There
are basically two ways to ensure that data does not become stale or invalid.
- Don't mix persistent access within the same transaction: for example,
within a transaction (that is, within the same Session Facade method), if
you use SDOs to update persistent data from a CMP, do not use the CMP itself
to update the data.
- Update the length of time the CMP entity bean is cached for mixed access
across transactions: for example, if you are updating data using SDOs
over DMS within one transaction and updating CMPs directly in another, then
you can change the Bean Cache setting to load at the
TRANSACTION.
Thus the CMP entity will be reloaded between transactions, and will maintain
the data integrity. This setting is updated in the EJB Deployment Descriptor
on the beans page for the specific CMP entity bean, as shown in Figure 14
below.
|
Figure 14: Setting the Bean Cache to load at TRANSACTION
|
What's next?
In Part 2 of this series, we're going to explore in more detail the complex structures that can be created using this new feature. Specifically, you will learn how to:
- Create lightweight SDOs that represent a subset of CMP entity data
- Create deep SDOs that contain associations which correspond to CMR relationships between entities
- Make incremental changes to existing defined SDOs and session facades
- Combine the contributions from multiple entity beans to a single Session Facade
- Add user-defined queries, in terms of the SDOs, to the Session Facade
So please read on, come back, and give us your feedback.
Conclusion
Having read this article, you should now be familiar with the concepts of the Value Object and Session Facade design patterns for EJB development, and the tools for generating this pattern using Rational Application Developer for WebSphere version 6.0. You've seen how to use these tools to generate the pattern, and how to build a client application using the pattern. You've seen how the pattern incorporates Service Data Objects (SDOs) for data transfer, change tracking, and persistence. You should have basic familiarity with when and how to use the Session Facade.
In future articles we will explore more advanced topics and usage patterns for this new feature, and finally, we will create an example end-to-end application that builds on all the advanced features.
Resources
About the authors  | |  | Daniel Berg is a software engineer at IBM Research Triangle Park Lab in Durham, North Carolina. He is a lead architect for J2EE and SOA/SDO tools for the Rational Application Developer team. |
 | |  | Narinder Makin is a software engineer at IBM Research Triangle Park Lab in Durham, North Carolina. He is an architect of J2EE and SOA/SDO tools for the Rational Application Developer team. |
 | |  | Ritchie Schacher is a software engineer at IBM Research Triangle Park Lab in Durham, North Carolina.. He is an architect of J2EE and SOA/SDO tools for the Rational Application Developer team. |
Rate this page
|