Programming Jakarta Struts, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Jakarta Struts, 2nd Edition [Electronic resources] - نسخه متنی

Chuck Cavaness

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








13.1 Implementing the Storefront Service Using EJB


Even though this chapter is specific to EJB, the intent is still to
keep the focus on Struts. With that in mind, the discussion of EJB
implementation details will be kept to a minimum. EJB is a complex
topic, but the nature of several design patterns geared toward the
interaction between EJBs and their clients makes this an easier task
than you might first think. After all, an overriding goal of this
chapter is to demonstrate how to design an application so that your
Struts classes aren't impacted by the choice to use
an EJB implementation of the model. You already have a head start on
some of the central issues here, having seen how the model component
of a web application can be hidden behind a service interface. In
particular, you've seen through the Storefront
example how easy it is to swap a debug model with a full
implementation that accesses a database when this design approach is
followed.

Throughout this chapter, the Storefront example will be used to
illustrate how an EJB application tier can be used with a Struts
application. If it weren't for the remote nature of
accessing an EJB from a client application, this implementation
choice wouldn't make any difference to you. However,
the distributed aspects of EJB must be taken into account when your
web-tier classes access this type of model. What
you'll see in the remainder of this chapter are some
recommendations on how best to implement the code needed to interface
with the application tier. Key to this discussion is an approach for
isolating the code that handles what's unique about
EJB so that your action classes aren't affected.


13.1.1 A Quick EJB Overview


The
EJB specification defines three types of beans: entity, session, and
message- driven. Each type of bean has a different purpose within an
EJB application.

Entity beans
provide transactional access to persistent data and are most often
used to represent rows stored in one or more related tables in a
database. For example, you might implement
CustomerBean, ItemBean, and
OrderBean entity classes, among others, to support
the Storefront application. These entity beans would incorporate the
functionality provided by the corresponding business object classes
in the example application. When entity beans are used, they take on
the primary role of supplying the application model. They are
expected to provide both the required data persistence operations and
the business logic that governs the data they represent. Because the
model should be reusable across applications in an enterprise, entity
beans need to be independent of the types of clients that ultimately
access the application tier.

Session beans are
often described as extensions of the client applications they serve.
They can implement business logic themselves, but more often their
role is to coordinate the work of other classes (entity beans, in
particular). They are more closely tied to their clients, so the
precaution of keeping entity beans reusable doesn't
apply to session beans to the same extent. The application tier is
primarily considered to be the application model, but session beans
are also referred to as
"controllers" because of the
coordination they do. This is especially true when session-bean
methods are used to implement transactions that touch multiple
business objects.


Session beans can be implemented as either stateless or stateful. A
stateless session
bean maintains no state specific to its client, so a single instance
can efficiently be shared among many clients by the EJB container. A
stateful bean instance, on the other hand, is
assigned to a specific client so that state can be maintained across
multiple calls. Holding state in the application tier can simplify
the client application logic, but it makes it more difficult to scale
to a large number of clients. Typical web applications maintain their
client state in the user session, and possibly the database, instead
of making use of stateful session beans. For this reason,
we'll focus on stateless beans for our examples
here.

The EJB 2.0 specification added message-driven
beans
as the third bean type so that EJB could be
integrated with the Java Message Service (JMS). Message-driven
beans differ from the other two types in that they respond
asynchronously to requests instead of being called directly by a
client application. The container invokes a message-driven bean
whenever a JMS message that meets the selection criteria of the bean
is received. For example, in a more complex version of the Storefront
application, a message-driven bean could be used to respond to a
notification that an item is being backordered. The bean could be
responsible for emailing any customers that had orders already in
place for the item. Message-driven beans have no direct interaction
with client applications, so they are even less dependent on the
client than entity beans are.


13.1.2 The Session Façade


