My Key Takeaways for Struts after One Day

After one day of playing with Struts these are my key takeaways:

– Struts is an MVC framework which attempts to separate the view (the HTML page), the control code (“Actions”), and the model or database code. This means it’s rather heavyweight if you’re building a three-page site that presents a calendar–but if you’re writing a complex site, then this separation of responsibility makes things much easier to track what is going on. Of course like all design models, it only helps if you keep things clear–all any design model can do is provide you tools to keep things clean. But like a vaccuum cleaner, it only works if you use the tool religiously and use the tool correctly.

– Setting up Struts2 is fairly simple: once you’ve created a Tomcat project within Eclipse (in my setup, I downloaded Tomcat 5.0.28 into the Eclipse project, then used the Sysdeo Eclipse Tomcat Plugin to get Eclipse to play well with Tomcat), it’s a matter of copying the xwork, ognl, freemarker, commons-logging and struts2-core jar files from the Struts2 distribution into the WEB-INF/lib folder, and telling Eclipse about the jar files located there.

Then create the web.xml file in WEB-INF, creating the struts.xml file in WEB-INF/src (where Eclipse will then automatically copy it into WEB-INF/classes where it belongs), and you should be set.

Of course if you screw something up–such as accidently copying the struts-core rather than the struts2-core jar file–then you will get the dreaded “FilterStart Error” without any explanation. Things to look for: did you mistype the name of the filter-class in the web.xml file? Did you copy the wrong jar file like I did? If you see a lot of struts startup stuff but then it fails–did you mistype the class name or package name for your action in struts.xml?

– Struts uses these things called “Actions”, derived from com.opensymphony.xwork2.ActionSupport, which implements the “execute()” method, which essentially is what happens when your web site processes a form. Actions take you from one place to another. How actions are wired up is defined in struts.xml, and what page we move to after executing an action is defined in struts.xml. Results can route to a .jsp file which uses struts tags to simplify getting state information for presentation from an action or from the current state.

– State between actions is maintained in a Map, which can be obtained through ActionContext.getContext().getSession()–the context is thread safe (it uses thread-local storage for the context), though I suspect the session Map is not, since multiple pages may share the same session.

Once you know how to build pages (Dreamweaver, Struts tags), how to build actions (and ActionSupport seems like a good place to start), and store context, then you should be able to get off the ground.

Now, of course, like any API, about three to four months from now I will read back at this post and say “ohmygod, how naive was I?” But you gotta start somewhere. And this looks to me like as good a place as any to start.

Time to understand Struts

Turns out Yahoo internally uses Struts for their web development, so it appears its time for me to understand Struts. (Did I mention the fact that I’m changing jobs? Could be; haven’t told my current employer, though, and the offer isn’t in writing.)

The problem is my fuzzy little brain doesn’t work well with black boxes. I need to have a mental model of how things work–even if that mental model isn’t precisely what is going on under the surface. I’ve never been a “cookie-cutter” programmer; I’m not the type to want to be handed something then told “okay, follow this formula and you’ll get something that works.”

So the tutorials for Struts which begin “grab the empty war file, and start adding stuff”–uh, it doesn’t work for me. I have a mental model as to how Tomcat works and the lifespan of a servlet; I even have a mental model of how JSP pages work–though the mental model is missing stuff, like how tags in the tag library works. So the idea that I should just follow the instructions and not worry about how it works for Struts–that won’t make my life easier.

Fortunately I came across the simple setup guide, which appears to walk through the skeleton of a Struts application, from where the .jar files go to how to set up the web.xml configuration file to the basic struts.xml file. Between this and the documentation home with the architecture, tags and configuration in a nutshell, that looks like a very promising place for me to start…

WebProxy posted.

A final fix (turns out ThinkGeek.com sends an HTTP/1.1 response with a “Content-Type” header but no length, so I had to change the reader code to take that into account), and the proxy server code now appears to work with every web site I visit.

So it’s now posted.

This is a Java server program which listens to incoming connection and proxies (forwards) the HTTP requests to another server. This basically implements the proxy protocol, though this is a bit of an “itch scratch” on my part–so I cannot guarantee the code will work for you, even though it seems to work for me. It uses the common tools library that is also posted here–“common tools” being a nice phrase for “jar file which catches all of the common crap that doesn’t have a good home elsewhere”, and helped me exercise the bugs on my HTTP client and server code.

