<!-- --><style type="text/css">@import url(https://www.blogger.com/static/v1/v-css/navbar/3334278262-classic.css); div.b-mobile {display:none;} </style> </head><body><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d7256432\x26blogName\x3dThe+Frustrated+Programmer\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dBLACK\x26layoutType\x3dCLASSIC\x26searchRoot\x3dhttp://frustratedprogrammer.blogspot.com/search\x26blogLocale\x3den_US\x26v\x3d2\x26homepageUrl\x3dhttp://frustratedprogrammer.blogspot.com/\x26vt\x3d5012862196962223429', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </script>
| Wednesday, June 30, 2004

When you have a page that needs some data to display (like a list of items), you usually populate the request in your struts action. But if that page is also a form.. things start to break down.

Here's what I mean:

<action path="/foo" type="FooAction" input="foo.jsp">
<forward name="viewSuccess" path="foo.jsp"/>
<forward name="updateSuccess" path="next.jsp"/>
<forward name="updateFailure" path="foo.jsp"/>
</action>

public class FooAction extends DispatchAction {
public view(...) {
request.setAttribute( "foos", businessLogic.getFoos() );
BarForm bar = (BarForm)form;
bar.populate( businessLogic.getBar() );
return viewSuccess;
}

public update(...) {
BarForm bar = (BarForm)form;
try {
businessLogic.doSomething( bar.getSomething() );
} catch( Exception e ) {
handleError( e );
return updateFailure;
}
return updateSuccess;
}
}


The problem? Well, if you have an error (with struts-validator or with the business logic), things will get forwarded to foo.jsp, but it will not get the data needed to properly render the page.

One solution is to make a common method for the loading the data that foo.jsp needs into the request. Although this works fine. I don't like it for two reasons:
-You need to keep calling this method for each method in your dispatch action.
-It breaks encapsulation.

If you change the action definition as follows, you get a better page, but it has a new problem, can you guess it?


<action path="/foo" type="FooAction" input="/foo.do&method=view">
<forward name="viewSuccess" path="foo.jsp"/>
<forward name="updateSuccess" path="next.jsp"/>
<forward name="updateFailure" path="/foo.do&method=view"/>
</action>


If you guessed that any changes the user made to the form are lost if an error occures, you're right! Since the view() method blindly overwrites the form's contents from the business logic, any changes made are blown away.

The solution I use is basically this:


public class FooAction extends DispatchAction {
public view(...) {
request.setAttribute( "foos", businessLogic.getFoos() );
if( !hasErrors( request ) ) {
BarForm bar = (BarForm)form;
bar.populate( businessLogic.getBar() );
}
return viewSuccess;
}

...

public boolean hasErrors( HttpServletRequest request ) {
ActionErrors errors = request.getAttribute( Globals.ERROR_KEY );
return errors != null && errors.size() > 0;
}
}


This seems to work fine for me. I think the best solution is to have a common abstract base class that can enforce this pattern for you:


public abstract class FormAction extends DispatchAction {
public ActionForward view(...) {
prepareRequest(...);
if( !hasErrors( request ) ) {
prepareForm(...);
}
return viewSuccess;
}

public ActionForward update() {
return updateForm(...);
}

public void prepareRequest(...) {}
public abstract prepareForm(...);
public abstract updateForm(...);

public boolean hasErrors( HttpServletRequest request ) {
ActionErrors errors = request.getAttribute( Globals.ERROR_KEY );
return errors != null && errors.size() > 0;
}
}


Update: Changed some of the method names in the abstract class based on Billy's comments.

| Tuesday, June 29, 2004

I use www.fastbuzz.com. Its a great site, but just in case it dies an unexpected death, here is my current reading list.

<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
<head>
<title>Fastbuzz Subscriptions</title>
<dateCreated>Tue Jun 29 12:30:42 PDT 2004</dateCreated>
<ownerName>paulkilroy</ownerName>
<ownerEmail>paulkilroy@yahoo.com</ownerEmail>
</head>
<body>
<outline text="Subscriptions">
<outline text="Wired News" htmlUrl="http://www.wired.com/" type="rss" xmlUrl="http://www.wired.com/news_drop/netcenter/netcenter.rdf"/>
<outline text="Joel on Software" htmlUrl="http://www.joelonsoftware.com" type="rss" xmlUrl="http://www.joelonsoftware.com/rss.xml"/>
<outline text="Scripting News" htmlUrl="http://www.scripting.com/" type="rss" xmlUrl="http://www.scripting.com/rss.xml"/>

<outline text="Scobleizer: Microsoft Geek Blogger" htmlUrl="http://radio.weblogs.com/0001011/" type="rss" xmlUrl="http://radio.weblogs.com/0001011/rss.xml"/>
<outline text="Boxes and Arrows" htmlUrl="http://www.boxesandarrows.com/" type="rss" xmlUrl="http://www.boxesandarrows.com/index.xml"/>
<outline text="Google Weblog" htmlUrl="http://google.blogspace.com/" type="rss" xmlUrl="http://google.blogspace.com/index.xml"/>
<outline text="Daring Fireball" htmlUrl="http://daringfireball.net/" type="rss" xmlUrl="http://daringfireball.net/index.xml"/>
<outline text="Simon Willison's Weblog" htmlUrl="http://simon.incutio.com/" type="rss" xmlUrl="http://simon.incutio.com/syndicate/rss1.0"/>
<outline text="JayAllen - The Daily Journey" htmlUrl="http://www.jayallen.org/journey/" type="rss" xmlUrl="http://www.jayallen.org/journey/index.xml"/>
<outline text="Jeffrey Zeldman Presents: The Daily Report" htmlUrl="http://www.zeldman.com/" type="rss" xmlUrl="http://www.zeldman.com/feed/zeldman.xml"/>
<outline text="SimpleBits" htmlUrl="http://www.simplebits.com/" type="rss" xmlUrl="http://www.simplebits.com/xml/rss.xml"/>
<outline text="dive into mark" htmlUrl="http://diveintomark.org/" type="rss" xmlUrl="http://diveintomark.org/xml/rss.xml"/>
<outline text="Recent updates made to Forums at fastbuzz.bloki.com Bloki" htmlUrl="http://fastbuzz.bloki.com/forum" type="rss" xmlUrl="http://fastbuzz.bloki.com/feeds/?fmt=rdf1.0&t=forum"/>
<outline text="Gizmodo" htmlUrl="http://www.gizmodo.com/" type="rss" xmlUrl="http://www.gizmodo.com/index.rdf"/>
<outline text="Digital Media Minute" htmlUrl="http://www.digitalmediaminute.com/" type="rss" xmlUrl="http://www.digitalmediaminute.com/?rss=1"/>
<outline text="sidesh0w.com" htmlUrl="http://sidesh0w.com" type="rss" xmlUrl="http://sidesh0w.com/feed/rdf/"/>
<outline text="Widgetopia" htmlUrl="http://www.eleganthack.com/widgetopia/" type="rss" xmlUrl="http://www.eleganthack.com/widgetopia/index.rdf"/>
<outline text="IBeBloggin'" htmlUrl="http://blog.vinniegarcia.com/" type="rss" xmlUrl="http://blog.vinniegarcia.com/index.rdf"/>
<outline text="A List Apart: for people who make websites" htmlUrl="http://www.alistapart.com/" type="rss" xmlUrl="http://www.alistapart.com/rss.xml"/>
<outline text="Slashdot" htmlUrl="http://slashdot.org/" type="rss" xmlUrl="http://slashdot.org/index.rss"/>

<outline text="Movable Type News" htmlUrl="http://www.movabletype.org/news/" type="rss" xmlUrl="http://www.movabletype.org/index.xml"/>
<outline text="Stopdesign" htmlUrl="http://www.stopdesign.com/" type="rss" xmlUrl="http://www.stopdesign.com/index.xml"/>
<outline text="Web Standards Project BUZZ" htmlUrl="http://webstandards.org/buzz/" type="rss" xmlUrl="http://webstandards.org/buzz/index.xml"/>
<outline text="O'Reilly Network Weblogs" htmlUrl="http://weblogs.oreilly.com/" type="rss" xmlUrl="http://www.oreillynet.com/cs/xml/query/q/333?x-ver=1.0&id_subject=3"/>
<outline text="accessify.com" htmlUrl="http://www.accessify.com/" type="rss" xmlUrl="http://uckan.info/rssify_b/rss_accessify.php"/>
<outline text="webgraphics" htmlUrl="http://web-graphics.com/" type="rss" xmlUrl="http://web-graphics.com/index.xml"/>
<outline text="IBM developerWorks Java technology zone" htmlUrl="http://www.ibm.com/developerworks/java/index.html?ca=drs-j2604" type="rss" xmlUrl="http://www.ibm.com/developerworks/news/dw_java.rss"/>
<outline text="The Register" htmlUrl="http://www.theregister.co.uk/" type="rss" xmlUrl="http://www.theregister.co.uk/feeds/latest.rdf"/>
<outline text="Javalobby Front Page" htmlUrl="http://www.javalobby.org/forum.jspa?forumID=61" type="rss" xmlUrl="http://www.javalobby.org/rss/rssthreads.jsp?forumID=61"/>
<outline text="TheServerSide.com: Your Enterprise Java Community" htmlUrl="http://www.theserverside.com" type="rss" xmlUrl="http://www.theserverside.com/rss/theserverside-rss2.xml"/>
<outline text="java.net Articles" htmlUrl="http://today.java.net/today/articles.csp" type="rss" xmlUrl="http://today.java.net/pub/q/articles_rss?x-ver=1.0"/>
<outline text="Ars Technica" htmlUrl="http://www.arstechnica.com/" type="rss" xmlUrl="http://arstechnica.com/news/rss2.0.rdf"/>
<outline text="Pending:http://www.securityfocus.com/rss/index.shtml" htmlUrl="" type="rss" xmlUrl="http://www.securityfocus.com/rss/index.shtml"/>
<outline text="Anne's Weblog about Markup & Style" htmlUrl="http://annevankesteren.nl/" type="rss" xmlUrl="http://annevankesteren.nl/feeds/rss"/>
<outline text="dionidium.com" htmlUrl="http://www.dionidium.com/" type="rss" xmlUrl="http://dionidium.com/rss20ex.xml"/>
<outline text="CSS Vault Resources" htmlUrl="http://www.cssvault.com/" type="rss" xmlUrl="http://www.cssvault.com/resources.xml"/>
<outline text="Dave Shea's mezzoblue" htmlUrl="http://www.mezzoblue.com/" type="rss" xmlUrl="http://www.mezzoblue.com/rss/2.0/"/>

<outline text="Digital Web Magazine - What's New" htmlUrl="http://www.digital-web.com/" type="rss" xmlUrl="http://digital-web.com/new/rss.php"/>
<outline text="Andy Budd::Blogography" htmlUrl="http://www.andybudd.com/" type="rss" xmlUrl="http://www.andybudd.com/index.rdf"/>
<outline text="alphaWorks: emerging technology" htmlUrl="http://www.ibm.com/alphaworks" type="rss" xmlUrl="http://alphaworks.ibm.com/rss/java.xml"/>
<outline text="The BileBlog" htmlUrl="http://jroller.com/page/fate" type="rss" xmlUrl="http://www.jroller.com/rss/fate?excerpts=true"/>
<outline text="CSS Vault Gallery" htmlUrl="http://www.cssvault.com/" type="rss" xmlUrl="http://www.cssvault.com/gallery.xml"/>
<outline text="Raible Designs ~ We Build Web Apps" htmlUrl="http://raibledesigns.com/page/rd" type="rss" xmlUrl="http://raibledesigns.com/rss/rd"/>
<outline text="Web Standards Awards" htmlUrl="http://www.webstandardsawards.com/" type="rss" xmlUrl="http://www.webstandardsawards.com/index.rdf"/>
<outline text="Pending:http://www.hackingnetflix.com/netflix/" htmlUrl="" type="rss" xmlUrl="http://www.hackingnetflix.com/netflix/"/>
<outline text="Heal Your Church Web Site" htmlUrl="http://www.healyourchurchwebsite.com/" type="rss" xmlUrl="http://www.healyourchurchwebsite.com/rss.xml"/>
<outline text="Clagnut" htmlUrl="http://www.clagnut.com/" type="rss" xmlUrl="http://www.clagnut.com/rss/summaries/"/>
<outline text="ETC. Indulging my inner geek" htmlUrl="http://www.fortysomething.ca/mt/etc/" type="rss" xmlUrl="http://www.fortysomething.ca/mt/etc/simpleindex.xml"/>
<outline text="Apache News Blog Online" htmlUrl="http://www.apachenews.org/" type="rss" xmlUrl="http://www.apachenews.org/index.xml"/>
<outline text="Shaun Inman // Commentary" htmlUrl="http://www.shauninman.com/mentary/" type="rss" xmlUrl="http://www.shauninman.com/feeds/commentary.rdf"/>
<outline text="The Man in Blue" htmlUrl="http://www.themaninblue.com/" type="rss" xmlUrl="http://www.themaninblue.com/perspective.xml"/>
<outline text="Pending:http://www.syndic8.com/link.php?URL=http://www.tonypierce.com/blog/bloggy.htm" htmlUrl="" type="rss" xmlUrl="http://www.syndic8.com/link.php?URL=http://www.tonypierce.com/blog/bloggy.htm"/>
<outline text="Moments" htmlUrl="http://www.aplus.co.yu/" type="rss" xmlUrl="http://www.aplus.co.yu/aplus.xml"/>
</outline>

</body>
</opml>

These are two different things in struts. Kinda silly really...

Here's the I write jsp code for a form (yes, I'm still not fully converted to CSS, hopefully in the next web app):