The first step in designing an interface to the application tier is
to identify the entry points exposed to a client application.
Message-driven beans aren't called directly, so they
don't come into play here. However, a typical EJB
application can include a number of session and entity beans. As
already pointed out, standard practice is to insulate entity beans
from the details of the client so that they can be reused in other
applications. If your Struts actions were to interact with
entity beans directly, the web tier
would quickly become coupled to the object model implemented by the
application tier. This tight coupling, combined
with the distributed nature of EJB, would lead to a number of issues:

  • Changes in the EJB object model would require corresponding changes
    in the web tier.

  • Action classes often would be required to execute multiple remote
    calls to the application server to satisfy the business logic needs
    of a request.

  • The business logic and transaction-management code needed to
    orchestrate multiple calls would have to exist in the web tier.


To avoid these issues, the interface exposed to the clients of the
application tier is almost always limited to session beans. This
approach is commonly referred to as either
"session wraps entity" or
a "session
fa¸ade." There are quite a few advantages
to this design. When a client application makes a single call to a
session-bean method to perform a required operation, the session bean
can easily execute the request as a single transaction, and the
implementation details can be hidden. This session-bean method might
need to access a number of entity beans, or even other session beans,
to satisfy the request. No matter what the flow of control is on the
application server, the complexity is hidden from the client. Because
session beans become the only clients of the entity beans when using
a session façade, there's little chance
of the entities becoming tied to any particular type of external
client.


Even though the discussion here assumes that the business objects are
implemented as entity beans, this doesn't have to be
the case in an EJB application. The same concerns and advantages that
support using a session façade apply when other
implementations are used. Just as some Java developers
don't like EJB, not all EJB developers like entity
beans. Because the session façade hides the object model
from the client, entity beans can be replaced with another approach,
such as Java data objects (JDOs) or regular data access objects
(DAOs), without impacting the interface exposed to the client.


13.1.3 The Business Interface


When using a session
façade, you must first define the details of the interface
between the web and application tiers. You might have some questions
about which side should be the primary driver of this contract
between the two tiers. Early in the development of the Storefront
example, the IStoreFrontService interface was
introduced to define the application-tier functionality required to
support the presentation needs of the application. In particular, the
presentation layer relies on the supporting service to authenticate
users and to provide product descriptions needed to build the online
catalog. Taking a user-centered view of the application,
it's easy to see how the service-layer requirements
can be driven by the web tier. After all, if a certain view is to be
built, the model functionality to support it has to exist. However,
one of the main reasons to implement an enterprise's
business logic within EJBs is to allow those business rules and the
supporting data to be used to across multiple applications. This
includes providing support for clients other than those associated
with web applications. This isn't a problem, because
the division of responsibility between session and entity beans helps
to protect the reusability of business logic.

Because session beans are treated as extensions of the clients they
serve, there's nothing wrong with defining a session
façade that's specific to a particular
client application. As long as your entity and message-driven beans
remain independent of the client, it's reasonable to
implement a session-bean interface in response to the requirements
set forth by a specific client. If multiple client views of the model
are required, multiple façades can be implemented to
support them.

The façade presented by a session bean can support either
local or remote clients. Local clients of a session or entity bean
can only be other EJBs deployed within the same container. These
clients are tightly coupled to the beans they access, but they offer
performance advantages when calls need to be made between EJBs. This
is because these method calls use pass-by-reference semantics instead
of being treated as remote calls between distributed components.
Session beans are often local clients of entity beans, but
it's less common for them to have local clients of
their own to support. Our web-tier components obviously
aren't EJBs running within the application tier, so
we care only about remote clients for our purposes here. What we need
to do, then, is define the remote interface for our
session façade.

Every session bean that supports remote clients must have a
corresponding remote interface that extends the
javax.ejb.EJBObject
interface. This interface determines the business methods that are
exposed by the session bean. It might seem strange, but
you'll almost never see a method explicitly declared
in a remote interface. This is because of an EJB design pattern known
as the business interface.


In addition to its remote interface, a session bean supporting remote
clients must have a home interface that extends
javax.ejb.EJBHome, an implementation class, and
one or more XML deployment descriptors. Unlike the remote interface,
which declares the bean's business methods, the home
interface defines factory-like methods for creating session-bean
references. By definition, the bean's methods are
implemented by the implementation class. The deployment descriptors
identify and configure a bean for use within a particular EJB
container. We'll define each piece for our example
as we go along.

