You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by "Shkuro, Yuri" <Yu...@gs.com> on 2001/02/16 15:26:14 UTC

Q: Design issue - application context

I wish the gurus of Struts would share their experience
with handling application context (or application-scope
resources).  One of the resources is DataSource - how do
you get access to it from the model classes?  Struts docs 
say something like "if you find youself importing javax.servlet.* 
in your business logic classes, then you're doing something 
wrong".  But in Struts framework DataSource is only available
via ActionServlet, which is not to be a part of the model.

I have one solution, which I'll explain below, but I would
appreciate any comments.

I extended the ActionServlet with my com.mycompany.struts.ActionServlet
which initializes com.mycompany.struts.Application - a singleton
class.  Application reads configuration settings from a hashtable
that ActionServlet initializes with its <init-param>s (or webapp's
<context-params>s).  Note that Application class is in a general
package, not an application-specific package.  One of the parameters
is the name of a factory class (descendent of Application) which
is in the desired application package, e.g.
com.mycompany.myapp1.Application.

What happens then is that any bean, in any package, can execute
com.mycompany.struts.Application.getInstance().getDataSource() 
to access the database.  getInstance() returns a reference to 
a singleton object of type com.mycompany.myapp1.Application.  Thus
you have a central point of reference for all application-scope
resources, and you don't need to pass it to each business logic bean.

The only problem with this approach is that I am not sure it will work
in all containers.  It is fine for a standalone app, but for webapp
it works only if each webapp is executed like a separate application,
which may be achieved by assigning a unique class loader to each webapp
(I am not sure).  Basically what it comes to is if you have a static
variable in a class, will this static variable be unique within each
webapp, or will it be the only one in the whole JVM of the container?

Re: Q: Design issue - application context

Posted by Zach Thompson <za...@mthoodmedia.com>.
Craig R. McClanahan said:
| "Shkuro, Yuri" wrote:
| 
| > I wish the gurus of Struts would share their experience
| > with handling application context (or application-scope
| > resources).  One of the resources is DataSource - how do
| > you get access to it from the model classes?  Struts docs
| > say something like "if you find youself importing javax.servlet.*
| > in your business logic classes, then you're doing something
| > wrong".  But in Struts framework DataSource is only available
| > via ActionServlet, which is not to be a part of the model.
| >
| 
| The Developer Guide section on the connection pool classes has some
| background information on using the data source, but doesn't specifically
| address your question here.
| 
| http://jakarta.apache.org/struts/api/org/apache/struts/util/package-summary.html#doc.JDBC
| 
| You can access the default connection pool either through the controller
| servlet:
| 
|     DataSource ds = servlet.findDataSource(null);
| 
| or by accesing a servlet context attribute:
| 
|     ServletContext context = servlet.getServletContext();
|     DataSource ds = (DataSource)
|       context.getAttribute(Action.DATA_SOURCE_KEY);
| 
| but this doesn't address the question of isolating business logic from the
| servlet APIs.
| 
| What I generally do (when not using EJBs) is to create model beans that
| have a save() or update() method that accepts a java.sql.Connection
| argument.  Then, you might have a "save customer" Action that does
| something like this:
| 
|     ... perform validation on the form bean ...
| 
|     CustomerModelBean cmb = ... acquire a reference ...;
|     cmb.setName(formBean.getName());
|     cmb.setXxx(...);    // Set appropriate updated properties
| 
|     DataSource ds = null;
|     Connection conn = null;
|     try {
|         ds = servlet.findDataSource();
|         conn = ds.getConnection();
|         cmb.update(conn);
|     } catch (SQLException e) {
|         ... report the problem ...
|     } finally {
|         if (conn != null) {
|             try {
|                 conn.close();
|             } catch (SQLException f) {
|                 ;
|             }
|         }
|     }
| 
| so that the CustomerModelBean does not know anything about javax.servlet.*
| (or even Struts) APIs.  Likewise, the Action class has no knowledge of what
| SQL commands are required to update the database -- the assumption is that
| a CustomerModelBean knows how to do that, and all it needs is an available
| JDBC connection to act on.
| 
| The net result is that CustomerModelBean depends only on JDBC APIs, and can
| be reused (for example) in other batch or online applications that create
| and edit customers.  Likewise, you can change the update() method to
| reflect changes in your database schema, without having to modify the web
| app to reflect this.
| 

