Software Steeplechase

Hayden Steep’s development obstacle course. (Java, JEE, and beyond)

August 25, 2006

Let the users do what they want

Filed under: Software Design

One of the must frustrating things about using software is when the it prevents you from doing what you want to do. Sometimes applications do this in order to prevent the user from performing some action that the programmers deem self-destructive. When these events are allowed, they are often followed by a confirmation message. “Are you sure you want to erase your hard drive? …. You numbskull?” At this point, most users may click the “No” button and let out a sigh of relief, while the more savvy users would click “Yes” because they are probably readying their disk for the latest distribution of the Linux operating system. If the programmers had thought to themselves, “You know, letting somebody erase their hard drive is probably a bad thing, let’s not allow them to do that,” then the Linux aficionado would find himself very frustrated. This frustration level may even result in that person not wanting to use whatever operating system was restricting him from doing what he wants to do, which could influence his future purchasing decisions.

When yahoo introduced it’s free web-mail service, I was one of the first people to sign up. I had tried Microsoft’s popular Hotmail, and found it to be inundated with spam only days (if not hours) after creating a new account. Yahoo’s service has served me well, but after 10 years of use the account has succumbed to spammers, despite their best filtering attempts. I setup an elaborate system of filters to try to reduce the flow of spam myself, and I routed these messages into a folder I created called SPAM. After a few weeks, the SPAM folder started to fill up with thousands of messages and they started to have an impact on my space quotas, so I decided to let yahoo do the filtering and send spam directly to the trash rather than a custom folder I’d created.

So, now I have a folder with 65,535 messages in it that I would like to delete. That’s what I want to do. I’m not trying to do anything fancy, I just want to delete a folder.

Mail Index

This image represents my folder index. I have no way of deleting the messages in my spam folder or deleting the folder outright. Therefore, I click on the ‘Edit’ button next to folders to see if I can do it on some other screen.

Folder Menu

Thank goodness. According to this menu, I can delete my SPAM folder. There’s a nice straight-forward delete button right next to it. However, when I click the delete button, I’m presented with the following message…

Error Message

I’m unable to delete a folder unless it is empty. To this, I can only ask… why? Why am I unable to delete a folder unless it’s empty. The most probable reason is that the developer was too lazy to call a delete subroutine in the event that the user tried to delete a non-empty folder. Or, perhaps it was some ill-conceived reasoning by a layer of management that thought removing a folder with email messages in it is “bad”, and they should protect the user from doing that.

Part of designing good software is determining what your users want to do. In the Yahoo Mail case, they got it partially correct. At some point, somebody realized that user’s may want to delete the folders they have created. However, instead of letting the users do this, a decision was made to protect us from ourselves. Either that or some project manager didn’t want to waste the 10 minutes it would have taken to implement that feature.

Now, I have to remove in excess of 65 thousand email messages without the benefit of a “delete all” button.

I wish the program would let me do what I want… so that I can expedite my transition to Gmail.

August 16, 2006

Virtual Java Interview ‘06 (Breadth of knowledge VS. Depth of knowledge)

Filed under: Java, General

canidate (smiling): Thank you for taking the time to see me. I’m excited about becoming a web developer for Turbo-MegaWidgets.

interviewer (smiling): No problem. I’m glad you could come in on such short notice. Why don’t you start by telling me about the technology you are familiar with.

interviewer (straight-faced): For example, we would like someone who knows how to program in Java, C#, Ruby, Python, Perl, PHP, C++, and Lisp. Can you do this for us?

canidate (forlorn): I’m sorry, but I’m only experienced with Java and know very little about those other programming languages.

interviewer (jolly): Ha ha ha ha! That’s perfectly ok! We focus on Java technology here at Turbo-MegaWidgets. I just use that as an ice-breaker to assure you that we don’t focus on breadth of knowledge here. As long as you have expertise, or depth of knowledge as we like to say, then you’ll do just fine!

