Monthly Archives: February 2010

A Google Analytics Plugin for Nexus

Since Firebase Community Edition uses Maven heavily, I realized I’d like to track our Nexus repository in Google Analytics. The Nexus Book says that there exists such a plugin already, but apparently now one knows where it is. So here’s my attempt to write a simple one.

If you’re an impatient kind, here’s the source code.

I created the project  in Eclipse, instructions here.

Now, let’s start off with a “repository customizer” which is Nexus extension point we’ll use to insert a custom request processor into every repository…

public class GaTrackerRepositoryCustomizer implements RepositoryCustomizer {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Inject
    @Named("gaTracker")
    private RequestProcessor gaTracker;

    @Override
    public void configureRepository(Repository rep) throws ConfigurationException {
        log.debug("Attaching tracker to: " + rep.getName());
        rep.getRequestProcessors().put("gaTracker", gaTracker);
    }

    @Override
    public boolean isHandledRepository(Repository rep) {
        boolean b = true;
        log.info("Handles repository '" + rep.getName() + "': " + b);
        return b;
    }
}

Not too complicated. We’re using injection to get hold of the actual tracker components and we’re inserting it into all repostories.

Wait, all repositories? Yes, and it’s problem I haven’t really figured out yet. Ideally I’d like to track “hosted” repositories only. However, we’ve configured all our builds to use a few common development “groups” for convenience. However, Nexus seems to treat groups as first class members, so even though an artifact may be deployed in a hosted repository while accessed through a group, the request processor will not get notified for the hosted repository, only the group. I tend to see “groups” as equivalent to “views” in which case I’d expect the hosted repository to be called as well, but, no…

Now, let’s create a request processor which will react when someone “reads” a path in a repository.  We’ll take it in pieces…

@Named("gaTracker")
public class GaTrackerRequestProcessor implements RequestProcessor {

    public static final String GA_TRACKER_ID =
        System.getProperty("cubeia.nexus.gaTrackerId");
    public static final String GA_REFERER_URL =
        System.getProperty("cubeia.nexus.gaRefererUrl");

    private final Logger log = LoggerFactory.getLogger(getClass());
    private final JGoogleAnalyticsTracker tracker;

    public GaTrackerRequestProcessor() {
        if(GA_TRACKER_ID == null) {
            String msg = "Missing system property 'cubeia.nexus.gaTrackerId'";
            throw new IllegalStateException(msg);
        }
         log.info("Creating new tracker, with id: " + GA_TRACKER_ID);
        tracker = new JGoogleAnalyticsTracker("nexus", "read", GA_TRACKER_ID);
        checkRefererUrl();
        adaptLogging();
    }

    [...]

We configure the plugin via system properties (not too beautiful, I know), the “tracker id” is the google tracking code id and is mandatory, and the “refer url” will be set on the call to GA if available.  We’re using the JGoogleAnalytics library to call GA for us. Also, I’m being naughty and throwing an illegal state exception if the tracker id is missing, since GA updates every 24 hours we’d like to be notified on errors early.

There’s  two methods above, one sets the logging in the tracker code to use to slf4j logger instead and the other checks for and sets the referer URL:

private void adaptLogging() {
    /*
     * Adapt the logging to use slf4j instead.
     */
    tracker.setLoggingAdapter(new LoggingAdapter() {

        public void logMessage(String msg) {
            log.debug(msg);
        }

        public void logError(String msg) {
            log.error(msg);
        }
    });
}

private void checkRefererUrl() {
    if(GA_REFERER_URL != null) {
        /*
         * If we have a referer URL we need to set this. However, the
         * tracker does not have a getter for the URL binding strategy, so
         * we'll simply create a new one, ugly, but works.
         */
        log.info("Modifying GA tracking to use referer URL: " + GA_REFERER_URL);
        GoogleAnalytics_v1_URLBuildingStrategy urlb;
        urlb = new GoogleAnalytics_v1_URLBuildingStrategy("nexus", "read", GA_TRACKER_ID);
        urlb.setRefererURL("http://m2.cubeia.com");
        // set new referer
        tracker.setUrlBuildingStrategy(urlb);
    }
}

Not too complicated eh? The only thing to note is that the only way to set the refer URL isby creating a new URL building strategy. Well, I can live with that.

Before we go on we’ll create a small subclass on FocusPoint which we’ll use for tracking. Since JGoogleAnalitycs is made primarily for applications the focus point will URL encode itself, however, that won’t work for us, so we need to override it’s getContentURI method:

/**
 * Simple inner class that adapts the content URI to
 * not be URL-escaped.
 */
private static class Point extends FocusPoint {