When you think of a class and an interface with which
it's associated, you would normally expect that the
class would explicitly implement that interface. This
isn't true with remote (or local) interfaces in EJB.
Instead, the container creates an intermediate object (often referred
to as an EJBObject) to implement the remote interface. This object
intercepts calls made by clients of the bean, then delegates them to
the implementation class after performing any operations (such as
security or transaction management) that might be required. Instead
of the Java compiler verifying that the bean class implements each of
the business methods declared by the remote interface, that
responsibility falls to the deployment tools provided by the
container. Even a bean class that compiles without any errors will
fail at deployment if there's a mismatch between it
and its remote interface.

If you declared a session bean to implement its remote interface,
you'd be guaranteed that the compiler would catch
any problems with its business-method declarations. The problem is
that you'd also have to provide dummy
implementations of the non-business methods declared by
javax.ejb.EJBObject. These methods would never be
called (they're called only on the intermediate
object created by the container), but they would have to be there to
satisfy the compiler. Instead of taking this approach, most EJB
developers create a second interface, known as the business
interface, that declares the business methods that need to be
exposed. Declaring the remote interface to extend this interface and
the bean class to implement it exposes the required methods, so the
compiler can verify that the bean implements them. This pattern
provides a convenient starting point for defining our client access
mechanism.


The use of a business interface also prevents programmers from
accidentally passing or returning a this
reference to an instance of a bean class that has been declared to
implement its remote interface. This topic is beyond the scope of
this book, but the short explanation is that the EJB container can
manage bean instances properly only when they're
referred to using only their remote (or local) interfaces. A bean
reference can't be returned in place of its remote
interface if the bean class implements only its business interface.

Returning to the IStorefrontService interface that
eventually must be satisfied by our implementation, recall that it
contains methods related to both user authentication and the product
catalog. Even when using a session façade, you would
likely separate these responsibilities into separate session beans.
This would reduce the complexity of the session beans involved and
simplify their maintenance. However, given that EJB design
isn't our focus here, our first simplification will
be to assume that our façade will consist of a single
Storefront session bean. You probably
wouldn't do this in a real application, but once you
know how to interface with a single session bean, applying the same
technique to multiple session beans is straightforward. A suitable
business interface for the Storefront session bean
is shown in Example 13-1.


Example 13-1. The business interface for the Storefront session bean

package com.oreilly.struts.storefront.service;
import java.rmi.RemoteException;
import java.util.List;
import com.oreilly.struts.storefront.catalog.view.ItemDetailView;
import com.oreilly.struts.storefront.customer.view.UserView;
import com.oreilly.struts.storefront.framework.exceptions.*;
/**
* The business interface for the Storefront session bean
*/
public interface IStorefront {
public UserView authenticate( String email, String password )
throws InvalidLoginException, ExpiredPasswordException,
AccountLockedException, DatastoreException, RemoteException;
public List getFeaturedItems( ) throws DatastoreException, RemoteException;
public ItemDetailView getItemDetailView( String itemId )
throws DatastoreException, RemoteException;
}

The first thing to notice about the IStorefront
interface is that its methods don't exactly match
those declared by IStorefrontService. First of
all, our business interface doesn't include the
logout() and destroy()
methods found in the service interface. The reason for this is that
those methods represent web-tier functionality, not true business
logic that needs to move to the application tier. Also, every method
in IStorefront is declared to throw
RemoteException, which is not part of the
declarations in IStorefrontService. All business
methods exposed to a remote client of an EJB must be declared to
throw RemoteException, which is used to report
communication failures specific to remote method execution. This is
the one aspect of a remote interface that can't be
hidden by the business interface. Without this restriction, this
interface could be made to look very much like our service interface.
Once we cover how our example session bean will be implemented,
we'll discuss how these mismatches between the
interfaces can be handled.