canidate (relieved): Whew! That’s good to know, because it would be hard to become proficient in so many different areas of technology.

interviewer (smiling): No kidding. I’m glad to be doing business analysis these days, the thought of it makes me shiver. Anyhow, let’s get back to the interviewing shall we?

interviewer (straight-faced): We’re looking for a Java developer who has an in-depth understanding of using the Rational Unified Process, Extreme Programming, or Agile Methodologies to implement EJBs, Servlets, JSP, Velocity, Custom Tag Libraries, JSTL, XML, Ant, Maven, JUnit, TestNG, JDBC, Hibernate, iBATIS, Struts, Tapestry, Java Server Faces, Javascript, AJAX, GWT, Spring (all of it), AspectJ, WebServices, SOAP, how to configure CVS, BugZilla, Tomcat, JBoss, HypersonicDB, MySQL, Apache Web Server, Eclipse or another IDE, and an Applet in a twisted pair tree.

canidate (jolly): Heh heh heh! You almost got me again!

interviewer (straight-faced): I’m not joking this time.

canidate (nervous): Oh… well…. I created a Javascript “rollover” one time… on a JSP…

interviewer (dismissive): Don’t call us. We’ll call you.

While talking with a contact recently, he marveled at the breadth of knowledge you had to have in order to be a competent web developer. The explosion of frameworks and tools over the last 10 years has had the affect of putting more responsibilities on fewer engineers.

When I started professional web development only a few years back in 1998, there were dedicated programmers responsible for framework architecture, configuration and build management, database design and maintenance, and so on. This setup had the by-product of allowing each programmer to develop an expertise, or depth of knowledge, in the area for which he was responsible. It was easy to bring new-hires into this environment because it wasn’t essential that they ‘knew everything’, but more important that they were bright and could immerse themselves in their duties.

Today there are many tasks that have been made easier through the use of good tools. It would be financially irresponsible to pay the same number of developers to fill roles with narrow scopes. This has benefited developers by allowing them to be involved with more aspects of projects than was previously possible. This increased responsibility requires a breadth of knowledge that makes developers feel important (humility aside, something most of us care about a great deal), but it also puts a burden of responsibility on us to stay on the upward curve. Whereas the old teams depended on many smart developers, the new teams depend on smart and studious developers.

August 2, 2006

Maximizing Struts page flow re-use

Filed under: Java, Struts

In Struts, form’s must be bound to a specific action. Because of this, developers might create nearly identical Forms, say in JSP, in order to perform 2 distinct actions… Create vs Update.

Example:

 <!-- CreateFoo.jsp -->
 <html :form action="CreateFoo">
   <html :text property="name"/>
   <html :text property="description"/>
 </html>    

 <!-- UpdateFoo.jsp -->
 <html :form action="UpdateFoo">
   <html :text property="name"/>
   <html :text property="description"/>
 </html>

In this scenario, the only thing that is different between the two JSP’s, is the Action that will be initiated when the form is submitted. It would be nice to have 1 form that could be used whether the system is trying to create OR update.

One approach to solving this problem would be to use Tiles and put the form fields into a separate file.

 <!-- CreateFoo.jsp -->
 <html :form action="CreateFoo">
   <tiles :insert attribute="FooFields"/>
 </html>

While this implementation would reduce some duplication, you would still end up with 3 JSP’s rather than just 1, or even 2 as in the previous example.

So, what do we do then? The trick is to re-think what you are trying to accomplish, and develop actions that are fine-grained enough to use in either scenario. In other words, when the user wants to CreateFoo or UpdateFoo, in each situation they want to EditFoo. So, instead of thinking about the 2 flows as atomic units of work, they can be broken down as:

CreateFoo = (1) Show FooForm (2) Edit FooData (3) Create Foo

UpdateFoo = (1) Show FooForm (2) Edit FooData (3) Update Foo

The 2 distinct flows show signs of commonality and we end up with 1 JSP that can be used in either the Create or Update flow.

