Return-Path: X-Original-To: apmail-struts-dev-archive@www.apache.org Delivered-To: apmail-struts-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4875910F56 for ; Mon, 7 Oct 2013 20:27:10 +0000 (UTC) Received: (qmail 58596 invoked by uid 500); 7 Oct 2013 20:27:08 -0000 Delivered-To: apmail-struts-dev-archive@struts.apache.org Received: (qmail 57094 invoked by uid 500); 7 Oct 2013 20:26:57 -0000 Mailing-List: contact dev-help@struts.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Struts Developers List" Reply-To: "Struts Developers List" Delivered-To: mailing list dev@struts.apache.org Received: (qmail 56870 invoked by uid 99); 7 Oct 2013 20:26:55 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Oct 2013 20:26:55 +0000 X-ASF-Spam-Status: No, hits=1.5 required=5.0 tests=HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of glopal@gmail.com designates 209.85.219.41 as permitted sender) Received: from [209.85.219.41] (HELO mail-oa0-f41.google.com) (209.85.219.41) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Oct 2013 20:26:48 +0000 Received: by mail-oa0-f41.google.com with SMTP id n10so6899427oag.0 for ; Mon, 07 Oct 2013 13:26:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to:content-type; bh=S3sax7gm69UNSbGMzszie++mFPFaOH4u/qDtLvYmCF8=; b=x2KPWXOdO/MdgSe6/U4ly2QOVPna2kiquiqSMjR+gdzG6C9Ht9gdxdOpVjs8gkHTNI 5NO6TUpKKt8rhlKSeQ9cnxXTW31spGEQnN92iqmtk9ndRyQN3xeGBc2p6eS4mh4L7ap8 NS5BJz3MhLrL4FyyQXF5uNHFgGLPvoD9vyfiH3ei28p1R+iHXSX9GvOU6KMmal5tiF6j BmzXukzrsSAu+Qb3PovgR2aQwc92jzu9jC46yHBELzJPsQerdRR/qLwHTnNw00roJ7BY h2hwBeDMi3HsrPhB22NZcF06gu2fmvp6bXvb+xLUgAtf1cUKZ43rpha1UIQ57loniaMw thxw== MIME-Version: 1.0 X-Received: by 10.60.63.9 with SMTP id c9mr5077154oes.35.1381177586502; Mon, 07 Oct 2013 13:26:26 -0700 (PDT) Received: by 10.60.132.75 with HTTP; Mon, 7 Oct 2013 13:26:26 -0700 (PDT) Date: Mon, 7 Oct 2013 15:26:26 -0500 Message-ID: Subject: JspHelper concept to improve MVC separation From: Mike Menzies To: Struts Developers List Content-Type: multipart/alternative; boundary=001a11c1d8be6a50e904e82c77a2 X-Virus-Checked: Checked by ClamAV on apache.org --001a11c1d8be6a50e904e82c77a2 Content-Type: text/plain; charset=ISO-8859-1 Hello, My name is Mike, and I work for a fairly large company that uses Struts 2 for most of its web apps. Some of these web apps are quite large, and one in particular has grown into a monster. In my ongoing efforts to clean up this project, I have developed what I like to call a JspHelper. Before I explain exactly what that is, I'd like to provide you with some examples of the problems that I set out to fix. *Problem 1 - Monster JSPs* During early development of this project, struts tags were highly leveraged. I was not involved with the early phases of this project, so I can't speak to why they were used so extensively.. but they were. Tons of nested if tags that read var tags that were set in the JSP. Some of these JSPs were very hard to follow. Even though I am a fan of struts tags, I've come to realize that they should not be overused. JSPs cannot be unit tested and JSPs are hard to debug. So cleaning up a JSP means shifting logic out, in most cases into the action. An easy clean up could be something like this: into It's instantly more readable, and the logic is in a testable Java class. Following this approach creates a lot of methods in the action, which can be a problem if you already have... *Problem 2 - Bloated Action Classes* When the project was small, it made a lot of sense to create abstract/parent action classes to share code between actions. As time passed, we just continued shoving any shared code in one of two "abstract" classes... even if the code was only being shared between two out of the 20 actions. The fix for this trend is to moved shared code into services, so we began enforcing that practice. But this approach had its limitations. We use Spring injection for all of our services, and some times a particular piece of logic just required too much stateful input... it just made more sense to leave it on the action where it had easy access to everything it needed, as opposed to shoving it in a stateless service and having to pass in a ton of arguments. What we really needed was some best practices put in place for the creation of stateful, non-Spring services. *Enter JspHelperInceptor * It wasn't until after I created this inceptor that I realized it is almost identical to the ModelDrivenInceptor. At which point, I wondered if that is what I should actually be using.. but more on that later. All this interceptor does is push an object onto the value stack. That's it. Your action implements JspHelperAware which provides Object getJspHelper(), and the intercept code reads almost line for line with the ModelDrivenInterceptor. The idea behind this JspHelper, which is just a POJO that you define, is to provide a place where all (or most) of the data your JSP needs can be found. This means that OGNL can find any public members or operations faster, and also developers can trace back any OGNL expressions in the JSP to it's corresponding Java code. This approach places code in a shareable component. You can easily aggregate one JSPHelper into another one, and then either provide wrapper methods or simply push the aggregate helper onto the stack using the s:push tag for direct access to it's members. This approach makes the actions leaner, and provides a very clear cut view. I find at times that the action tends to be both controller and view, when really it should only act as the controller (IMO). This technique is functionally very similar to ModelDriven, but it's used slightly differently. I've used a JspHelper in combination with ModelDriven, and I've found the JspHelper to be a good place to store logic.. and leave the Model strictly as a business object. *Conclusion * I have personally found this technique incredibly useful in cleaning up code. You can shift all of the JSP exclusive logic and properties to the helper without touching the JSP (the OGNL expressions remain the same). Then after that initial step, you have a real handle on the exact input your JSP needs, and the actual clean up process progresses faster and with less errors. I'm still in the process of establishing some best practices, but so far this approach is working for us. I would like some honest feedback --001a11c1d8be6a50e904e82c77a2--