James,

I was reading through the archives when I came across the below post.  If
one follow the design as you have shown below, dose the Delegate get stored
in the form?  If it does then does one reference the DAO via the delegate
with a nested type syntax in the view/JSP?

So if I have a Delegate, which in you example does a "viewall", I first
create the delegate, create the DAO and populate it and then store the
Delegate in the form with some standard setter and getter method:

   FORM:

   public void setDelgate(Object d) {
       this.delgate = d;
   }

   public Object getDelgate() {
       return this.delgate;
   }

Then in the view how do I reference the values in the DAO?

Is it just something like:

        <bean:write name="ViewAllFrm" property="delegate.dao.value"/>

Where dao is the Data access object, and value is a getter in that object.

Is this the correct flow or am I missing something?

Thanks in advance.

Regards,

Todd G. Nist

-----Original Message-----
From: Hicks, James [mailto:[EMAIL PROTECTED]]
Sent: Thursday, November 29, 2001 5:46 PM
To: Struts Users Mailing List
Subject: RE: Design Help


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]>



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

Reply via email to