Example:

 <!-- EditFoo.jsp -->
 <html :form action="EditFoo">
   <html :text property="name"/>
   <html :text property="description"/>
 </html>

Now the question is… How do I incorporate the single ‘Edit’ page into an Update or Create Flow?

First of all, don’t try to define the page flows for the Create or Update flows outright, just access the Create or Update action mappings and let those actions delegate responsibility to other actions for the data they require.

Example:

<action path="/CreateFoo"
  type="my.action.CreateFoo"
  name="FooForm">
    <forward name="success" path="/FooSummary.jsp"/>
    <forward name="noFormData" path="/EditFoo.jsp"/>
</action>

<action path="/UpdateFoo"
  type="my.action.UpdateFoo"
  name="FooForm">
    <forward name="success" path="/FooSummary.jsp"/>
    <forward name="noFormData" path="/EditFoo.jsp"/>
</action>

<action path="/EditFoo"
  type="my.action.EditForm"
  input="/EditFoo.jsp"
  name="FooForm">
    <forward name="success" path="/Exit.do"/>
</action>

When the Create/Update flows are invoked, they will check to see if the FooForm contains any data. If not, those actions can return a forward that will cause the EditFoo.jsp to be displayed.

The only problem left is, to return control back to the ‘calling’ actions.

Note that the /EditFoo action mapping invoked /Exit.do upon successful completion (which is just the submission of the EditForm without any validation errors). So how do we tell Exit.do where to go?

Create a Stack in the session called ExitForwards. An action mapping would then add itself to the stack whenever it chains to a delegate flow, like when Create or Update flows branch off to the Edit flow.

An easy way to do this is to create a subclass of the Struts ActionForward called ExitForward.

package my.forward.ExitForward

public class ExitForward extends org.apache.struts.action.ActionForward
{}

Not one of your more complicated classes is it?

Then in your action mappings, just indicate that a forward is an ExitForward by simply specifying the ExitForward class type in the action mapping.

<action path="/CreateFoo"
  type="my.action.CreateFoo"
  name="FooForm">
    <forward name="success" path="/FooSummary.jsp"/>
    <forward name="noFormData" path="/EditFoo.jsp"
      className="my.forward.ExitForward"/>
</action>

<action path="/UpdateFoo"
  type="my.action.UpdateFoo"
  name="FooForm">
    <forward name="success" path="/FooSummary.jsp"/>
    <forward name="noFormData" path="/EditFoo.jsp"
      className="my.forward.ExitForward"/>
</action>

The only thing left to do is override the RequestProcessor to Push the current ActionMapping onto the ExitForwardStack (in the Session) whenever a forward of type ExitForward is returned.

//Override the RequestProcessor (or TilesRequestProcessor)
protected void processForwardConfig(HttpServletRequest request, HttpServletResponse response,
    ForwardConfig forward) throws IOException, ServletException {
  if (forward != null) {
    if (forward instanceof ExitForward) {
      Stack exitForwardStack = (Stack)request.getSession().getAttribute("ExitForwardStack");
      ActionForward redirectForward = new ActionForward();
      redirectForward.setRedirect(true);
      redirectForward.setPath(this.processPath(request, response));
      exitForwardStack.push(redirectForward);
    }
  }
  super.processForwardConfig(request, response, forward);
}

Then just wire up your Exit.do mapping and Action class

<action path="/Exit"
  type="my.action.GetExitForward"/>


  public class GetExitForward extends Action{

    public ActionForward Execute(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception{
      Stack exitForwardStack = (Stack)request.getSession().getAttribute("ExitForwardStack");
      ActionForward forward = (ActionForward)exitForwardStack.pop();
      return forward;
    }
  }

That’s all there is to it. With these modifications, you can transform your Struts application into a lean, mean, re-use machine. Applying these changes will allow you to re-use proven functionality in flows without having to create a lots of unnecessary action mappings and JSP’s.

Get free blog up and running in minutes with Blogsome
Theme designed by Jay of onefinejay.com