Of course if you want something robust and industrial strength, may I recommend looking at the Apache foundation web site instead? This stuff is small potatoes, but as I said, it does appear to work for me, so have at it!

Here is the web site with some information.

D’oh!

So I uploaded the common tools package to the web site last night–and realized that I had no version information in the jar file or the documentation files. How do you know if you have the latest thing?

Quick tweak to the build.xml file for all uploaded projects to add a build number to the jar and the javadocs, uploaded everything to the server–and all is well in the world.

What I did, by the way, was to use <propertyfile> in ant to manage a build properties file, then use the resulting properties to generate the correct manifest and copyright information. So now the copyright information contains the version number corresponding to the build number use to build the docs–which generally is the same as the build number for the jar files. (But not always; some of my code checks to make sure the jar file of other projects are up to date, which triggers a build number increment on that other project.)

The latest additions I made to the common files include code for an HTTP server framework–so I could build my proxy, a simple and stupid ‘debug’ IO Stream class which allows you to hook an OutputStream to an InputStream or OutputStream chain so you can see what’s going through the pipe, as well as a watchdog timer class which has the nice property that you can tell an object in the timer to reset itself.

Nothing earth-shattering here, and all are things you probably can find better examples of elsewhere. But what the heck; it entertains me, which is really what all this is about. Right? If someone else accidently finds all this stuff useful, then so much the better.

Really odd proxy bug. Fixed. (Yay!)

I sorted out a really interesting proxy bug, which means that my proxy code now apears to work correctly with WordPress posting.

What was really happening is this: in my test harness I’ve turned off the connection shutdown timer code, so the connection stays alive indefinitely. But on the WordPress side, as I was typing in a long post, it would shut its side of the connection down.

Now I have a call in my outbound connection code which was verifying the connection was open by calling the java.net.Socket calls ‘isClosed’, ‘isConnected’, ‘isInputShutdown’ and ‘isOutputShutdown’ routines to see if the connection was still open. If the connection had been closed, this was supposed to bring the connection down, discard the socket, and reconnect it fresh.

And apparently, even though all of these calls were indicating the connection was open, the moment I sent data out, I was getting an ‘end of connection’ error on reading the input stream from the socket.

So I kludged it: if I get an end of connection when I expect a response, close down the socket, re-open it, and try the connection again–but only once. If it fails a second time, bail.

This appears to do the trick–I managed to post this through the firewall code, where yesterday I was failing.

That means I can clean this up, post it to the ‘net (probably on Monday) and set up my proxy so I can get my e-mail from work… 🙂

The Internet is held together by duct tape and bailing wire!

So I finally figured out what was wrong with my proxy code.

The World Wide Web is held together by duct tape and bailing wire! I hadn’t realized that HTTP/1.1 requests aren’t uniform across the Internet–or even on the same damned server. A request can ask for a /1.1 request and get a /1.0 response–and I wasn’t handling /1.0 responses well. Further, some servers go so far as to just close the damned connection rather than return a 404 error. (*sigh*) So after working through all of the niggly issues (thanks to Safari, which provides helpful diagnostic tools to allow me to find the things failing to load) and a ton of debugging statements, and I’ve finally arrived at a proxy server that appears to work.

(In fact, I posted this message through my proxy engine.)

The proxy isn’t the most efficient thing in the world: for example, my connection caching code is serializing requests that are being made in parallel to the same web server. (Thus, even though Safari happily opens up five separate threads to process requests faster, I catch them and funnel them through one connection.) So the net effect is a connection which is pretty slow.

And of course I’m not caching or doing any of the nice things proxy servers are supposed to do.

But it appears to work!

I’ll clean it up and post it later this weekend.

Update, two seconds later: I went to hit ‘publish’, and nothing. WTF? Okay, back to the drawing board. Apparently I’m mangling the POST message being made when I tried to submit this post.

Progress on the HTML Proxy

I managed to work out the bug in my previous post. Turns out that it’s a bug in my software and a bug in whatever is handling the e-mail redirect at LunarPages. My bug: I was sending the ‘Host:’ HTML header twice. LunarPages? They were grabbing the list of host values, and concatenating them in order to produce the redirect location. So what was happening was in my host header I was writing

Host:www.chaosinmotion.com
Host: www.chaosinmotion.com:80

and apparently they were grabbing the value–presumably as part of some sort of JSP processing system, which I guess was returning the value

