Exploring Java Web Development, Part 3

I’m happy to report that I’ve completed resurrecting JWebTrack, the terribly feature-incomplete bug tracking project I did for a Java class oh so long ago.  After building an appropriate database and populating it with some data, the app worked like a charm.

Well, except for one thing:  I had to change statement.executeQuery() to statement.executeUpdate() for the INSERTs.  That must be a recent development in either the MySQL connector or Java because it wasn’t like that in 2003.

One thing I have to admit about JSP web pages and servlets:  They load fast.  Admittedly these pages aren’t doing a lot, but they come up like lightning compared to the PHP and ASP.NET pages I’ve done lately.  (This was in the Eclipse browser… don’t know what it’s using to render pages.)

I have this crazy idea to port some of my PHP blog code to JSP and ASP.NET MVC.  Working on them in parallel should give a pretty fair estimate of the pros and cons of each development environment.

GridView, UpdatePanel and PopupControlExtender

This is a nightmarish combination to deal with in ASP.NET 3.5.  I will attempt to document what I learned today about how to get this combination working.

Here is the scenario:  Start with a GridView and a bunch of templated columns.  The first four columns of the GridView contain labels with magnifying glass icons next to them, and the rest of the columns are just TextBoxes.  The idea is that when you click the magnifying glass, a “popup” window appears that lets you pick from a list of choices.  It’s just like a DropDownList, except we can’t use a DropDownList in this case because the descriptive text of each choice could be huge.  Each selection affects the list that appears in the next column over, so the selections “cascade.”  We don’t want any postback inside the grid to redraw the full page.

The grid looks something like this, with anything that my boss might consider sensitive fuzzed out (not that it’s the slightest bit sensitive):

GridView Popups

I won’t get into the details of binding data to the grid and all the controls inside the grid; that’s a whole different subject (in brief – don’t put the GridView.DataBind call in Page_Load if you want to see any events from controls inside the grid).  I’ll just focus on the AJAX functionality.

Here is the template for the first column (oops, I guess you know it’s a Task column now):

<asp:TemplateField HeaderText="Task">
    <asp:UpdatePanel ID="updateTaskPanel" runat="server" UpdateMode="Always">
        <asp:Label ID="lblTask" runat="server" />
        <asp:Image ID="imgTask" runat="server" ImageUrl="~/Images/Hourglass.png" />
        <cc1:PopupControlExtender ID="pceTask" runat="server"
          Position="Bottom" />
        <asp:Panel ID="popupPanelTask" runat="server" CssClass="modalPopup" style="display:none;">
          <asp:UpdatePanel ID="updatePopupTaskPanel" runat="server">
              <asp:Label ID="lblTaskPopup" runat="server">Title</asp:Label>
              <asp:RadioButtonList ID="radioTasks" runat="server"
                DataTextField="DisplayLabel" DataValueField="Id" />
              <asp:Button ID="btnPopupTaskOkay" runat="server" Text="OK"
                OnClick="btnPopupTaskOkay_Click" UseSubmitBehavior="false" />
              <asp:Button ID="btnPopupTaskCancel" runat="server" Text="Cancel"
                OnClientClick='AjaxControlToolkit.PopupControlBehavior.__VisiblePopup.hidePopup(); return false;'
                UseSubmitBehavior="false" />
  <ItemStyle Wrap="false" />

There are several important things to note:

  • There are two ContentTemplates; one for the contents of the column and one for the contents of the popup panel.  I believe both are necessary to avoid a full-page refresh.
  • The outer UpdatePanel is set to UpdateMode=”Always”.  I found this necessary because changing the value of one column affected what was in the other columns.
  • The Image (aka. TargetControlID) and the PopupControlExtender are both in the same UpdatePanel.  That’s a requirement.
  • The popup Panel is also inside the ItemTemplate.  I needed to do this because each row could have different popup contents.  If they were all the same, I believe it could have been moved entirely outside the GridView.
  • There is a style=”display:none;” attribute on the popup Panel.  That is necessary to avoid seeing the panel flash for a second when the page first loads.  (Don’t set Visible=”false”.)
  • Both Buttons in the popup panel have the attribute UseSubmitBehavior=”false”.  I had all kinds of problems without those.
  • The Cancel button has the attribute OnClientClick=’AjaxControlToolkit.PopupControlBehavior.__VisiblePopup.hidePopup(); return false;’.  This causes the popup to close with a client-side call, rather than posting back to the server.  This results in much faster performance.