My approach is to create a set of "Module" classes that are initialized in
a servlet when the application starts.  They can read their own config files
which can reference data source aliases that Struts creates (among other 
things).  The modules are then placed in the application context, and are
therefore available to the Action classes.  Now the Action classes can 
populate beans via ActionForms or Modules, and use the Modules to store/update
the databases.  Here's an example of part of an Action class:

    User user = (User) session.getAttribute(ModUser.USER_KEY);
    if (user == null) {
        return (mapping.findForward("logon"));
    }

    // Load the application scoped module.
    ModUser modUser = (ModUser) session.getAttribute(ModUser.APP_KEY);

    // Fill in the permissions allowed to this user.
    try {
        modUser.fillPermissions(user);
    } catch (ModUserExcpetion ex) {
        ;
    }

    // Maybe the user wants to edit their profile...

    if (user.checkPermission(ModUser.EDIT_PROFILE)) {

        // Edit away ...

        try {
            modUser.updateProfile(user);
        } catch (ModUserException ex) {
            ;
        }
    } else {
        return (mapping.findForward("denied"));
    }

You get the idea...  I'm not sure what the name of this pattern is, but it
seems similar to Context Managed EJB's.  I like it because I can group sets
of beans together in a module, and keep the beans very bare bones (possibly
allowing them to be auto-generated...), and most of the business logic is 
contained within the module, which doesn't have to know that it's running in a 
servlet context.

Zach

| > [Cut the rest to keep the msg short...]

Re: Q: Design issue - application context

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
"Shkuro, Yuri" wrote:

> I wish the gurus of Struts would share their experience
> with handling application context (or application-scope
> resources).  One of the resources is DataSource - how do
> you get access to it from the model classes?  Struts docs
> say something like "if you find youself importing javax.servlet.*
> in your business logic classes, then you're doing something
> wrong".  But in Struts framework DataSource is only available
> via ActionServlet, which is not to be a part of the model.
>

The Developer Guide section on the connection pool classes has some
background information on using the data source, but doesn't specifically
address your question here.

http://jakarta.apache.org/struts/api/org/apache/struts/util/package-summary.html#doc.JDBC

You can access the default connection pool either through the controller
servlet:

    DataSource ds = servlet.findDataSource(null);

or by accesing a servlet context attribute:

    ServletContext context = servlet.getServletContext();
    DataSource ds = (DataSource)
      context.getAttribute(Action.DATA_SOURCE_KEY);

but this doesn't address the question of isolating business logic from the
servlet APIs.

What I generally do (when not using EJBs) is to create model beans that
have a save() or update() method that accepts a java.sql.Connection
argument.  Then, you might have a "save customer" Action that does
something like this:

    ... perform validation on the form bean ...

    CustomerModelBean cmb = ... acquire a reference ...;
    cmb.setName(formBean.getName());
    cmb.setXxx(...);    // Set appropriate updated properties

    DataSource ds = null;
    Connection conn = null;
    try {
        ds = servlet.findDataSource();
        conn = ds.getConnection();
        cmb.update(conn);
    } catch (SQLException e) {
        ... report the problem ...
    } finally {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException f) {
                ;
            }
        }
    }

so that the CustomerModelBean does not know anything about javax.servlet.*
(or even Struts) APIs.  Likewise, the Action class has no knowledge of what
SQL commands are required to update the database -- the assumption is that
a CustomerModelBean knows how to do that, and all it needs is an available
JDBC connection to act on.

The net result is that CustomerModelBean depends only on JDBC APIs, and can
be reused (for example) in other batch or online applications that create
and edit customers.  Likewise, you can change the update() method to
reflect changes in your database schema, without having to modify the web
app to reflect this.

>
> I have one solution, which I'll explain below, but I would
> appreciate any comments.
>
> I extended the ActionServlet with my com.mycompany.struts.ActionServlet
> which initializes com.mycompany.struts.Application - a singleton
> class.  Application reads configuration settings from a hashtable
> that ActionServlet initializes with its <init-param>s (or webapp's
> <context-params>s).  Note that Application class is in a general
> package, not an application-specific package.  One of the parameters
> is the name of a factory class (descendent of Application) which
> is in the desired application package, e.g.
> com.mycompany.myapp1.Application.
>
> What happens then is that any bean, in any package, can execute
> com.mycompany.struts.Application.getInstance().getDataSource()
> to access the database.  getInstance() returns a reference to
> a singleton object of type com.mycompany.myapp1.Application.  Thus
> you have a central point of reference for all application-scope
> resources, and you don't need to pass it to each business logic bean.
>
> The only problem with this approach is that I am not sure it will work
> in all containers.  It is fine for a standalone app, but for webapp
> it works only if each webapp is executed like a separate application,
> which may be achieved by assigning a unique class loader to each webapp
> (I am not sure).  Basically what it comes to is if you have a static
> variable in a class, will this static variable be unique within each
> webapp, or will it be the only one in the whole JVM of the container?

As long as you are running in a modern servlet container (extremely likely,
since Struts requires servlet 2.2 and JSP 1.1), this should still work.
The reason it's OK is that each web application is loaded by a separate
class loader, and static variables (such as singletons) are unique per
class loader, rather than per JVM.

I have a personal dislike for statics and singleton patterns (having been
burned way too many times in past projects by arbitrary changes to global
data), but that's a personal style preference -- your technique is
certainly technically sound.

Craig