<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Cubeia Blog &#187; plugin</title>
	<atom:link href="http:///index.php/blog/archives/tag/plugin/feed" rel="self" type="application/rss+xml" />
	<link>http://www.cubeia.com/index.php/blog</link>
	<description>Tech Lust and Lust of Words</description>
	<lastBuildDate>Tue, 31 Jan 2012 13:11:30 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>A Google Analytics Plugin for Nexus</title>
		<link>http://www.cubeia.com/index.php/blog/archives/39</link>
		<comments>http://www.cubeia.com/index.php/blog/archives/39#comments</comments>
		<pubDate>Thu, 11 Feb 2010 10:40:12 +0000</pubDate>
		<dc:creator>larsan</dc:creator>
				<category><![CDATA[java]]></category>
		<category><![CDATA[google analytics]]></category>
		<category><![CDATA[maven]]></category>
		<category><![CDATA[nexus]]></category>
		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://www.cubeia.com/index.php/blog/archives/39</guid>
		<description><![CDATA[Since Firebase Community Edition uses Maven heavily, I realized I&#8217;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&#8217;s my attempt to write a simple one.
If you&#8217;re an impatient kind, here&#8217;s the source code.
I [...]]]></description>
			<content:encoded><![CDATA[<p>Since <a href="http://www.cubeia.org">Firebase Community Edition</a> uses Maven heavily, I realized I&#8217;d like to track our Nexus repository in Google Analytics. The <a href="http://www.sonatype.com/books/nexus-book/reference/">Nexus Book</a> says that there exists such a plugin already, but apparently now one knows where it is. So here&#8217;s my attempt to write a simple one.</p>
<p>If you&#8217;re an impatient kind, <a href="/docs/misc/tracker-1.0-SNAPSHOT-project.zip">here&#8217;s</a> the source code.</p>
<p>I created the project  in Eclipse, instructions <a href="http://www.sonatype.com/people/2010/01/writing-a-nexus-plugin-using-m2eclipse/">here</a>.</p>
<p>Now, let&#8217;s start off with a &#8220;repository customizer&#8221; which is Nexus extension point we&#8217;ll use to insert a custom request processor into every repository&#8230;</p>
<pre>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;
    }
}</pre>
<p>Not too complicated. We&#8217;re using injection to get hold of the actual tracker components and we&#8217;re inserting it into all repostories.</p>
<p>Wait, <em>all</em> repositories? Yes, and it&#8217;s problem I haven&#8217;t really figured out yet. Ideally I&#8217;d like to track &#8220;hosted&#8221; repositories only. However, we&#8217;ve configured all our builds to use a few common development &#8220;groups&#8221; 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 &#8220;groups&#8221; as equivalent to &#8220;views&#8221; in which case I&#8217;d expect the hosted repository to be called as well, but, no&#8230;</p>
<p>Now, let&#8217;s create a request processor which will react when someone &#8220;reads&#8221; a path in a repository.  We&#8217;ll take it in pieces&#8230;</p>
<pre>@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();
    }

    [...]</pre>
<p>We configure the plugin via system properties (not too beautiful, I know), the &#8220;tracker id&#8221; is the google tracking code id and is mandatory, and the &#8220;refer url&#8221; will be set on the call to GA if available.  We&#8217;re using the <a href="http://code.google.com/p/jgoogleanalytics/">JGoogleAnalytics</a> library to call GA for us. Also, I&#8217;m being naughty and throwing an illegal state exception if the tracker id is missing, since GA updates every 24 hours we&#8217;d like to be notified on errors early.</p>
<p>There&#8217;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:</p>
<pre>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);
    }
}</pre>
<p>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.</p>
<p>Before we go on we&#8217;ll create a small subclass on <em>FocusPoint</em> which we&#8217;ll use for tracking. Since JGoogleAnalitycs is made primarily for applications the focus point will URL encode itself, however, that won&#8217;t work for us, so we need to override it&#8217;s <em>getContentURI</em> method:</p>
<pre>/**
 * 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();
    }
}</pre>
<p>And finally we&#8217;ll tie it all toghether. We&#8217;ll react on &#8220;read&#8221; actions, create a URI (of form &#8216;//path&#8217;) and track asynchronously (which will spawn a new thread for calling GA:</p>
<pre> 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;
}</pre>
<p>And that&#8217;s it. It&#8217;s not perfect though ideally I&#8217;d like to track hosted repositories only, I&#8217;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 &#8220;conf/wrapper.conf&#8221; files), but I&#8217;ll leave those for a rainy day.</p>
<p><a href="/docs/misc/tracker-1.0-SNAPSHOT-project.zip">Here&#8217;s</a> the source code as a Maven project.</p>
<p>Enjoy!</p>
<p><strong>Update:</strong> If you download and try to compile, you will probably be missing the JGoogleAnalytics lib. You&#8217;re welcome to use our, GA-tracked, repository if you like <img src='http://www.cubeia.com/components/com_wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<pre>&lt;repository&gt;
  &lt;id&gt;cubeia-nexus&lt;/id&gt;
  &lt;url&gt;http://m2.cubeia.com/nexus/content/groups/public/&lt;/url&gt;
  &lt;releases&gt;
    &lt;enabled&gt;true&lt;/enabled&gt;
  &lt;/releases&gt;
  &lt;snapshots&gt;
    &lt;enabled&gt;true&lt;/enabled&gt;
  &lt;/snapshots&gt;
&lt;/repository&gt;</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cubeia.com/index.php/blog/archives/39/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