"www.chaosinmotion.com:80, www.chaosinmotion.com"

and they were just slapping a resource identifier at the end (“/forwardaddress”) and an “http://&#8221; on the front and sending it out.

I assert it’s a bug in their software because the RFC says that the Host: field of an HTTP request can be a host name optionally followed by a colon and a port number. Their software is broken because when they attempt to redirect people to their on-line e-mail reader application (which is on port 2095), if you set the Host string to

Host: www.chaosinmotion.com:80

you get redirected to http://www.chaosinmotion.com:80:2095

There are some other issues I’m ironing out. But it appears my proxy redirector is working on about 95% of the web sites I’ve tried it with. Of course it’s failing on the one web site I want–the on-line e-mail application I want to connect to from work–but at least I’m making progress.

Oddities while building an HTTP proxy.

I’m building an HTTP proxy. Because I can.

I’ve modified the HTTP classes within the common library to allow me to build an HTTP server; it’s just a matter of taking incoming requests and forwarding them, spitting back the response.

Or so I thought.

One web site, hosted at LunarPages, seems to be sending a forward request (302) in reply to a request I’m making–but for some reason the Location filed is being munged. Instead of setting the URL to something that looks like:

Location: http://www.chaosinmotion.com/forwardaddress.html

I seem to be getting something like:

Location: http://www.chaosinmotion.com:80, www.chaosinmotion.com/forwardaddress.html

What the hell?

Once I sort it out I’ll post it–because where I work they have the firewall from hell, and I want a proxy server running somewhere so I can get my e-mail.

New section in Wiki

I’ve added a new section to the Wiki to store articles I put together to track how to do something. This new section, called “technical notes” will contain notes about how to string a technology together to make it work.

The first article is an overview of the work I’ve done so far to understand Java’s Drag and Drop.

Java Swing Stupidity, pt. 2

How the hell can Sun take something complicated and make it overengineered and overly complicated to boot?

Well, if the functionality is ‘drag and drop’, the answer is apparently easy.

Now, in the ideal world, drag and drop would be handled as follows: your custom drag component would call a magic “is drag initiated” when a MouseListener.mousePressed() method is called, which would take control of the mouse click and automagically determine if drag and drop was initiated. If it is initiated, the magic routine would return true, giving you the opportunity to build your drag operation and call some magic ‘startDrag’ routine. If the magic routine returned false, then you know you should handle the mousePressed routine as usual.

Drop would be similarly easy: you would register and implement a ‘DropTargetListener’ interface, which would give you the opportunity to say “yes, I’ll accept that drop” and handle mouse events and the drop target.

A third class would implement the ‘Transferable’ interface which would represent the thing you’re dragging.

Is Java Swing’s drag and drop this easy? That is, do they do the hard work so that, as someone who wants to implement a custom JComponent which accepts drag and drop, it’s fairly easy?

Uh, no.

No, instead we wind up with this whole dazzling array of stupidity on the part of Sun which, after spending a few hours deciphering some excellent articles, eventually leads you to something that resembles drag and drop nerdvana. (*sigh*)

What’s wrong with Drag and Drop?

Okay, to start off with, apparently Java didn’t decide to create an insulating drag & drop layer for Swing above AWT. So we need to use AWT, which scatters drag and drop functionality across two packages: java.awt.dnd (not Dungeons and Dragons), and java.awt.datatransfer. And our first step in creating a drag and drop tool is handling dragging.

With dragging, we need to do the following: (a) determine if our mouse event is a drag event, then, if true, (b) create an object we transfer, and (c) start dragging. So in my search I came across this thing called DragGestureRecognizer, and its subclass, MouseDragGestureRecognizer.

And immediately we encounter a violation of the first rule of making an application framework–a violation which is very common with intermediate-level developers: they engineered it more complicated than it needed to be. Why the fuck have an abstract drag gesture recognizer engine which is then enstantiated with a concrete class? Does Sun think that at some time in the future drag recognition is going to be triggered by a network event? Do they anticipate a TCP/IP packet which starts a drag event?

Drag and drop is not a general-purpose user interface thingy; it is specifically an action that is triggered by a mouse gesture (or the equivalent of a mouse gesture). Conceptually if you press at a given location with your mouse (finger/tablet pen) and hold for a moment, you pick something up, and then you drag it somewhere else and you drop it. You don’t trigger this sequence through keyboard actions (unless your keyboard is emulating a mouse), nor do you trigger this sequence through a TCP/IP network connection or by manipulating the knobs on the front of the computer.

