1) The relationship is a starting point.  Basically, I would not pass an
ActionForm to my business delegate (RentalBean).  Doing so ties that
delegate to the web interface.  If you were wanting to create a Swing client
in the future, you would have to create seperate business delegates for it.
Try to abstract your business delegates from your view ( see code below ).

2) Use a constructor that requires no params, but provide 'public void
init(...)' params to initialize the state of your bean.

3) Depends on the scope of the application.  I usually provide several
different applications, all depending on the business cases (admin app, web
user app, swing app, web services for b2b,...).

4) Start off with a base data access class that has general methods.  The
methods should provide a way to create jdbc connections and close
connections, statements, and result sets.  You can also have query builder
methods in this class that don't tie it to any single use case.


If you are wanting the application to scale upto EJB in the future, you are
best to use delegate objects like you are doing (RentalBean).

If you are wanting to use an Action class for every use case ( adding a
rental, editing a rental, viewing a rental, deleting a rental), then you
don't need to the action property .  If you are wanting to use one class,
then use the 'action' property in the request, this is how id did it below.

The data access object (RentalDAO) should be an interface.  You should use a
factory that uses a JNDI value to select the DAO object.  This allows you to
change databases by just modifing a value in the web.xml file and
redeploying your app.  I wont go into this, but check the java petstore app
(1.2.1) for examples.

With the following code, only the BaseAction, RentalForm, and RentalAction
are tied to the web client and struts.  The business delegate
(RentalDelegate) and the data access object(s) (BaseDAO and RentalDAO) can
now be used with other clients (web, wap, swing,...).

Lets rewrite some of the pseudo-code you sent:

-- Code for BaseAction.java --
public abstract class BaseAction extends Action {
   //Don't use class variables in Action objects.  These are not thread
safe.
   //protected String action=null; 

   //provide this method just in case any of your subclassing actions want
to do some pre-initialization processing.
   public ActionForward prePerform(ActionMapping mapping,
                                    ActionForm form,
                                    HttpServletRequest request,
                                    HttpServletResponse response)
                                   throws IOException, ServletException {

       //could do custom authentication or something else.

       //base implementation returns null.
       return null;
   } 
   
   //this is good.  I do this on all of my projects.  Also got the idea from
Ted Husted and a few others on the list.
   public abstract ActionForward performAction(ActionMapping mapping,
                                    ActionForm form,
                                    HttpServletRequest request,
                                    HttpServletResponse response)
                                   throws IOException, ServletException;

   
   //proxy method
   public ActionForward perform(ActionMapping mapping,
                            ActionForm form,
                            HttpServletRequest request,
                            HttpServletResponse response)
                           throws IOException, ServletException {

       //WE CANT DO THIS BECAUSE Action OBJECTS ARE NOT THREAD SAFE.
       //Find out what we are doing (Create,Edit,Save,Reset, Delete, etc.)
       //action=request.getParameter("action");  
       //if (action == null)
       //  action=mapping.getParameter();


       //catch all uncaught exceptions.  Provide global error handling.
       try {
           // do pre-processing
           ActionForward forward =
prePerform(mapping,form,request,response);
           
           //if the pre-processing returned a forward, then forward
           if(forward != null) {
               return forward;
           }

           forward = performAction(mapping,form,request,response);
  
           if(forward != null) {
               return forward;
           } else {
               //return an ActionForward to the main page
           }
        } catch(Exception e) {
           //do something with the exception
        }
    }
}

-- code for RentalAction.java --
public class RentalAction extends BaseAction {
    public static final String CREATE = "create";
    public static final String UPDATE = "update";
    public static final String VIEW = "view";
    public static final String VIEWALL = "viewall";
    public static final String DELETE = "delete";

    public ActionForward performAction(ActionMapping mapping,
                                       ActionForm form,
                                       HttpServletRequest request,
                                       HttpServletResponse response)
                                       throws IOException, ServletException
{
        String action = request.getParameter("action");

        if(action == null || action.equals("")) {
            //list all actions
            return viewAll(mapping,form,request,response);
        } else if(action.equals(UPDATE)) {
            return update(mapping,form,request,response);
        } else if(action.equals(DELETE)) {
            return delete(mapping,form,request,response);
        } else if(action.equals(CREATE)) {
            return create(mapping,form,request,response);
        } else if(action.equals(VIEW)) {
            return view(mapping,form,request,response);        
        } else {
            return viewAll(mapping,form,request,response);
        }
    }