<tr>
<td class="reqlabel">*Name</td>
<td class="input"><html:text property="name"/></td>
</tr>


The problem is, in my validation.xml I also have this:

<form name="NameForm">
<field property="name" depends="required">
<msg name="mask" key="Name" resource="false"/>
</field>
</form>


This violates DRY (Don't Repeat Yourself) since both the fact that its required and the label/resource key are duplicated. Ideally the label could be built dynamically from the information in the validation.xml. For example:
-required - Make it bold with a star in front of the label.
-date/phone/etc - Append a format template to the label, e.g. Birthdate (MM/dd/yy):

Suggestions?


| Monday, June 28, 2004

If an input is not configured correctly (typo or just non existent), the error is not propogated to the user. Instead just a blank screen is presented to the user.

This bug is related.

| Tuesday, June 15, 2004

So I grab a pack of M&M's from the vending machine. I pour 5 of them into my hand and notice there are only two colors present. I do this again, with the same result. And finally a third time with the same result.

In this bag of M&M's there are 6 colors, Red, Blue, Yellow, Orange, Brown and Green.

So the odds of this happening are???

Struts is an excellent framework for simple forms with a static number of fields on them, but when you need to present a fairly dynamic form, struts starts to stumble pretty quickly.

Today the customer came to me with a change request to add text inputs next to what had previously just been a dynamic list of 'options' from the database:

(*new column*)
[x] Option 1 ______________
[ ] Option 2 ______________
[ ] Option N ______________

Before the text input change request came in, struts multibox handled the form perfectly. But now, there now a good choice in the struts tool box to solve this. Here is a decent reference to the a similar problem:

http://www.developer.com/java/other/article.php/2233591

Their solution seems like a little overkill for this problem.

| Thursday, June 10, 2004

'We need to start monitoring our logs. There are errors we are missing and not filling as defects.'...

I go on to explain the following:
-Our current error log messages do not include enough state about the error. Things such as IDs of objects need to be included in the messages.
-Logs files can first be monitored by hand, but later can be automated with log4j custom appenders. At my last job we used a custom appender that sent emails every 15 minutes with up to 25 errors, further errors with just referenced as a count (... '30 errors ignored') too avoid too much email spewage.
-Tools like SiteScope can also be used to help monitor the server and log files.
-You can even get as advanced as my friend Frank who is automatically entering errors into the bug database with no user intervention.

I was pretty much ignored since this isn't hurting them at this second. I will try to escalate it next week.

| Wednesday, June 09, 2004

These beans seem less and less cool every day (Yeah, I know I'm the only one on the planet that is still using them). Today I get the following error:

org.apache.commons.beanutils.ConversionException: Cannot assign value of type 'java.math.BigDecimal' to property 'foo' of type 'java.lang.String'
org.apache.struts.action.DynaActionForm.set(DynaActionForm.java:423)
org.apache.commons.beanutils.PropertyUtils.copyProperties(PropertyUtils.java:314)

Now, what's happening is I'm calling PropertyUtils.copyProperties() trying to prepopulate my form bean with data from my business object. There is a type mismatch between foo in the business object that is a BigDecimal and foo in the form which is a String. This is because in struts-land, form beans tied to a text input MUST always be of type String because first struts populates, then it validates.

Struts/Validator/BeanUtils are no help here, but the fix is fairly easy I think. Just extend DynaActionForm add override set() as follows:


public void set(String name, Object value) {
DynaProperty descriptor = getDynaProperty(name);

if (value != null) {
if (descriptor.getType() != null && descriptor.getType().getName().equals(String.class.getName())) {
super.set(name, value.toString());
} else {
super.set(name, value);
}
}
}


Well, this is just half the problem. This does not fix the post population of the business object. The error here is in PropertyUtils. PropertyUtils IMO needs some pluggable data conversion utilities. It just throws Conversion Exceptions now.

'Your next task is to clean up the print view and add the servicable grid...'

'You know, these green and red symbols we are using for indicators don't really convey the same meaning in black and white printouts.'

'Its ok, just get it done'.

Do I mention that 10% of men in the United States are green/red color blind? No, I'm too beat down and its only 10:00 AM.