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 A950310F93 for ; Mon, 7 Oct 2013 20:35:56 +0000 (UTC) Received: (qmail 70179 invoked by uid 500); 7 Oct 2013 20:35:56 -0000 Delivered-To: apmail-struts-dev-archive@struts.apache.org Received: (qmail 69923 invoked by uid 500); 7 Oct 2013 20:35:55 -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 69901 invoked by uid 99); 7 Oct 2013 20:35:51 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Oct 2013 20:35:51 +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 (athena.apache.org: domain of davelnewton@gmail.com designates 209.85.219.52 as permitted sender) Received: from [209.85.219.52] (HELO mail-oa0-f52.google.com) (209.85.219.52) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Oct 2013 20:35:47 +0000 Received: by mail-oa0-f52.google.com with SMTP id n2so6669436oag.25 for ; Mon, 07 Oct 2013 13:35:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type; bh=hC/m5JCy5d/11Lx51YNZ69urNds01XJcrlUkZmO9V+M=; b=0u2GM0XGvrZno6//Z2qXU0i9M9qptkfIYcaZzackaYf1audVeeLf1IKWUzcgCr68EH /Ga4MvTd+CnGwLbnmENySHFNgseMPU/8+DxaVRyQjCtI6HGijyW13rdcpVooQmm3o1uA MIYKBVM8HXgR4aaxRtSZU7rtEtf10AXyO9AmrMgYZMX459adkuok2LLZ3SAyhkWt0mXG AhDTFPu+gEAW8YcTk1+Gl/tGC8z/xs+xCxyu5KX1DjUjxmLPPqKIfPVqIgz3Swwy4Qj0 g3KIttCmpngT1J6f2H0xPeiXn3FBlgtbsHTUY1LRCoeJx3VmlEVTKjnJ8UgKyu5ozGkb SOnw== MIME-Version: 1.0 X-Received: by 10.60.74.170 with SMTP id u10mr3593665oev.49.1381178126935; Mon, 07 Oct 2013 13:35:26 -0700 (PDT) Received: by 10.76.129.145 with HTTP; Mon, 7 Oct 2013 13:35:26 -0700 (PDT) In-Reply-To: References: Date: Mon, 7 Oct 2013 16:35:26 -0400 Message-ID: Subject: Re: JspHelper concept to improve MVC separation From: Dave Newton To: Struts Developers List Content-Type: multipart/alternative; boundary=001a1134c98ea0ad7b04e82c9769 X-Virus-Checked: Checked by ClamAV on apache.org --001a1134c98ea0ad7b04e82c9769 Content-Type: text/plain; charset=ISO-8859-1 So it's a presenter/decorator? Dave On Mon, Oct 7, 2013 at 4:26 PM, Mike Menzies wrote: > 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 > -- e: davelnewton@gmail.com m: 908-380-8699 s: davelnewton_skype t: @dave_newton b: Bucky Bits g: davelnewton so: Dave Newton --001a1134c98ea0ad7b04e82c9769--