Announcement Announcement Module
Collapse
No announcement yet.
Trading Complexity Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Trading Complexity

    It's not that good really is it?

    There are some nifty little tools and good techniques, but generally...

    as soon as you want to do something less than straightforward...

    your just swapping the complexity of the servlet api for the complexity of the framework.

    I for one am just about to give up - the time i am wasting trying to figure out how to achieve common tasks such as having two forms on one page might as well have been spent studying the servlet api and a decent design patterns book.

    Feel free to correct me if im wrong

  • #2
    Maybe you're a trailblazer - being the first to figure out how to handle 2 forms on 1 page. Why don't you post your solution here so others can benefit from your experience?

    Comment


    • #3
      What problem are you encountering having two forms on one page? You simply point each one at a separate URL and have a controller or controller method to handle each post.

      Rob

      Comment


      • #4
        Problems with multiple forms on one page

        I had no end of problems trying to use the binding and validating when two forms were on same page. It seems to want to bind every element on the page regardless of which form it belongs to.

        Comment


        • #5
          I see - that is interesting. Can you post your code so I can look into this a little more. It may be that we need to modify the code slightly or it may be that it just requires some re-configuration.

          Rob

          Comment


          • #6
            Multiple forms on one page

            I guess the lack of decent tutorials/examples is just the problem -

            My first attempt used the SimpleFormController.
            The tutorial indicates that your form tag should look like this
            <form method="post">
            Only you can't specify more than one command class so I created another class just for the form which had two references to the individual form backing objects - i did something similar with the validator.

            got myself in a right muddle with all this - tried it all ways and eventually gave up.

            I am now getting some results by forgetting about the FormControllers and just implementing the Controller interface (I still like the idea of the dispatcher etc). This gives me greater flexibility when it comes to processing my forms - i can still use the ServletRequestDataBinder.

            I have encapsulated both forms into one which posts to a controller that checks which button was pressed in order to decide what action to take. This allows me to keep all the info entered by the user on the forms.

            I just need to get my head round the spring:bind tag to see if it's got the flexibility i need (select option lists etc), and also the validators.


            code snippets follow

            <form name="logonContact" action="logon.htm" method="post">
            <input type="text" name="username" size="15" class="default_input" style="width: 100px;height: 14px;" value="<%=(credentials.getUsername() != null)?credentials.getUsername():""%>">

            <input name="logonButton" type="image" src="imgs/buttons/login.gif" alt="login">

            <input type="text" name="company" class="default_input" style="width: 200px;height: 14px;" value="<%=(enquiry.getCompany() != null)?enquiry.getCompany():""%>" maxLength="25">

            <input type="image" src="imgs/buttons/send.gif" alt="send">

            </form>


            My controller can then process the form like so

            public class LogonContactController implements Controller {

            /** Logger for this class and subclasses */
            protected final Log logger = LogFactory.getLog(getClass());

            private CredentialsValidator credentialsValidator;
            private EnquiryValidator enquiryValidator;

            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

            Credentials credentials = new Credentials();
            Enquiry enquiry = new Enquiry();
            ServletRequestDataBinder binder = null;

            try {
            binder = new ServletRequestDataBinder(credentials,"credentials" );
            binder.bind(request);
            binder.close();

            } catch (BindException be) {


            }

            // LOGON button clicked
            if (request.getParameter("logonButton.x") != null) {




            // REMINDER button clicked
            } else if (request.getParameter("reminderButton.x") != null) {


            // CONTACT button clicked
            } else if (request.getParameter("contactButton.x") != null) {

            // DEFAULT
            } else {

            request.setAttribute("credentials",credentials);
            return new ModelAndView("logon");

            //Map myModel = new HashMap();
            //myModel.put("credentials", credentials);
            //return new ModelAndView("logon", "model", myModel);

            }


            public void setEnquiryValidator(EnquiryValidator lcv) {
            enquiryValidator = lcv;
            }

            public EnquiryValidator getEnquiryValidator() {
            return enquiryValidator;
            }

            public void setCredentialsValidator(CredentialsValidator lcv) {
            credentialsValidator = lcv;
            }

            public CredentialsValidator getCredentialsValidator() {
            return credentialsValidator;
            }

            }

            Comment


            • #7
              You might find it easier if you have each distinct form post to a different URL and then map these URLs to different controllers. I am guessing that each form represents a different action the user can take - in this case you would typically use one controller per action. A good approach to this might be to use the MultiActionController which allows for a single controller class with different methods to handle each user action.

              Rob

              Comment


              • #8
                different urls

                Thanks Rob,

                I tried this but then realised that if the form processing reverted to displaying the form again (eg incorrect username or something) then the other forms on the page would lose any state that the user might have entered (didnt want to store everything in the session).

                As I said im a newbie so i might have missed the obvious.

                The MultiActionController sounds promising - do you know of any decent tutorials on using it?

                Cheers,

                Mark

                Comment


                • #9
                  Mark,

                  The petclinic application demonstrates the usage of the MultiActionController. I'm not sure if this is going to what you want though - it may have problems rebinding the data for the other forms. I'm fairly certain that if you have two forms, A and B, then submitting A will not send the data for form B, so you will probably have to use the session to store that data - unless you can reload the data from the data source.

                  This may show the need for a controller that can handle a single form post but with multiple submit buttons.

                  Rob

                  Comment


                  • #10
                    I prefer doing it the long way

                    Hi Rob,

                    I know this isn't a good characteristic for a programmer to have, but i prefer to do some things the long way. I think frameworks are good for some things but when you take a framework too far you end up with code that is totally unrecognisable to anyone who hasn't spent months learning and using the framework. If we all learn the Servlet API then at least we're all singing from the same hymn sheet?

                    Bearing that in mind the thing i like most about the framework so far is that it allows me to pick and choose the framework tools and techniques that I want to.

                    This is my latest code for handling the multiple forms

                    package com.alkalinegroup.app.clientarea.web;

                    import org.springframework.web.servlet.mvc.Controller;
                    import org.springframework.web.servlet.ModelAndView;

                    import javax.servlet.*;
                    import javax.servlet.http.*;

                    import java.io.IOException;

                    import java.util.*;

                    import org.apache.commons.logging.Log;
                    import org.apache.commons.logging.LogFactory;

                    import com.alkalinegroup.core.security.bus.Credentials;
                    import com.alkalinegroup.core.security.bus.CredentialsVal idator;
                    import com.alkalinegroup.module.contactform.bus.Enquiry;
                    import com.alkalinegroup.module.contactform.bus.EnquiryVa lidator;
                    import com.alkalinegroup.core.AlkalineGroupResourceBundle Factory;

                    import org.springframework.web.bind.ServletRequestDataBin der;
                    import org.springframework.validation.BindException;
                    import org.springframework.validation.ObjectError;


                    /**
                    * <p>Title: Client Area Controller Servlet</p>
                    * <p>Description: Spring Controller Servlet for the Client Area web application</p>
                    * <p>Copyright: Copyright (c) 2005</p>
                    * <p>Company: Alkaline Group Ltd</p>
                    * @author Mark Ford
                    * @version 1.0
                    */

                    public class LogonContactController implements Controller {

                    /** Logger for this class and subclasses */
                    protected final Log logger = LogFactory.getLog(getClass());

                    private CredentialsValidator credentialsValidator;
                    private EnquiryValidator enquiryValidator;
                    private int maxLoginAttempts = 3;

                    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException {

                    Credentials credentials = new Credentials();
                    Enquiry enquiry = new Enquiry();
                    ServletRequestDataBinder binder = null;

                    // LOGON button clicked
                    if (request.getParameter("logonButton.x") != null) {

                    // Get the session and the client ip address
                    HttpSession session = request.getSession();
                    String ip = request.getRemoteAddr();

                    // Check in case user has exceeded max login attempts
                    if (exceededMaxLoginAttempts(session, "clientareaLoginAttempts")) {
                    // Max login attempts exceeded - display error page
                    logger.info("Admin login attempt failed (exceeded max attempts) [" + request.getParameter("username") + "] : (" + ip + ")");
                    request.setAttribute("errorMessage", AlkalineGroupResourceBundleFactory.instance().getR esource("com.alkalinegroup.app.clientarea.resource s.SystemMessages").getString("exceeded_login_attem pts"));
                    return new ModelAndView("logon");
                    }

                    // Bind to the credentials instance and validate
                    try {
                    binder = new ServletRequestDataBinder(credentials,"credentials" );
                    binder.bind(request);
                    credentialsValidator.validate(credentials, binder.getErrors());
                    binder.close();

                    } catch (BindException be) {
                    // Sanity checks in case client side validation fails
                    //Iterator iter = be.getAllErrors().iterator();
                    //while (iter.hasNext()) {
                    // logger.error((ObjectError)iter.next());
                    //}

                    Map myModel = new HashMap();
                    myModel.put("logonContact", logonContact);
                    return new ModelAndView("logon", binder.getModel());

                    }

                    request.setAttribute("credentials",credentials);
                    return new ModelAndView("logon");



                    // REMINDER button clicked
                    } else if (request.getParameter("reminderButton.x") != null) {


                    // CONTACT button clicked
                    } else if (request.getParameter("contactButton.x") != null) {

                    // DEFAULT
                    } else {

                    request.setAttribute("credentials",credentials);
                    return new ModelAndView("logon");

                    }

                    String now = (new java.util.Date()).toString();
                    logger.info("returning hello view with " + now);

                    Map myModel = new HashMap();
                    myModel.put("now", now);

                    return new ModelAndView("logon", "model", myModel);
                    }

                    public void setEnquiryValidator(EnquiryValidator lcv) {
                    enquiryValidator = lcv;
                    }

                    public EnquiryValidator getEnquiryValidator() {
                    return enquiryValidator;
                    }

                    public void setCredentialsValidator(CredentialsValidator lcv) {
                    credentialsValidator = lcv;
                    }

                    public CredentialsValidator getCredentialsValidator() {
                    return credentialsValidator;
                    }

                    public void setMaxLoginAttempts(int value) {
                    maxLoginAttempts = value;
                    }

                    public int getMaxLoginAttempts() {
                    return maxLoginAttempts;
                    }

                    private boolean exceededMaxLoginAttempts(HttpSession session, String attribName) {
                    int clientareaLoginAttempts = 0;

                    Integer cla = (Integer)session.getAttribute(attribName);
                    if (cla != null) {
                    clientareaLoginAttempts = cla.intValue();
                    } else {
                    clientareaLoginAttempts = 0;
                    }
                    clientareaLoginAttempts = clientareaLoginAttempts + 1;
                    logger.info("Number of admin login attempts this session: " + clientareaLoginAttempts);
                    session.setAttribute("clientareaLoginAttempts", new Integer(clientareaLoginAttempts));

                    return (clientareaLoginAttempts > maxLoginAttempts);
                    }

                    }

                    Comment


                    • #11
                      Mark,

                      I can see what you are attempting here and it looks okay. Can I suggest that you take a look at MethodInvokingFactoryBean as a mechanism for injecting an instance of your ResourceBundle class into the Controller.

                      I think that I might be able to create a generic controller to handle this - some kind of DiscriminatedController where you can configure the button names and the handler methods that they map to.

                      I'll post this on the dev list there may already be a better way to handle this - if not - I'm sure we'll add something .

                      Rob

                      Comment


                      • #12
                        Cheers Rob,

                        I certainly will have a look at MethodInvokingFactoryBean, in fact I need to have a look at the whole message thing -

                        <bean id="messageSource" class="org.springframework.context.support.Resourc eBundleMessageSource">
                        <property name="basename"><value>messages</value></property>
                        </bean>

                        As i'm not sure how it works yet and if it could replace my message factory class altogether.

                        M

                        Comment


                        • #13
                          Here's a controller class that I wrote that is a combination of SimpleFormController and MultiActionConroller. This allows you to have a form with mutiple submit buttons, each one that has a separate method to handle the corresponding action.

                          Sanjiv

                          Code:
                          /**
                           * Spring supports a method disptach controller &#40;@link MultiActionConroller&#125; that does simple
                           * method based dispatching but not complex databing binding and error reporting. It also has a
                           * SimpleFormController class for handling forms that have just one action.
                           * <p/>
                           * For complex screens that have multiple actions on the same page that result in stateful changes,
                           * neither of the above controllers are adequate. This class basically provides method based dispatching
                           * as well as the form submission mechacanism supported by SimpleFormController. This controller is
                           * ideal for complex screens that neither SimpleFormController or MultiActionController can handle.
                           * <p/>
                           * The execution workflow like data binding, form submission, reference data etc capabilities of
                           * SimpleFormController is preserved in this controller class.
                           *
                           * @author Sanjiv Jivan
                           * @since Dec 3, 2004
                           */
                          public abstract class BaseMultiFormController extends SimpleFormController
                          &#123;
                          	protected ParameterMethodNameResolver methodNameResolver = new ParameterMethodNameResolver&#40;&#41;;
                          	public static final String METHOD_PARAM_NAME = "action";
                          
                          	protected BaseMultiFormController&#40;&#41;
                          	&#123;
                          		setSessionForm&#40;true&#41;;
                          		setBindOnNewForm&#40;false&#41;;
                          		methodNameResolver.setParamName&#40;METHOD_PARAM_NAME&#41;;
                          	&#125;
                          
                          	public void setMethodParam&#40;String methodParam&#41;
                          	&#123;
                          		methodNameResolver.setParamName&#40;methodParam&#41;;
                          	&#125;
                          
                          	protected abstract Object formBackingObject&#40;HttpServletRequest request&#41; throws Exception;
                          
                          	protected ModelAndView onSubmit&#40;HttpServletRequest request, HttpServletResponse response,
                          	                                Object command, BindException errors&#41;
                          	        throws Exception
                          	&#123;
                          		String methodName = this.methodNameResolver.getHandlerMethodName&#40;request&#41;;
                          		Method m;
                          		List params = new ArrayList&#40;3&#41;;
                          
                          		try
                          		&#123;
                          			m = this.getClass&#40;&#41;.getMethod&#40;methodName, new Class&#91;&#93;&#123;HttpServletRequest.class, HttpServletResponse.class, Object.class, BindException.class&#125;&#41;;
                          			params.add&#40;request&#41;;
                          			params.add&#40;response&#41;;
                          			params.add&#40;command&#41;;
                          			params.add&#40;errors&#41;;
                          		&#125;
                          		catch &#40;NoSuchMethodException e&#41;
                          		&#123;
                          			try
                          			&#123;
                          				m = this.getClass&#40;&#41;.getMethod&#40;methodName, new Class&#91;&#93;&#123;HttpServletRequest.class, BindException.class&#125;&#41;;
                          				params.add&#40;request&#41;;
                          				params.add&#40;errors&#41;;
                          			&#125;
                          			catch &#40;NoSuchMethodException e1&#41;
                          			&#123;
                          				throw new NoSuchRequestHandlingMethodException&#40;methodName, getClass&#40;&#41;&#41;;
                          			&#125;
                          		&#125;
                          
                          		ModelAndView mv = &#40;ModelAndView&#41; m.invoke&#40;this, params.toArray&#40;new Object&#91;params.size&#40;&#41;&#93;&#41;&#41;;
                          
                          		Map model = errors.getModel&#40;&#41;;
                          		if &#40;mv.getModel&#40;&#41; != null&#41;
                          		&#123;
                          			mv.getModel&#40;&#41;.putAll&#40;model&#41;;
                          		&#125;  
                          
                          		return mv;
                          	&#125;
                          
                          	protected ModelAndView showForm&#40;HttpServletRequest request, HttpServletResponse response, BindException errors&#41; throws Exception
                          	&#123;
                          		if &#40;isSessionForm&#40;&#41;&#41;
                          		&#123;
                          			request.getSession&#40;&#41;.setAttribute&#40;getFormSessionAttributeName&#40;&#41;, errors.getTarget&#40;&#41;&#41;;
                          		&#125;
                          		String methodName = null;
                          		boolean methodExists = true;
                          		try
                          		&#123;
                          			methodName = this.methodNameResolver.getHandlerMethodName&#40;request&#41;;
                          		&#125;
                          		catch &#40;NoSuchRequestHandlingMethodException e&#41;
                          		&#123;
                          			methodExists = false;
                          		&#125;
                          		if &#40;errors.hasErrors&#40;&#41; || !methodExists&#41;
                          		&#123;
                          			Map model = errors.getModel&#40;&#41;;
                          			Map referenceData = referenceData&#40;request, errors.getTarget&#40;&#41;, errors&#41;;
                          			if &#40;referenceData != null&#41;
                          			&#123;
                          				model.putAll&#40;referenceData&#41;;
                          			&#125;
                          			return new ModelAndView&#40;getFormView&#40;&#41;, model&#41;;
                          		&#125;
                          		else
                          		&#123;
                          			Method m = null;
                          			List params = new ArrayList&#40;3&#41;;
                          			params.add&#40;request&#41;;
                          			try
                          			&#123;
                          				m = this.getClass&#40;&#41;.getMethod&#40;methodName, new Class&#91;&#93;&#123;HttpServletRequest.class, BindException.class&#125;&#41;;
                          			&#125;
                          			catch &#40;NoSuchMethodException e&#41;
                          			&#123;
                          				try
                          				&#123;
                          					m = this.getClass&#40;&#41;.getMethod&#40;methodName, new Class&#91;&#93;&#123;HttpServletRequest.class, HttpServletResponse.class,  BindException.class&#125;&#41;;
                          					params.add&#40;response&#41;;
                          				&#125;
                          				catch &#40;NoSuchMethodException e1&#41;
                          				&#123;
                          					throw new NoSuchRequestHandlingMethodException&#40;methodName, getClass&#40;&#41;&#41;;
                          				&#125;
                          			&#125;
                          			params.add&#40;errors&#41;;
                          			ModelAndView controlMV = &#40;ModelAndView&#41; m.invoke&#40;this, params.toArray&#40;new Object&#91;params.size&#40;&#41;&#93;&#41;&#41;;
                          			if&#40;controlMV == null&#41;
                          			&#123;
                          				return super.showForm&#40;request, errors, null, null&#41;;
                          			&#125;
                          			else
                          			&#123;
                          				Map controlModel = controlMV.getModel&#40;&#41;;
                          				return super.showForm&#40;request, errors, controlMV.getViewName&#40;&#41;, controlModel&#41;;
                          			&#125;
                          		&#125;
                          	&#125;
                          &#125;

                          Comment


                          • #14
                            Still Frustrated

                            Nope - this framwork malarky is definately a complete waste of time. Good architects and devlopers should concentrate on giving us examples of best practices and techniques that make use of the standard Java API's.

                            They talk about how singletons etc are BAD, but I'd bet any money that thats exactly how they implement their framework classes. I pity the new developer who comes along and dives straight into Spring - spends about 2 years doing it then realises he knows absolutley nothing about Java.

                            Please please tell me i'm not alone in thinking like this? I've developed a number of large Java web applications which perfectly well, but boy am i having trouble implementing a similar system using Spring.

                            My latest beef is with the message source stuff. I've obviosuly got a problem with my properties file but finding out what that problem is is impossible

                            <bean id="systemMessageSource" class="org.springframework.context.support.Resourc eBundleMessageSource">
                            <property name="basename"><value>com.alkalinegroup.app.clien tarea.resources.SystemMessages</value></property>
                            </bean>

                            <jsp:useBean id="systemMessageSource" scope="application" class="org.springframework.context.support.Resourc eBundleMessageSource"/>
                            <%=systemMessageSource.getMessage("title",null,Loc ale.UK)%>


                            Yes, my problem is with the scooe statement - what is the point of having a completely different context any way.

                            And dont tell me to use that fmt crap
                            <fmt:message key="title"/>
                            what on eath is that little gem of code supposed to mean / do

                            aaaaargh

                            Comment


                            • #15
                              I think that the main problem is the lack of documentation. I've spent a couple of weeks learning Spring MVC and have also found it to be a frustrating and confusing experience.

                              But the code that comes out the other end of this frustrating experience is definitely a lot less than if you have to write it all yourself. So I think that the potential is there, but I would not personally recommend this MVC framework for use on a real large-scale project in it's current form, it's definitely bleeding edge at this stage, and the learning curve is just too steep.

                              Better (any?) documentation with more than just a totally trivial tutorial will make a huge difference. I've found the only way to discover what some features actually do is to just play around with them and see what happens. It was a revelation when I discovered that you could use "redirect:" to pre-populate a form. But I'm still wondering what bindOnNewForm really does.

                              Comment

                              Working...
                              X