Optional, But Enforced, Parameters

There’s bound to be a better term than optional, enforced parameters. My definition is that these are URL parameters that you don’t really care about except for SEO (search engine optimization) but if you have to have them, then they may as well be correct. They help make friendlier URLs.

If I want to get to a details page of an object, the ideal URL parameter is a single integer mapping to an id. It’s simple, clean, and fast. It’s the default URL mapping in Wordpress. However, a single id tells a search engine nothing about the page. It’s a wasted opportunity and it’s important to take advantage of it.

Let’s get concrete. A good URL for a game details page may be http://www.atomicgamer.com/games/34/Diablo-III. It contains the game id (34) for quick lookups, but also contains the game name for SEO. I don’t care about the Diablo III portion when fetching the data, but I do want it to remain consistent for links.

I took a look into doing this with both Tapestry 5 and Wicket. I’m fair from an expert in either of them, so there could be a better way to do this. This is simply the way I determined to do it.

Tapestry 5

First, I need to put a link to my details page somewhere.


<t:grid source="games" row="game">
  <t:parameter name="nameCell">
    <a t:type="pagelink" t:page="games" context="list:game.id,game.name">${game.name}</a>
  </t:parameter>
</t:grid>

Pretty straightforward. That’ll create a link formatted as /games/{id}/{name}. Tapestry does all that for me, and in fact, if I didn’t care about enforcing the game name to be correct, I’d be done. (I’m using the list binding prefix for a quick shorthand here.)

All the rest of the work is done in the game details page’s onActivate method. This method will be called whenever a person goes to the page. I’ll need to fetch the game from the id, and then compare the name parameter to the game’s actual name.


Object onActivate(Long id, String name)
{
  game = gameDAO.findById(id);
  if (game == null)
  {
    return pagelinkSource.createPageRenderLink(Index.class);
  }

  if (!game.getName().equals(name))
  {
    return pagelinkSource.createPageRenderLinkWithContext(this.getClass(), id, game.getName());
  }

  return null;
}

The interesting part is the second if statement. If the names don’t match, I’m actually going to redirect the user to the page with the correct name. This costs a bit in terms of performance, but it will force it to stay consistent. If you’re ambitious, you could include a flash message here and tell the user to update their bookmarks.

That’s about it for Tapestry.

Wicket

The Wicket code is eerily similar in concept. First, I build up my bookmarkable link with the two parameters.


BookmarkablePageLink link = new BookmarkablePageLink("detailsLink", DetailsPage.class);
link.setParameter("id", game.getId());
link.setParameter("name", game.getName());
link.add(new Label("name", game.getName()));
item.add(link);

And somewhere down the line, I use that link.


<a wicket:id="detailsLink"><span wicket:id="name">Game Name</span></a>

Now, Wicket supports clean URLs out of the box, but you have to configure it first or you’re going to get some really ugly URLs. To make my life easier in the long run, I installed the wicketstuff-annotation library. With this, I can just add a couple annotations to the top of my details page to get clean URLs.


@MountPath(path = "games")
@MountMixedParam(parameterNames = {"id", "name"})
public class DetailsPage extends WebPage
{
...
}

With a nice clean URL in hand (/games/{id}/{name}), we’re just a redirect away from being finished. Like Tapestry, I’ll fetch the game and redirect.

public DetailsPage(PageParameters parameters)
{
  super(parameters);

  Game game = getGame(parameters.getAsLong("id"));
  if (!game.getName().equals(parameters.getString("name")))
  {
    parameters.put("name", game.getName());
    setResponsePage(this.getClass(), parameters);
    setRedirect(true);
    return;
}
...
}

And that’s it. I’ll disclaim again that I’m neither a Tapestry or Wicket guru, and I would love someone to point out a better way to do this.

For now though, it’s minor performance cost, but grants me lovely clean, friendly URLs with parameters I don’t have to worry about. If I change the name of Diablo III to Diablo 3, users with the old URL will be redirect to the new URL. I don’t have to think about it and that’s perfect for me.

Wicket, Guice 2.0, Warp-persist 2.0

When I’m in development mode, I dislike using frameworks that will be out-of-date by the time I release. I will install beta (or alpha or simply development) builds of the software I’m choosing to use for my project. The downside (besides framework bugs) is that I’m usually on my own when trying to figure out how to integrate the projects together as most web tutorials aren’t up-to-date.

I’ve spent some time getting Wicket (just 1.4.2), Guice 2, and Warp-persist 2.0 running together. Everything seems to be running happily but I haven’t tried anything complicated. I can inject my Hibernate session and start a transaction though. Just remember that Warp-persist 2.0 is in active development and wicket-guice doesn’t mention Guice 2.0 at all. Use it this all together at your own peril.