    public Point(String name) {
        super(name);
    }

    @Override
    public String getContentURI() {
        return getName();
    }
}

And finally we’ll tie it all toghether. We’ll react on “read” actions, create a URI (of form ‘//path’) and track asynchronously (which will spawn a new thread for calling GA:

 public boolean process(Repository rep, ResourceStoreRequest req, Action action) {
    if(action == Action.read) {
        /*
         * 1) create path by appending repo path to repo id
         * 2) create a subclass of focus point that handles proper URI's
         * 3) track asynchronously, this will perform the tracking on a new thread
         */
        String path = rep.getId() + req.getRequestPath();
        log.debug("Tracking path: " + path);
        FocusPoint p = new Point(path);
        tracker.trackAsynchronously(p);
    } else {
        log.debug("Ingoring action '" + action + "' for: " + req.getRequestPath());
    }
    return true;
}

And that’s it. It’s not perfect though ideally I’d like to track hosted repositories only, I’d like to avoid tracking crawlers and I would prefer not to configure via system properties (hint for you Nexus bundle users out there, you can set system properties in the “conf/wrapper.conf” files), but I’ll leave those for a rainy day.

Here’s the source code as a Maven project.

Enjoy!

Update: If you download and try to compile, you will probably be missing the JGoogleAnalytics lib. You’re welcome to use our, GA-tracked, repository if you like :-)

<repository>
  <id>cubeia-nexus</id>
  <url>http://m2.cubeia.com/nexus/content/groups/public/</url>
  <releases>
    <enabled>true</enabled>
  </releases>
  <snapshots>
    <enabled>true</enabled>
  </snapshots>
</repository>
By |Thursday, February 11, 2010|java|1 Comment

Problems with Comments + Flash

For some reason I can’t seem to comment at the moment. So if you’re experiencing problems, please bear with us.

In the last post, netsql asked about action script support (as upposed to Flex) for Firebase, and I’ll post my answer here for the time being: Good point, it should be documented. As far as I know, but I’m no Flash coder, the API is the same. So you should be able to download it and then use the Flex examples to find your way.

Drop me a line (lars.j.nilsson at cubeia dot com) and I’l let you know when we have something up on the wiki. Otherwise just hang around, we’ll get it updated in the next few days.

Update: Oh, apparently commenting is on again.

By |Wednesday, February 10, 2010|cubeia|Comments Off on Problems with Comments + Flash

Write a multiplayer game in 10 minutes or less!

As I was surfing along the other day it struck me that one of the coolest things about Firebase Community Edition is how incredibly fast you can get going. Do you think the title is a boast? Well, in a manner of speaking it is,  you see: we’re using Maven to build, and if you haven’t used Maven to build a Flex/Flash client before, Maven is going to start with downloading half of the Internet for you, and that will inevitably slow you down and may take a few minutes. But hear me! If you have used Maven before, and if you allow for the first time Maven will download the artifacts needed to compile the Firebase game and the Flex client, then I stand firm: you will have a Flex client and a Java server going in less than 10 minutes!

Do you want to get started on a multiplayer game really, really fast? Here’s two different ways:

  • The Extreme Quick Start – This hard-core, and Maven only, quick start will have you up in less than 5 minutes (excluding Maven download times). It does not however, send actual game actions between the client and the server, all you can do is join/leave tables and chat with other players… Excuse me? All you can do?! It’s completely awesome if you ask me.
  • The Beloved Hello World – This tutorial can be done with Maven and optionally Flexbuilder. It will explain along the way what happens, and it will also replace the Firebase standard chat with game actions (also chat) showing you how to communicate properly between game server and client. 10 minutes? Well, if you’re impatient and a fast reader, or if you do it twice you will most certainly beat the 10 minute mark.

If you ask me, and I’m obviously biased, this is extremely cool. Of course, this isn’t actually a game yet and there’s a lot more to learn before launching your own international success on Firebase, but hell, you want to write a game? Hop right to it!

Update: The commenting system seems to be behaving badly.  Even I can’t seem to comment at the moment. Please check the main blog for updates. I’ll be looking at switching blog system now…

By |Wednesday, February 10, 2010|firebase|Comments Off on Write a multiplayer game in 10 minutes or less!

Pomodairo v1.7 Released

We have released a new version of Pomodairo, a tool that is designed to help you work with the Pomodoro technique.

The new version is 1.7 and contains a cleaner graphical interface and some experimental Twitter integration. Check it out here: http://code.google.com/p/pomodairo/

pomodoro client

Happy Pomodoroing!

Fredrik

By |Wednesday, February 3, 2010|misc|3 Comments