    protected ActionForward create(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response)
                                   throws IOException, ServletException {
        //add the stuff using the RentalDelegate object
        RentalDelegate rd = new RentalDelegate();
    
        //cast the ActionForm to RentalForm
        RentalForm rf = (RentalForm)form;
        rd.create(rf.getCity(),rf.getRooms());

        //return an action forward to the success page
        return mapping.findForward("createsuccess");
    }

    //... do the same for the other methods
}

-- code for RentalDelegate.java --
// Other clients can now use this business delegate.  Cuts down on the
maintence of the entire application.
public class RentalDelegate {
    public RentalDelegate() {
    }

    public void create(String city, int rooms) {
        RentalDAO dao = new RentalDAO();
        dao.create(city,rooms);
    }

    //...other Rental methods..update,view,viewall,delete
}


-- code for BaseDAO.java --
public abstract class BaseDAO {
    protected Connection getConnection() {
        //....
    }

    protected void close(ResultSet rs, Statement stmt, Connection con) {
        closeResultSet(rs);
        closeStatement(stmt);
        closeConnection(con);
    }

    protected void closeResultSet(ResultSet rs) {
        try {
            if(rs != null) {
                rs.close();
            }
        } catch(Exception e) {
        }
    }

    protected void closeStatement(Statement stmt) {
        try {
            if(stmt != null) {
                stmt.close();
            }
        } catch(Exception e) {
        }
    }

    protected void closeConnection(Connection con) {
        try {
            if(con != null) {
                con.close();
            }
        } catch(Exception e) {
        }
    }
}
-- code for RentalDAO.java --
public class RentalDAO extends BaseDAO {
    public RentalDAO() {
    }

    public void create(String city, int rooms) {
         //add the stuff to the database
    }

    //... other methods to persist rental data.
}



If you need more help, just email me.  If you need a consultant, let me
know.

James Hicks

-----Original Message-----
From: Naphtali Visser [mailto:[EMAIL PROTECTED]]
Sent: Thursday, November 29, 2001 3:40 PM
To: [EMAIL PROTECTED]
Subject: Design Help


Hi there... I'm a Java/Struts newbie, but a web developer oldie (Perl, ASP, 
Cold Fusion, blah blah blah).  This is my first attempt.  If someone could 
look at this design and give me some comments, I'd appreciate it.

Excuse the pseudo-code or comments-instead-of-code in some places.

Here are some specific questions (also look for "QUERY:" in the code [there 
are lots of them])

First, the purpose of this application is to manage a real estate office's 
rental and sales listings.  So, for every class here, there will be an 
analogous Sales class, since a sales listing is described much differently 
than a rental listing.  Any opinions on that would be helpful too.


1) Is the relationship between RentalAdd (action), RentalBean (the logic 
bean), and DataAccess (a data access object) correct?  Specifically, does 
it adhere to MVC conventions, and is it a good design if I want to be able 
to switch databases or user EJBs in the future?

2) Do I need the constructor in RentalBean?  What is the easiest way to do 
what Im trying to do there?

3) Should the entire application go in the same package?

4) If my DataAccess/Rental Bean model is correct, then should all database 
specific stuff for the whole application go into my DataAccess class or 
should I have  a RentalDataAccess and a SalesDataAccess, etc.?


ANY HELP IS GREATLY APPRECIATED!  IF ANYONE REALLY KNOWS THIS STUFF AND 
WANTS TO HELP ME ON A CONSULTATIVE BASIS, EMAIL ME!

--Naf


package com.nafster.realestate;

public class RentalForm extends ActionForm{
   private String city=null;
   private int rooms=null;

   //setters and getters for member vars go here

   // validate domain of form data
   public ActionErrors validate(ActionMapping mapping, HttpServletRequest 
request) {
           ActionErrors errors = new ActionErrors();
           if (city == null)
             errors.add("","");
           if (rooms == null)
             errors.add("","");
           if (rooms < 0)
             errors.add("","");
           return errors;
   }

   public void reset(ActionMapping mapping, HttpServletRequest request){
     city=null;
     rooms=null;
   }
}

