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