It's also important to notice that our business
interface is referencing the view classes already created to support
the service interface. The same Data Transfer Object (DTO) pattern
introduced in Chapter 7 applies to an EJB-based
model. Instead of exposing the actual business object implementation
classes or many fine-grained methods to access their properties, you
can use simple JavaBean classes to communicate the state of the model
to the client. We declared our BaseView superclass
to be Serializable so that the view classes could
be referenced in a remote interface. DTO classes tend to consist of
simple data types, so this constraint should be a minor one.

With our business interface defined, Example 13-2
shows the trivial remote interface
declaration we'll need to eventually deploy our
session bean.


Example 13-2. The remote interface for the Storefront session bean

package com.oreilly.struts.storefront.service;
import javax.ejb.EJBObject;
public interface Storefront extends EJBObject, IStorefront {
/**
* The remote interface for the Storefront session bean. All methods are
* declared in the IStorefront business interface.
*/
}


13.1.4 Stateless Session Bean Implementation


Without getting into anything
too elaborate, we next want to come up with an implementation of our
session façade. We'll make a few
decisions here to simplify things, but the result will be all we need
to illustrate how to interface the web and application tiers. It will
also be good enough for you to deploy in an EJB container and use to
test your Struts interface.

If you were given the task of implementing the application tier of
the Storefront application using EJB, you probably would produce a
design consisting of both session and entity beans. You could
represent the model components using entity beans, and
you'd likely have a number of session beans to
provide the functionality for security, catalog, and order
operations. The session beans would provide the workflow
functionality required from process business objects, and the entity
beans would serve as the corresponding entity business objects.

We've already made the decision to use only a single
session bean for the example. The session façade makes our
next simplification easy as well. Because we've
isolated the interface between our two tiers into a
façade, any division of responsibilities between session
and entity beans is of no concern to us as Struts developers. The web
tier sees only session-bean methods and DTO classes, so nothing else
about the implementation will affect the web components. Given that,
we'll implement our façade using a single
stateless session bean that does not require any other EJBs.