class BaseAction extends Action{  //  QUERY: This was a suggestion by Ted 
Husted.  Is this logical?
   protected String action=null;

   public abstract ActionForward performAction(ActionMapping mapping,
                                    ActionForm form,
                                    HttpServletRequest request,
                                    HttpServletResponse response)
                                   throws IOException, ServletException;

   public ActionForward perform(ActionMapping mapping,
                            ActionForm form,
                            HttpServletRequest request,
                            HttpServletResponse response)
                           throws IOException, ServletException {
           //Find out what we are doing (Create,Edit,Save,Reset, Delete,
etc.)
           action=request.getParameter("action");  // This is accessed by 
subclasses primariliy
           if (action == null)
             action=mapping.getParameter();

           return(performAction(mapping, form, request, response));
   }
}





public class RentalAdd extends BaseAction{
   /*
    This Action is responsible for Creating a new Rental Record or Editting 
an Existing one
    Possible parameters (from Struts) are:
           * Create: Generate a blank form
           * Edit: Generate a form with existing values
           * Save: INSERT or UPDATE the database
   */

   public ActionForward performAction(ActionMapping mapping,
                            ActionForm form,
                            HttpServletRequest request,
                            HttpServletResponse response)
                           throws IOException, ServletException {

           // "action" comes from BaseAction
           if (action.equals("create")){
                   // TODO: Forward to a blank form
           }
           else if (action.equals("edit")){  // QUERY: Am I thinking too 
much like a procedural developer?
                   // Get the ID of the one we want to edit
                   int id=request.getParameter("id");

                   // Create a new RentalBean, populate it
                   RentalBean rentalBean=new RentalBean();
                   rentalBean.get(id);  // QUERY: Make sense to abstract 
like this?

                   //TODO: Populate RentalForm with Contents of RentalBean
                   RentalForm rentalForm= (RentalForm) form;
           }
           else if (action.equals("save")){
                   // Pull stuff out of the form
                   RentalForm rentalForm = (RentalForm) form;
                   RentalBean rentalBean = new 
RentalBean(rentalForm);  //QUERY:  Can I do this?  How else?

                   //Make sure all of the values adhere to business rules
                   rentalBean.validate();

                   // Store the data to the database, or wherever else we 
might store it
                   rentalBean.store();   //QUERY: Should store() do the 
validate?
           }

           // Save error messages keys into HTTP request for use by 
<struts:errors> tag
           if (!errors.empty){
                   saveErrors(request,errors);

                   //return to the original form
                   return (new ActionForward(mapping.getInput()));
           }

           // forward control to the specified "success" URI defined in 
Action.xml
           return(mapping.findForward("success"));
   }
}


public class RentalBean{
   private int id;
   private String address;
   private String city;
   private float rent;
   private int rooms;

   ActionErrors errors=new ActionErrors();
   DataAccess dao=new DataAccess();

   public RentalBean(RentalForm rf){
     //TODO: Set all  this.<property> = rf.<property>
   }

   public ActionErrors validateLogic(){ //QUERY: This is the type of logic 
that should be validated here, right?
     // TODO: Biz Rules
     if (!city.equals("Boston") && rooms > 5)
       errors.add("",new ActionError("No place in Boston has 5 rooms!"));
   }

    public void store(){
       dao.rentalInsert(this);
    }

    public ResultSet get(int id){
       this.id=id;
       return dao.rentalGet(this);
   }
}


public class DataAccess {
   private Connection conn;
   // Other DB specific stuff

   public void rentalInsert(RentalBean){
     // Create sql statement based on values in the bean
     // Execute the sql statement
   }

   public RecordSet rentalGet(RentalBean){
     // Create sql statement to SELECT record from database WHERE 
ID=RentalBean.id
     // Execute the sql statement
     // Return a "RecordSet" or null
   }
}


public class BaseActionServlet extends ActionServlet{
        // TODO: Check logon state, reroute as appropriate
        // TODO: (if necc.) accessing, modifying,, resetting
application-specific 
objects
        // TODO: log exceptions, save under a common request attribute
}


--
To unsubscribe, e-mail:
<mailto:[EMAIL PROTECTED]>
For additional commands, e-mail:
<mailto:[EMAIL PROTECTED]>

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to