Here is the code-behind for one of the Okay buttons in a popup Panel:

protected void btnPopupTaskOkay_Click(object sender, EventArgs e)
    Button btn = (Button)sender;
    GridViewRow row = (GridViewRow)btn.NamingContainer;
    RadioButtonList rbl = (RadioButtonList)row.FindControl("radioTasks");
    TaskRowView dataItem = this.editingRows[row.RowIndex];
    dataItem.TaskId = int.Parse(rbl.SelectedValue);
    dataItem.IsModified = true;
    PopupControlExtender pce = AjaxControlToolkit.PopupControlExtender.GetProxyForCurrentPopup(Page);

The important part is the Cancel() at the end.  That is what actually dismisses the popup.  If you don’t do that, the popup will just sit there on the screen.

One other important thing to note about UpdatePanel if you don’t already know:  Even with a partial-page refresh, during the round-trip to the server, the entire page is generated, even though only a subset of the page is sent back to the client.  But keep in mind that all the Page events are fired and all the control events are fired, even if they’re outside the UpdatePanel being refreshed.  That means you need to be careful in tuning the performance of partial page renders.  For example, if your Page_Load event does a time-intensive database load or something, it will occur on every asynchronous refresh and performance will suffer perhaps more than necessary.

Exploring Java Web Development, Part 2

Day two of reacquainting myself with JSP development, wherein we learn that IDEs are powerful tools but they are not very friendly to newcomers.

(Btw thanks to Red for helping out with this stuff.)

My first test was a hello world page using a simple one-property User class.  I got a static page working fine, but for some reason I couldn’t get Eclipse to import the class (<%@ page import=”User” %>).  It kept saying it couldn’t find it.

I figured it had something to do with GlassFish, so I installed Apache Tomcat 6 and set it up as a server in Eclipse.  This also didn’t work at first.  I kept getting “Project facet Sun Deployment Descriptors Files version 9 is not supported” errors.  Thinking GlassFish was still interfering somehow, I uninstalled the entire EE SDK.  Suddenly the import worked and the page loaded perfectly.  (I may not have those steps exactly right, so don’t quote me on it.  I think somewhere in there I also changed some project settings that had something to do with “facets.”)

(Around this time Eclipse stopped working, complaining that I didn’t have the JRE anymore.  I had both a 32-bit and 64-bit JRE and I think it got confused or something, so I uninstalled everything, then reinstalled a 32-bit JRE, the 32-bit SE SDK and the EE SDK (without starting GlassFish!).)

Feeling empowered, I next started a new project and imported some code I wrote many years ago for a Java class, which was a very, very simple bug-tracking app connected to a MySQL database.  I downloaded the latest MySQL JDBC connector, fixed a whole bunch of warnings and errors in the code that made me wonder how it ever worked before, and everything compiled cleanly (at least according to Eclipse).  But it didn’t come up in Tomcat with or without debug, complaining of a 404 Not Found.

It turned out all the .html and .jsp files need to go in the WebContent directory.  Okay, no problem – after moving the files, the index.html page came up.  But it bombed when I clicked into the .jsp pages because it couldn’t find the JDBC driver class.  After some more trial and error I discovered that the mysql-connector-java-5.1.10-bin.jar file needed to go in the WebContent/WEB-INF/lib directory.  (Not in the Project’s Libraries where I originally put it.)

And that’s where I left things, since I don’t actually have a copy of the database for this silly app, so I’ll need to recreate one before I can look at anything else.

Exploring Java Web Development, Part 1

Out of curiosity and recent disgruntlement with ASP.NET I decided to look into Java web development.  I haven’t done this since around 2002, so of course I’ve forgotten everything I ever knew about it.  Herein I will attempt to document the knowledge I uncover.

Immediately after arriving at java.sun.com, I remembered that the Java world has a bewildering array of cryptically-named technologies to sort through.  There are approximately 100,000 different choices and variations of what you can download and there is almost nothing to guide you toward what you’re supposed to get.

First of all, for web development, get the Java EE SDK, not the Java SE SDK.  (SE doesn’t have the servlets and stuff in it.)  The Java EE download page has a bunch of different options – the one I got was called “GlassFish Java EE + JDK.”  (GlassFish is apparently Sun’s alternative to Tomcat.)

