Get the URL of a Page in Tapestry 5

Sometimes your program will need to get the full URL of a page to send in an email, share on Facebook or run through a URL shortener to send to Twitter. You can do this by injecting the HTTPRequest and the building a string representation of the URL manually, but this is a lot more work that it needs to be. Here is a simple way to do it using Tapestry’s PageRenderLinkSource and a Link object.

@Inject
PageRenderLinkSource linkSource

/**
 * Return a string with the full URL of MyPage with a 
 * context of 5.
 */
public String getPageURL() {
	Link link = linkSource.createPageRenderLinkWithContext("MyPage", 5);
	return link.toAbsoluteURI();
}

I’ve hard coded the value 5 as the context–you’d normally set that based on whatever you are wanting to share. This method will return a link to MyPage with a context of 5, so it will probably look something like: http://www.myserver.com/MyPage/5. One of the nice things about this solution is that it will automatically handle things if your application needs to use a URL like: http://www.myserver.com/myapp/MyPage/5

Java Thinks There Are 13 Months

I was working on a component for credit card processing where I needed to get the months of the year. Trying to plan ahead, I decided to use the DateFormatSymbols so it could easily handle localization if it ever needed to be used in a different language.

It wasn’t doing what I expected and I finally found the problem with the following code:

DateFormatSymbols symbols = new DateFormatSymbols();
String[] months = symbols.getMonths();
int numOfMonths = months.length
System.out.println(numOfMonths);

The output is 13. If you list all of the strings in the months array, you’ll find the first 12 are what you’d expect, but there is a 13th blank month at the end.

It turns out that there are some lunar based calendars that have to add a “leap month” every so many years. This month is called Undecimber and comes after December. However, I’m not clear why Java is giving me a blank month. If the given locale only has 12 months, what is the value of giving back a 13 item array and just leaving one blank?

Adding Context to Links in Tapestry

Introduction

Tapestry looks at the URL and assumes that anything after the page name is the context that is being passed to the page. So a URL like:

http://www.site.com/MyPage/5

Is going to expect that the number 5 is the context needed on MyPage.  You can have multiple contexts passed to a page like this:

http://www.site.com/MyPage/5/foo

This will pass two contexts to the page.  Depending on what the page is expecting to receive in the context, these values may be uses as they are or they might be coerced into something else.  For example, the five might represent the ID of some object that will be retrieved from the Hibernate.

In the case of a single context value being passed, MyPage can get the value into a page property by doing something like this:

@PageActivationContext
@Property
private int myNumber;

The PageActivationContext annotation tells Tapestry to put the value on the URL that comes after the page into that variable. With Hibernate involved you can do something like this:

@PageActivationContext
@Property
private Person aPerson;

Tapestry will tell Hibernate to get the person object with an id of 5 and assign it to the variable aPerson.

If the page has multiple context values being passed, they are handled through the onActivate method:

public void onActivate(int myNumber, String myString) {
//set appropriate page properties
}

PageLink

Thats a little background. The main thing I wanted to look at in this post is how to get links that pass in these context values in the first place.

This snippet shows a pagelink that will give us something of the form:
http://www.site.com/MyPage/5
Assuming that the person.id is 5.

<t:pagelink page="MyPage" context="person.id">${person.name}</t:pagelink>

If we need to pass multiple context values it can be done like this.

<t:pagelink page="MyPage" context="[person.id,order.id]">view order</t:pagelink>

If person.id is 5 and order.id is 22 this link will produce:
http://www.site.com/MyPage/5/22

Returning Page and Context from a Method

Tapestry lets us return values in Java that specify the next page to load. This is commonly seen in the onSuccess method which can look like this:


    public Object onSuccess() {
       //Do what needs to be done
       return "MyPage";
    }

At first it looks like we can simply append the values like:


    public Object onSuccess() {
       //Do what needs to be done
       return "MyPage/" + person.id + "/" + order.id;
    }

But this does not work because Tapestry can’t find the page “MyPage/5/22”. It only knows about “MyPage”. To add a context you will need to inject the pageRenderLinkSource and use it to build the context.

    @Inject
    private PageRenderLinkSource pageRenderLS;

    public Object onSuccess() {
       //Do what needs to be done
       return pageRenderLS.createPageRenderLinkWithContext("MyPage", person.id);
    }

You can add additional context values to the parameters as well.

    @Inject
    private PageRenderLinkSource pageRenderLS;

    public Object onSuccess() {
       //Do what needs to be done
       return pageRenderLS.createPageRenderLinkWithContext("MyPage", person.id, order.id);
    }

Starcraft and Genetic Algorithms

I read an interesting article about using genetic algorithms to find the optimal first steps for Starcraft 2 gameplay. Starcraft is a bit like chess in that the first few minutes of play can usually be done in a prescribed order. In chess this is known as your opening moves. In Starcraft this is known as the build order.

You can find the code posted here. It uses JGAP which is something I’ve fiddled around with before as part of the genetic algorithms used in Robocode.

Eclipse Multi User Plugin Management

I am working on writing the curriculum for a class on Java Programming. I want it to scale and I want to be able to focus on writing code–not fiddling around with the tools. I don’t want it to turn into a big tech support nightmare to try to figure out why this student can’t seem to get the JUnit to run and why another can’t seem to see the GIT plugin in Eclipse.

To accomplish that I’m going to run Eclipse on a server and give students access through X2Go. They can run Eclipse on their local machine, but it is up to them to support their own setup if they aren’t working on the server. To make this work I needed a way to make sure everyone was using the same set of plugins in Eclipse. The “dropins” folder does what I need.

If you install Eclipse through apt-get on Ubuntu, it will create a folder of /usr/share/eclipse under that folder is the plugin folder, etc. However, there is no dropins folder. You have to create it manually at /usr/share/eclipse/dropins. This folder is checked by Eclipse when it starts up and the plugins put in that folder are loaded and installed into the users’s environment. This means you can easily manage what plugins each user has installed without touching each user’s home directory.

If a user installs a plugin from Eclipse, it gets installed in /home/[username]/.eclipse/[version]/plugins/. If you set things up with plugins in the /usr/share/eclipse/dropins folder, their personal plugins directory can be completely empty.

The best way I’ve found for setting this up is to create a fresh user that has nothing in their personal plugins directory. Have that user launch Eclipse and then use Eclipse to install any plugins you want to add for everyone. Once they are added, close eclipse and move everything that is in /home/[username]/.eclipse/[version]/plugins/ to /usr/share/eclipse/dropins. This will make sure you get all the dependencies you need to make things run.

If you need to troubleshoot how things are being loaded from /usr/share/eclipse/dropins, look in the workspace that is being launched. It will be in something like /home/[username]/workspace/.metadata/.log