If you're starting with an EJB implementation that
includes entity beans, you might want to use XDoclet (available from
http://www.sourceforge.net/projects/xdoclet/) to
automatically generate Struts ActionForms from
these beans. For more complex EJB implementations than what
we're looking at here,
XDoclet also
provides an automated means of generating the various interfaces and
deployment descriptors required for a bean. This code generation is
performed based on special JavaDoc tags that you include in your EJB
implementation classes.

Because we're not using entity beans, we can make
use of the same ORM approach and entity business object classes
already used by the StorefrontServiceImpl class.
In fact, our implementation will look very much like that class, with
the exception of the callback methods required by the javax.ejb.SessionBean
interface. This is shown in Example 13-3.


Example 13-3. The Storefront session bean

package com.oreilly.struts.storefront.service;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import org.odmg.*;
import ojb.odmg.*;
import com.oreilly.struts.storefront.businessobjects.CustomerBO;
import com.oreilly.struts.storefront.businessobjects.ItemBO;
import com.oreilly.struts.storefront.catalog.view.ItemDetailView;
import com.oreilly.struts.storefront.catalog.view.ItemSummaryView;
import com.oreilly.struts.storefront.customer.view.UserView;
import com.oreilly.struts.storefront.framework.exceptions.AccountLockedException;
import com.oreilly.struts.storefront.framework.exceptions.DatastoreException;
import com.oreilly.struts.storefront.framework.exceptions.ExpiredPasswordException;
import com.oreilly.struts.storefront.framework.exceptions.InvalidLoginException;
/**
* This is a simple Session Bean implementation of the Storefront service
*/
public class StorefrontBean implements SessionBean, IStorefront {
private SessionContext ctx;
private Implementation odmg = null;
private Database db = null;
public UserView authenticate( String email, String password )
throws InvalidLoginException, ExpiredPasswordException,
AccountLockedException, DatastoreException {
// Query the database for a user that matches the credentials
List results = null;
try{
OQLQuery query = odmg.newOQLQuery( );
// Set the OQL select statement
String queryStr = "select customer from " + CustomerBO.class.getName( );
queryStr += " where email = $1 and password = $2";
query.create(queryStr);
// Bind the input parameters
query.bind( email );
query.bind( password );
// Retrieve the results
results = (List)query.execute( );
}catch( Exception ex ){
ex.printStackTrace( );
throw DatastoreException.datastoreError(ex);
}
// If no results were found, must be an invalid login attempt
if ( results.isEmpty( ) ){
throw new InvalidLoginException( );
}
// Should only be a single customer that matches the parameters
CustomerBO customer = (CustomerBO)results.get(0);
// Make sure the account is not locked
String accountStatusCode = customer.getAccountStatus( );
if ( accountStatusCode != null && accountStatusCode.equals( "L" ) ){
throw new AccountLockedException( );
}
// Populate the value object from the Customer business object
UserView userView = new UserView( );
userView.setId( customer.getId( ).toString( ) );
userView.setFirstName( customer.getFirstName( ) );
userView.setLastName( customer.getLastName( ) );
userView.setEmailAddress( customer.getEmail( ) );
userView.setCreditStatus( customer.getCreditStatus( ) );
return userView;
}
public List getFeaturedItems( ) throws DatastoreException {
List results = null;
try{
OQLQuery query = odmg.newOQLQuery( );
// Set the OQL select statement
query.create( "select featuredItems from " + ItemBO.class.getName( ) );
results = (List)query.execute( );
}catch( Exception ex ){
ex.printStackTrace( );
throw DatastoreException.datastoreError(ex);
}
List items = new ArrayList( );
Iterator iter = results.iterator( );
while (iter.hasNext( )){
ItemBO itemBO = (ItemBO)iter.next( );
ItemSummaryView newView = new ItemSummaryView( );
newView.setId( itemBO.getId( ).toString( ) );
newView.setName( itemBO.getDisplayLabel( ) );
newView.setUnitPrice( itemBO.getBasePrice( ) );
newView.setSmallImageURL( itemBO.getSmallImageURL( ) );
newView.setProductFeature( itemBO.getFeature1( ) );
items.add( newView );
}
return items;
}
public ItemDetailView getItemDetailView( String itemId )
throws DatastoreException {
List results = null;
try{
OQLQuery query = odmg.newOQLQuery( );
// Set the OQL select statement
String queryStr = "select item from " + ItemBO.class.getName( );
queryStr += " where id = $1";
query.create(queryStr);
query.bind(itemId);
// Execute the query
results = (List)query.execute( );
}catch( Exception ex ){
ex.printStackTrace( );
throw DatastoreException.datastoreError(ex);
}
//
if (results.isEmpty( ) ){
throw DatastoreException.objectNotFound( );
}
ItemBO itemBO = (ItemBO)results.get(0);
// Build a ValueObject for the Item
ItemDetailView view = new ItemDetailView( );
view.setId( itemBO.getId( ).toString( ) );
view.setDescription( itemBO.getDescription( ) );
view.setLargeImageURL( itemBO.getLargeImageURL( ) );
view.setName( itemBO.getDisplayLabel( ) );
view.setProductFeature( itemBO.getFeature1( ) );
view.setUnitPrice( itemBO.getBasePrice( ) );
view.setTimeCreated( new Timestamp(System.currentTimeMillis( ) ));
view.setModelNumber( itemBO.getModelNumber( ) );
return view;
}
/**
* Opens the database and prepares it for transactions.
*/
private void init( ) throws DatastoreException {
// Get odmg facade instance
odmg = OJB.getInstance( );
db = odmg.newDatabase( );
// Open database
try{
db.open("repository.xml", Database.OPEN_READ_WRITE);
}catch( ODMGException ex ){
throw DatastoreException.datastoreError(ex);
}
}
public void ejbCreate( ) throws CreateException {
try {
init( );
}catch ( DatastoreException e ) {
throw new CreateException(e.getMessage( ));
}
}
public void ejbRemove( ) {
try {
if (db != null) {
db.close( );
}
}catch ( ODMGException e ) {}
}
public void setSessionContext( SessionContext assignedContext ) {
ctx = assignedContext;
}
public void ejbActivate( ) {
// Nothing to do for a stateless bean
}
public void ejbPassivate( ) {
// Nothing to do for a stateless bean
}
}

In our StorefrontBean class, the business method
implementations are unchanged from the
StorefrontServiceImpl versions. Only the
management of the database connection needed to be modified. Whenever
the EJB container creates a new instance of this bean, the
ejbCreate() callback
method is invoked and a database connection is established. This
connection is closed in the corresponding
ejbRemove() method
that is called prior to the instance being destroyed. The container
never passivates stateless session beans, so do-nothing
implementations are supplied for the ejbPassivate(
)
and ejbActivate() methods of the
SessionBean interface. If we needed more than one
session bean in our example, we'd move these two
methods into an adapter class and extend all our concrete
implementation classes from it.


A more correct EJB approach would be to open the database connection
using a javax.sql.DataSource
connection factory obtained from a JNDI lookup. This allows the
container to manage connection pooling and transaction enlistment for
you automatically. Again, this doesn't affect our
interface, so we can continue to use this simple implementation.

Our session bean now has a remote interface and an implementation
class. That leaves the home interface, which is always simple in the
case of a stateless session bean. All we need is a single
create() method, as shown in Example 13-4.


Example 13-4. The home interface for the Storefront session bean

package com.oreilly.struts.storefront.service;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
/**
* The home interface for the Storefront session bean.
*/
public interface StorefrontHome extends EJBHome {
public Storefront create( ) throws CreateException, RemoteException;
}


13.1.5 JBoss Deployment


We need to select an EJB container and create the required XML
deployment descriptors before we can deploy and use our session bean.
The open source
JBoss application server fits our
requirements perfectly. This full-featured J2EE implementation,
complete with EJB support, is a favorite among open source
developers. You can download the software for free from
http://www.jboss.org.

With our minimal implementation, we don't need
anything complicated as far as deployment information for our session
bean. Example 13-5 shows the standard
ejb- jar.xml
descriptor for our bean. For the most part, this file simply
identifies the home and remote interfaces and the implementation
class. It also declares that all of the business methods are
nontransactional (because they're read-only
methods).


Example 13-5. The ejb-jar.xml deployment descriptor for the Storefront session bean

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0// EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar >
<description>
Generic deployment information for the Storefront session bean
</description>
<display-name>Storefront Session Bean</display-name>
<enterprise-beans>
<session >
<ejb-name>Storefront</ejb-name>
<home>com.oreilly.struts.storefront.service.StorefrontHome</home>
<remote>com.oreilly.struts.storefront.service.Storefront</remote>
<ejb-class>com.oreilly.struts.storefront.service.StorefrontBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor >
<container-transaction >
<method >
<ejb-name>Storefront</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

In addition to the ejb-jar.xml file, most
containers require one or more vendor-specific descriptors as part of
a bean's deployment information. In this case, all
we need to do is associate a JNDI name with our bean. Example 13-6 shows how this is done with JBoss. The fully
qualified name of the bean's remote interface was
chosen as the JNDI name. It's also common to use the
home interface name.


Example 13-6. The JBoss deployment descriptor for the Storefront session bean

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS//EN"
"http://www.jboss.org/j2ee/dtd/jboss.dtd">
<jboss>
<enterprise-beans>
<session>
<ejb-name>Storefront</ejb-name>
<jndi-name>com.oreilly.struts.storefront.service.Storefront</jndi-name>
</session>
</enterprise-beans>
</jboss>

Deployment of an EJB requires packaging it into a Java archive (JAR)
file. The deployment JAR file for our session bean needs to
include the following files:

  • The home and remote interface class files

  • The bean implementation class file

  • The two deployment descriptors (these files must be placed in a
    META-INF directory)

  • The OJB.properties file and the various
    repository XML files used by the ORM framework

  • The business object and DTO class files referenced by the
    Storefront bean


Once you've created this JAR file, you can deploy
the bean by copying the file to the
server/default/deploy directory underneath your
JBoss installation. You can place the JAR files for your JDBC driver
and the OJB classes in the server/default/lib
directory. At this point, you can start JBoss and verify that you
have everything in place to execute the application tier.


    / 181