It’s really not that much of a stretch from the current 1.0 releases of Guice and Warp-persist. And really, most of what I did I got from blog 1 and blog 2. The current Warp-persist 2.0 docs are a work-in-progress.

First, let’s setup the Maven 2 dependencies under the assumption you got Wicket and Hibernate already configured appropriately.

We’ll need wicket-guice which is dependent on Guice 1.0. We’ll remove that dependency.

        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket-guice</artifactId>
            <version>${wicket.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.code.guice</groupId>
                    <artifactId>guice</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Then, we’ll add a dependency for Guice 2.0.

         <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>2.0</version>
        </dependency>

Lastly, we’ll add our dependency for Warp-persist 2.0. Unfortunately, there aren’t any public Maven repositories that have a recent build of Warp-persist 2.0 stored. We’ll need to download the most recent public build (you can get the source and build your own snapshot, but I leave that as an exercise to the ambitious) and manually install it our local Maven repository.

Head to the public dist and download warp-persist-2.0-20090214.zip. The snapshot is out-of-date (as of 2009/10/12)! Extract the warp-persist-2.0-20090214\warp-persist-2.0-20090214.jar located in the zip file to some local directory. Then run the following Maven command to install this jar into your local repository.


mvn install:install-file -Dfile=warp-persist-2.0-20090214.jar -DgroupId=com.wideplay.warp -DartifactId=warp-persist -Dversion=2.0-20090214 -Dpackaging=jar -DgeneratePom=true

With any luck, you’ll be able to add the last Maven dependency.

        <dependency>
            <groupId>com.wideplay.warp</groupId>
            <artifactId>warp-persist</artifactId>
            <version>2.0-20090214</version>
        </dependency>

Maven is exciting.

The SessionPerRequestFilter from Warp-persist 1.0 has been deprecated. In our web.xml, we’ll have to use the new PersistenceFilter instead. It’s pretty straightforward. Just make sure you define the PersistenceFilter mapping first. This’ll get us towards our open session in view goal.

	<filter>
		<filter-name>wicket.web</filter-name>
 		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
		<init-param>
			<param-name>applicationClassName</param-name>
			<param-value>com.atomicgamer.web.WebApplication</param-value>
 		</init-param>
 	</filter>

    <filter>
        <filter-name>warpPersistFilter</filter-name>
        <filter-class>com.wideplay.warp.persist.PersistenceFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>warpPersistFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>wicket.web</filter-name>
	    <url-pattern>/*</url-pattern>
    </filter-mapping>

We’re almost there. In our subclasses WebApplication, we need to configure Warp-persist’s PersistenceService, tell Guice about it, and tell Wicket-guice about everything. One difference from Warp-persist 1.0 is that we don’t need to manually start the PersistenceService. The PersistenceFilter takes care of that for us.

        //We'll be wanting to use Hibernate
        Module warpModule = PersistenceService.usingHibernate().across(UnitOfWork.REQUEST).buildModule();
        //And use the Warp-persist module followed by our own module for Guice.
        Injector injector = Guice.createInjector(warpModule, buildWebModule());
        //And we'll probably be wanting to use some injecting inside of our Wicket classes
        addComponentInstantiationListener(new GuiceComponentInjector(this, injector));

For completeness, here’s the buildWebModule() method inside my WebApplication. There’s no magic here. I’m using a Hibernate annotation configuration and telling it about my DAO classes.

    protected Module buildWebModule()
    {
        return new Module()
        {
            public void configure(Binder binder)
            {
                AnnotationConfiguration configuration = new AnnotationConfiguration().configure();
                binder.bind(Configuration.class).toInstance(configuration);
                configuration.addAnnotatedClass(Game.class);

                binder.bind(GameDAO.class).to(GameHibernateDAO.class);
            }
        };
    }

With this all set, I can take those DAO and use them finally! Let’s get a session.

public abstract class HibernateDAO<T, ID extends Serializable> implements DAO<T, ID>
{
    @Inject
    protected Provider<Session> session;

And more importantly, let’s start a transaction.

    @Transactional
    Game getGame(long id)
    {
        return gameDAO.findById(id);
    }

And that’s all. If you’re already running 1.0 versions of everything, it’s not too bad to upgrade to 2.0. Practically everything is backward compatible and just deprecated at this point. So if you want to be on the bleeding edge of Hibernate 3.5, Guice 2.0, Warp-persist 2.0, Wicket 1.4.2, well, there you go.

Wicket – First Thoughts

I decided to take a break from my duel with Tapestry and look into Wicket. It’s similar to Tapestry in that it’s a Java component-based web framework and I was really tired of smashing my head into the wall with Tapestry, so I started playing around with it.

I’m only had a handful of hours into it, so take it all with a grain a salt. The same with my Tapestry thoughts as well.

The code is more concrete in Wicket. I can write regular Java code that uses interfaces and inheritance to let IntelliJ IDEA know that the code is actually being used somewhere. Compared this to Tapestry which leaves my IDE with a plethora of grayed methods and variables.

By convention is fine in the beginning of a project, but it can become a nightmare as you work to maintain that code and forget whether that “unused” method is really used or not. I’ll take a extending a class over magic methods any time.

However, as I’ve gotten older, I’ve become lazier. The second thing I noticed once I got a project running is how little Wicket provides out-of-the-box. IoC? Install your own. ORM? Find it yourself.  Wicket simply manages the “web” portion of being a web framework.

And that’s not necessarily a bad thing. It’s not shameful to do one thing well. But it does mean, I have to do more work at the beginning to make a useful web application. I spent last night getting Guice, warp-persist, and Hibernate to play nice. (There’s probably a useful blog post from that experience). Getting frameworks to work together is not the most productive thing I can do with my time.

I don’t mean to turn this into a Tapestry vs Wicket article, but I can’t help but compare them. Tapestry, out of the box so to speak, does offer IoC and Hibernate components. And Tapestry does a decent job without becoming a bloated, huge monster.

If I just went by Tapestry and Wicket, I would think open source fails at documentation. Luckily, Hibernate and Spring prove that good documentation is possible. I think both Wicket and Tapestry just need to reorganize most of what they already have written. To find the useful Wicket documentation, you have to scroll past the blurbs about how cool Wicket is to use, how you should have been using it yesterday already, and how you can get it now. Move past all that and you can get find some useful info scattered in the wiki.

As my spare time allows, I’ll continue to play around with both Tapestry and Wicket. I don’t want to rush into any decision and be stuck with it for the next few years. Right now, neither is jumping out and screaming “productivity” to me.

Tapestry Growing Pains

I really want to like Tapestry 5. How can a Java web developer not want to like something that basically describes itself as not-Struts? However, there have been some frustrations as I’ve prototyped AtomicGamer 2.0.

Documentation

Tapestry 5 actually has a good amount of documentation. The API documentation is commented. A tutorial is available. Several of the concepts have long and detailed pages of text.

I just can’t find anything.

There’s 5 separate pages on component functionality, but I can’t find out how to actually inject a component onto a page. I know I saw it somewhere, but it’s lost now. There’s InjectComponent and Component annotations, but I don’t know what the difference is between them and I’m sure the difference is significant.

I need more information about contexts, but it took forever to learn that they are a page rendering request feature and documented under page navigation. I apparently should have known this.

I think the problem is the layout of the documentation rather than the contents. I understand what I want to do, but I cannot map my concepts into Tapestry concepts. Hence, the documentation is painful to use at best.

By Convention

I was a Java and C++ developer, but I’m currently employed as a PHP web developer. Being the only “Java guy” at work, I get some flak thrown at me, but I like to toss some back. For example, PHP is dynamically-typed and interpreted. You never can really be sure if that method is used or if those parameters are the correct type. At least, not until run-time.

Thanks to the popularity of Ruby on Rails, “by convention” is the new big thing and Tapestry 5 loves it. My IntelliJ IDEA is filled with grayed method names since IDEA has no way to know if those methods or even classes are really in use. Is my onActivate method going to be called? Are those the correct parameter types? Who knows! Who cares! It’s by convention!

Annotations can help remove the gray, but then I’m just left with an IDE that thinks everything is used whether or not reality matches.

Prototype and AJAX

I’ve been on the fence with Tapestry 5 ever since I learned they embedded the Prototype Javascript library into it instead of being Javascript-framework neutral. I know jQuery already, do I really need to learn another Javascript library to use your Java web framework?

In theory, the standard components in Tapestry should hide Prototype and any Javascript library from me. For example, I want to display my BeanDisplay component. If a user clicks an edit button, it makes an AJAX call and loads a BeanEditForm component.

The Zone component may be able to do this, but I can’t get it to work how I want. Maybe because I’m haven’t found the right documentation. I’m left with implement the Javascript myself, but now I need to learn Prototype or learn how to safely embed jQuery and Prototype into my Tapestry 5 application.

I forget — what, exactly, is Tapestry suppose to be helping me accomplish?

I’ll admit, I’m a Tapestry newbie. However, I’m not a web newbie. I’m not a software engineer newbie. I’m sure Tapestry can be masterfully productive for some people, but I’m just not feeling it yet. Mostly, it feels like I’m fighting the framework and documentation to get it to do what I need it to do, and still failing.

Wicket? Stripes? Back to Struts 2? I don’t know.

GETting Tapestry

I’m trying to wrap my head around Tapestry’s contexts and event handling. The details are hidden in various sections of the documentation so it’s been a lot of trial and error. My goal is to be able to handle URL parameters in a way I can understand and process with some intelligence.

On a link, you can pass in a context:

<t:pagelink page=”games/Edit” context=”game”>${game.name}</t:pagelink>

In this instance, I’m passing a Hibernate entity to the game edit page. The URL will be http://www.atomicgamer.com/game/edit/5 where 5 is the entity id. On the edit page, we have several ways to handle get this value.

Note:  Since we passed the Hibernate entity and not just the id, Tapestry is smart enough to fetch the entity from the session. That’s why the following examples use variables of Game, not Long.

PageActivationContext Annotation

We can use the @PageActivationContext annotation and game will be automatically populated.

@Property
@PageActivationContext
private Game game;

onActivate Convention

If we defined a method onActivate(Game game), the parameter will be populated and we can assign it to an instance variable.

OnEvent Annotation

If we don’t want to use the onActivate convention, we can use the @OnEvent annotation on a method instead.

@OnEvent(”activate”)
public void annotationTriggered(Game game)…

While all of the above examples will get you access to the context, you will want to watch for the order of operations if you try to mix them.

I wanted to use PageActivationContext to automatically populate my instance variable. Then, I wanted to use the OnEvent method to check for a errors (the instance variable will be null if it doesn’t exist in the database).

However, the OnEvent method is fired before the PageActivateContext. The instance variable will be null regardless. I tried onActivate() (without any parameters) and while that is fired after the PageActivateContext, I felt that was getting too far into happenstance for me to feel happy about the solution.

The current order of operation appears to be:

  1. @OnEvent(”activate”)
  2. @PageActivationContext
  3. onActivate()

I think I’ll just stick with one of these instead of trying to mix them in any fashion.

I’m still learning Tapestry 5 and this is just one of the many growing pains I’m suffering at the moment. It’s not that the framework can’t do what I want it to do. Rather, I’m not sure which way I want it done that makes sense down the road. I’m lacking clarity in the big picture of a Tapestry application.

History Lessons

It’s been a very long road to get to where we are today. AtomicGamer’s origins can be traced back to 1997, when I was in high school.

I fell in love with Quake. My brother, who happened to work at an ISP, wanted to keep me out of his hair. My brother convinced me to start a Quake site. After tossing around a few domain names (back then, you could actually get .coms with some reliability), we settled on TeleFragged.

As it grew, we started another site called 3D Downloads that focused on providing gaming file downloads for free.  It was a relative success and grew along with TeleFragged.

As the Quake series dwindled in popularity, the name TeleFragged was rising in obscurity. After much deliberation, we combined 3D Downloads and TeleFragged into AtomicGamer. In our 12 years, we’ve never achieved mainstream success, we keep on trucking.

At the very least, we’ve definitely churned out a lot of code and iterations.

  • 1997 – TeleFragged 1.0 – HTML/SSI
  • 1999 – TeleFragged 2.0 – PHP3
  • 2000 – 3D Downloads 1.0 – PHP3
  • 2002 – TeleFragged 3.0 – PHP3
  • 2002 – 3D Downloads 2.0 – PHP3
  • 2004 – 3D Downloads 3.0 – Java
  • 2007 – AtomicGamer 1.0 – PHP5

All dates are approximate and generated in part by the Internet Archive. TeleFragged went through some minor revisions during the years, but who’s keeping track really?

Hi?

Traditionally, we’ve kept  AtomicGamer and TeleFragged as a black box to our visitors, you.  We simply did stuff in the background and it would magically show up on AtomicGamer.  We’ve rarely catered or explained ourselves to you all.

I want to change that.

Few people know or care about the inner workings of AtomicGamer. To most people, it’s your run-of-the-mill gaming website. They get their downloads, read a couple reviews, and leave. But I know there must be people that have been following us since we were TeleFragged and started in 1997. We really do want to hear from you.

So, I’m starting this blog to get feedback and suggestions from you. I don’t expect a rush. I also be posting programming-related comments as I struggle with some new feature.

Welcome to AtomicGamer! Go post a comment. Assuming I’ve got Wordpress configured correctly.