Do not be alarmed at the incredibly ugly, 1990s-looking Swing window that the Java EE SDK installer brings up.  Ah, Java Swing.  It was one of the main reasons I switched to .NET.  Showing a Swing-based installer that looks nothing like a normal Windows app is an especially horrible way to introduce Windows people to Java development.  (I guess I should be thankful it even has an installer.)

Java EE 5 SDK Installation Wizard - Swing components

Beware that Windows 7 64-bit is an “unsupported installation platform” for Java EE 5 SDK Update 7.  You have to resize the installer window for the Next button to appear so you can continue anyway.

Java EE 5 SDK Installation Wizard Java EE 5 SDK Installation Wizard after Resizing

(I couldn’t help but notice that the SE install goes to C:Program FilesJava but the EE install goes to some crazy thing like C:SunSDK.  Not very Windows-friendly.)

The installation sets up a GlassFish application server so you should be able to browse to http://localhost:8080 when it’s done.  I suppose theoretically you could start writing code at this point with a text editor and compile with command-line tools, but I’m definitely not that ambitious.

So I downloaded the Eclipse IDE for Java EE Developers.  This was a far, far less painful experience than getting the SDK.  This is the greatest thing about Eclipse:  You just unzip it somewhere, double-click the executable and it just works.  Wouldn’t life be grand if everything worked like that?

Because the SDK installed a GlassFish server and I didn’t particularly feel like trying to setup a Tomcat server, I got a GlassFish plugin for Eclipse.  Follow the handy instructions on that page and when it asks for the directory of the GlassFish application server, put in the same place you installed the SDK (eg. C:SunSDK).  (It took me a while to figure out that was where the GlassFish application server directory was.)

So now theoretically I should be able to create a Java web app.  I’ll save that for another time, though, because I have to dig through my backups to find my old Java code.

Playing FarCry 2 on PS3

Normally I only play one game at a time, but Need For Speed SHIFT is pretty one-dimensional so I’ve also started FarCry 2 for the PS3.  The story is nothing like the original FarCry, sharing only a name that honestly has nothing to do with anything.  This time around you’re running (and driving) around an enormous chunk of Africa.

It’s one of those open-world style games, so you can do the missions in any order you want.  I actually find this style of game a little annoying – I get a better sense of accomplishment from linear games.  With open games I usually feel like I’m wandering around aimlessly for no particular reason.  FarCry 2 is not so bad, though – it actually feels a little like an MMO.

I tried some multiplayer but I was not impressed.  The lag was pretty intense so it felt like playing on a dial-up connection (maybe nobody was running servers near me).  Plus most of the maps that came up were user-generated, which means they pretty much sucked.  So far, I’d say FEAR2 had a much better multiplayer experience.

ASP.NET Can Be Annoying

I’ve been working on a new ASP.NET app at work, which gives me a new opportunity to complain about ASP.NET.

One of my ongoing struggles with ASP.NET is figuring out the correct sequence that page and control events are fired in.  I find myself constantly stepping through code to figure out whether SelectedIndexChanged events happen before or after DataBind events or between the Load event and PreRender event or some other such nonsense.  It drives me crazy because it’s something I never have to do in PHP.  (Because you get to write your own page life cycle.)  It’s really tempting sometimes to just override Page.Render() and do everything myself.

And don’t get me started on UpdatePanels.  This is the second project in a row where I simply can’t get them to work right.  “AJAX is so simple in ASP.NET!” they said.  “Just drop an UpdatePanel in there and it just works!”  Yeah, right.  Take my advice and stick with jQuery or even hand-coded Javascript.  It’s much less painful.

Changing Ids

If you’re reading this in an RSS feed reader, you may have noticed duplicate entries because I keep changing things in the feed.  Now I’m going to change all the id values of the entries to “tag URIs” so you’ll probably be spammed again.  Sorry about that.

Google Wave

Google Wave is all the rage among the digerati:  If you don’t have a Google Wave invite, you are a nobody in the industry.  (The super-elite even have more than one account!)  LifeHacker is already running tips and tricks even though nobody can access it.  My opinion on Google Wave?  Don’t have it, don’t need it, don’t want it.

I applaud Google’s effort to find a new email paradigm, but I think they’re going in a weird direction.  From what I understand, Wave is basically a real-time document collaboration tool with an open architecture so developers can write their own extensions.  I personally don’t get how creating a document will replace email – I hardly ever want to take an email and turn it into a document.