So the first rule of frameworks is violated: don’t make it overly generalized unless it needs to be.

So, now that we know that we somehow need this gesture recognizer to, well, recognize a gesture, how do we plug this in?

Not into mousePressed, we don’t. Instead, when we construct our JComponent, we have to initialize the DragGestureRecognizer for our component and pass a reference to our component to this DragGestureRecognizer, which listens to mouse events in the background. Well, that’s convenient: instead of adding like three likes of code to our mousePressed routine, we add three lines of code to our constructor and like magic, our mouse presses are automatically translated into drag events.

Except now we have a problem. Because this effectively happens asynchronously to our mouse down/mouse move events (because we keep receiving these events as the drag gesture recognizer is figuring out if this is a drag event), we have the odd problem that we continue receiving mouse events unaware or uncertain if this will be handled as a drag event. So we go along, implementing a ‘rectangle select’ operation–then, all of a sudden, our rectangle select event turns into a drag event.

The reason why it is preferable to handle triggering drags on mouse down is that we know right away if the event is a drag and drop event or not. Generally the rule for drag and drop is if the mouse stays within a small (5 pixel) rectangle for more than 1/4th of a second, the user is probably ‘grabbing’ the object to drag–so it’s not like that quarter second delay is going to be all that noticeable. Further, we can decide prior to asking if it is a drag event if the location where we are clicked is eligable for dragging in the first place.

But now we have this asynchronous operation taking place behind our back–a monster we have to feed. And while the solution is simple: on our click event decide if we are eligable for a drag event, and if we are, bail and allow the background drag code then handle things–this has two problems. First, it scatters the logic for handling drag and drop across multiple locations rather than keeping them in one spot–which makes maintanance a royal pain in the ass. Second, it means that if we select an object eligable for drag and drop, we have no way to implement secondary behavior if the user clicks and drags in a way which doesn’t fit in our drag and drop sematics.

Bah.

No problem, I suppose: I still have to decide if the click is eligable for drag and drop, and if it is, set a variable with a reference to the current mouse event we’re doing drag and drop with–because we need that event to set up drag and drop. By placing the drag and drop operation as an asynchronous operation, however, we suddenly find drag and drop going from “call this function and see if it’s drag and drop, then set up the drag object and run drag and drop” to implementing three separate interfaces and creating two classes.

I suppose two of those interfaces make a certain sort of sense, at least in the context where we have this background class “helping us”: if we’ve got this “helper class” helping us, we need an interface to tell the “helper” what it should do. And that’s the role of DragGestureListener: to listen for a drag gesture triggered event, and decide if it’s a valid drag operation, and trigger the drag if it is one. And the second interface, the Transferable, is something we’d need anyway: it’s the data being transfered.

The interface DragSupport, however, doesn’t seem to serve any purpose I can think of–except it does allow us to work around a bug in MacOS X’s implementation of Drag and Drop which doesn’t reset the mouse cursor when drag and drop ends. (*sigh*)

So, instead of overriding mousePressed, (a) determine if this is a drag event by calling some routine, (b) setting up drag, and (c) calling a routine to handle dragging, instead we (a) get an instance to the DragSource object, (b) create a new DragGestureRecognizer with our (c) interior class implementing the DragGestureListener interface and the DragSourceListener interface, which (of course) (d) fixes the MacOS X Java bug by setting the cursor in dragExit. Then we override mousePressed, (e) calculating if this is a drag event (which we’d have to do anyway), and (f) if so, setting some variable which our DragGestureListener can look at to see if it should then actually drag. In our DragGestureListener, we then (g) set up the drag and (h) call a routine to handle dragging.

Because 9 steps is easier than 3, natch.

Dropping

Now dropping should be an equally easy operation, and for the most part it is: you have a DropTargetListener which actually listens for drop events. And of course you still need to tell some global mechanism that your control accepts drop events: this appears to be the role of the DropTarget class. So we create a new DropTarget class, give it a reference to our component, and a reference to our inner class which implements the DropTargetListener class–and away we go.

So dropping isn’t as painful as dragging, I suppose.

Now if someone could tell me why Java took something that should have been two calls and turned it into two interfaces and two classes too many, I’d be greatful.