Honestly I think it’s that second part — the open architecture — that is generating all the buzz.  It’s what businesses tend to do when they know their product has no intrinsic value:  They open it up so that “the developer community” will create the value and demand for them.  There are gajillions of examples of this:  Photoshop is popular because developers can write plugins.  Firefox is popular because developers can write extensions.  Twitter is popular because developers can hook into the API.  The iPhone is popular because developers can write apps.  Wave will be popular because developers can write their own gadgets (or whatever they’re called).

Having been there, I find developing open architectures is a dual-edged sword.  It’s nice when other developers do your work for you (for free!), but it’s a pain to maintain a system that’s open enough for other people to write plugins.  It’s essentially like writing an operating system.  You spend all your time working on the framework and no time on the actual features of your software.  I used to swear I wouldn’t write any more open systems, but unfortunately, the digerati demand it, and successful products depend on it.

But I digress.  Back to Wave.  I haven’t seen it and I don’t have an invite; I’ve only heard people talking about it (endlessly) on tech podcasts.  It sounds to me like there are going to be some usability issues.  For example, I certainly won’t be using it unless there’s a way to turn off the “other people watch you type letter-by-letter” feature.  It was annoying when ICQ did it in 1997 and I have no doubt that it’s still annoying now.  The “wow, that’s cool” factor wears off pretty quick when you realize that, in practice, you end up staring at people backspacing a hundred times until they figure out what they’re trying to say, which is a huge timesink.  Average users certainly won’t like it because they’ll feel like they’re “on the spot” — like the conversation has turned into a performance of some sort.

I just don’t get it, and unlike the digerati, I don’t feel the need to jam it into my life just because it’s a Google product.

Gubernatorial Smackdown Tonight

There’s a Virginia gubernatorial debate tonight at 8 PM (see Creigh’s writeup and Bob’s writeup).  This one will actually be broadcast on local television (I think this is the first one but I’m not sure) so you won’t have to fight with streaming video to see it.

If this debate goes like the rest of the campaign has so far, we’ll see a lot of talk about national issues that have nothing to do with Virginia.  Bob McDonnell (R) will be calm and confident – he’ll talk about creating jobs, downplay his archaic social views and attack Deeds on raising taxes.  Creigh Deeds (D) will be wildly animated, acting like he needs some Ritalin.  He’ll attack McDonnell on his 20-year old graduate thesis and leave people wondering how he managed to win the Democratic nomination in the first place.

Most voters probably won’t watch, and if they do they do they’ll probably judge the candidates solely on their body language or something their friend forwarded them in a chain email.

Playing Need For Speed SHIFT

I’m currently playing Need For Speed SHIFT on the PS3. It’s pretty good but it takes a lot of work to turn off all the silly music.

UPDATE: It’s actually really good, but it’s much harder to control than your standard arcade racing game. Supposedly not as complex as Forza or Turismo though. Online play is kind of fun but everyone seems to have faster cars than me. They’re probably cheating somehow. :)

UPDATE 2:  Online NFS races are sort of frustrating.  A large percentage of people seem to think they are playing demolition derby, which I suppose is the racing form of griefing.

Most online races are the same:  Half the field gets wiped out at the first turn because they don’t slow down after flooring it from the starting line, which usually causes a huge pileup to avoid.  (It’s fun to watch from the back though – cars and dust go flying all over the place.)  If you survive that you’re pretty much within 1 or maybe 2 positions of your final position, because it’s almost impossible to overtake anyone that gets more than 10 seconds ahead of you.  (For me, at least; I’m convinced my top speed is throttled somehow.)  Your only hope is that the people in front of you will miss the corners while you nail them perfectly.

If you’re unlucky enough for someone to overtake you later in the race, most of the time they aren’t content with merely passing you – they go out of their way to knock you off the road and end any chance that you’ll make the podium.  One time I was going around a sharp corner with a guy right behind me, and I swear he tried to T-bone me at full speed.  (He missed and crashed into the guardrail instead hehe.)

There aren’t enough people hosting games apparently.  I’ll leave one lobby to find another and half the time it picks the exact same lobby again.  (You have very little control over which games you get into.)  This is a major problem because a lot of people choose to host ridiculous races – like where you have to drive a super-powered Maserati around on a tiny circular track meant for Yugos.  (It’s like watching popcorn pop – everyone slams into the outside walls and flies up in the air.)

And this is especially annoying:  I can’t find any way to turn off the idiots with headsets.  There’s always one guy with a headset in the race carrying on a conversation with… nobody.  And they always sound drunk or stoned out of their mind.  Gah.

I guess what I’m saying is that online players are still dorks. :)