How to set up a public Maven & Git Repo with sbt & Jetty 7

Nov 13, 2010 16:20 · 1252 words · 6 minutes read sbt Maven git Jetty

Update (7th August 2017): This and other articles are kept for reference. Git Repos are now on github, maven repos on maven central - though most projects are discontinued.

As I want to publish some of my code in the next months, I wanted to be able to provide a maven repository for compiled libraries and git repositories for the source. I could of course have used github et al., but I kinda liked the idea of repos under maven.henkelmann.eu and git.henkelmann.eu

“If there’s anything more important than my ego on this ship, I want it caught and shot right now!”

The Ingredients

Luckily other people have already provided instructions on how to do most of the necessary work, so all I had to do was piece the parts together. Googling for instructions on how set up your own maven repo using sbt, I found [this excellent article](“http://brizzled.clapper.org/id/100/"). Following those instructions, I was only left with one more task for my own public maven repo: Getting Jetty to serve static content out of a certain folder for the subdomain maven.henkelmann.eu.

I use Jetty 7, so a quick look at the Jetty 7 documentation yielded the necessary config for virtual hosts. I only found a static content configuration for Jetty 6, so I had to adapt it to the slightly changed API of Jetty 7.

The necessary config for the git repository was also easily found. The git book has an easily understandable chapter on how to set up public repositories. I was going to do it properly using the git daemon at first, but as I already had the necessary config for Jetty 7 for the maven repo and subdomain I simply repeated the steps for exporting a git repo via http.

The steps en detail

The following is the config for my jetty/maven/git/sbt combo. For your own setup just replace the respective paths and domains.

First, the Jetty 7 config to host the two static resources for git and maven alongside my blog webapp:

<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Set name="handler">
    <!--We need three handlers, one for the webapp, one for maven, one for git, 
    so we wrap them in a handler collection -->
    <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
    <Set name="handlers">
    <Array type="org.eclipse.jetty.server.Handler">
        <!-- The first context is for the blog engine -->
        <Item>
        <New class="org.eclipse.jetty.webapp.WebAppContext">
            <Set name="Server"><Ref id="Server" /></Set>
            <Set name="ContextPath">/</Set>
            <Set name="War"><Property name="jetty.home" default="." />/webapps/blog.war</Set>
            <!-- The following config lets the webapp only listen to the 
            requests directly to the main domain and the www subdomain
            (and localhost).-->
            <Set name="VirtualHosts">
            <Array type="java.lang.String">
                <Item>127.0.0.1</Item>
                <Item>www.henkelmann.eu</Item>
                <Item>henkelmann.eu</Item>
            </Array>
            </Set>        
            <!-- The following disables session handling via url parameters so we 
            always have nice URLs-->
            <Get name="SessionHandler"><Get name="SessionManager">
            <Set name="SessionIdPathParameterName">none</Set>
            </Get></Get>
        </New>
        </Item>
        <Item>
        <!-- This context handler only serves static context from the given folder-->
        <New class="org.eclipse.jetty.server.handler.ContextHandler">
            <Set name="Server"><Ref id="Server" /></Set>
            <Set name="ContextPath">/</Set>
            <!-- This is the directory where we put the maven repo-->
            <Set name="ResourceBase">/srv/maven/</Set>
            <!-- Restrict responses to requests to the maven subdomain-->
            <Set name="VirtualHosts">
            <Array type="java.lang.String">
                <Item>maven.henkelmann.eu</Item>
            </Array>
            </Set>
            <!-- This is different from Jetty 6. The method was renamed 
            from addHandler to setHandler -->
            <Set name="Handler">
            <New class="org.eclipse.jetty.server.handler.ResourceHandler"/>
            </Set>
        </New>
        </Item>        
        <Item>
        <!-- Now we do the same for the git repo-->
        <New class="org.eclipse.jetty.server.handler.ContextHandler">
            <Set name="Server"><Ref id="Server" /></Set>
            <Set name="ContextPath">/</Set>
            <Set name="ResourceBase">/srv/git/</Set>
            <Set name="VirtualHosts">
            <Array type="java.lang.String">
                <Item>git.henkelmann.eu</Item>
            </Array>
            </Set>
            <Set name="Handler">
            <New class="org.eclipse.jetty.server.handler.ResourceHandler"/>
            </Set>
        </New>
        </Item>
    </Array>
    </Set>
    </New>
</Set>

Now you need to create the empty directories and make sure they belong to the user that runs Jetty (I assume it is called jettyuser and member of the group jettyuser here):

mkdir /srv/maven
chown jettyuser:jettyuser /srv/maven
mkdir /srv/git
chown jettyuser:jettyuser /srv/git

Now that this done we only need to configure sbt to publish to our maven repo. For more details on sbt and maven I recommend first reading the original article I got the config from. I publish to a staging server named taupo that I can access via ssh, so I add the following to the project definition file (the .scala file in the project/build/ directory) of every sbt project I want to publish:

override def managedStyle = ManagedStyle.Maven
//do not add username or password to the following line if you also publish the 
//source via git! Also remember that if you have already done so and removed it, 
//it will still be viewable in the git repo's history!
lazy val publishTo = Resolver.ssh("Christoph's Maven Repo", "taupo", "/srv/maven/")

The Resolver tells sbt how to transfer the files. For alternatives to ssh, see the sbt documentation on the subject. In the case of the ssh resolver I used in the above example, sbt will pop up a window where you can enter your username and password.

The last bit is the preparation of the git repository/repositories you want to publish. Again, before you just copy/paste anything here, you should check out the respective chapter in the git book. In my case, the project I want to publish is named junit_xml_listener and is in the folder ~/sbt_projects/junit_xml_listener.

# 'junit_xml_listener.git' is the name of the folder that contains the cloned repo
# The --bare option creates a clone that has no checked out files..
cd ~/sbt_projects
git clone --bare junit_xml_listener/.git junit_xml_listener.git
#This magic file tells the git daemon that it is ok to make the 
#repo publicly available. This is unnecessary for the export via Jetty/http 
#of course, but I do it now so I can switch to the git daemon later if I like.
touch junit_xml_listener.git/git-daemon-export-ok
#Now create the info necessary for git clients to pull via http.
cd junit_xml_listener.git
git --bare update-server-info
#Rename the sample post-update hook and make it executable, 
#so it is called when you push changes to your public repo.
#When it is executed after an update it will update the server info again.
mv hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update
#Now copy your cloned bare repo to the folder we created for jetty to read.
#In my case, I use scp for that: 
cd ..
scp -r junit_xml_listener.git jettyuser@taupo:/srv/git/
#Now you can remove the local copy of the cloned repo.
rm -rf junit_xml_listener.git

If you have changes now you can push them to your cloned public repo like so:

cd ~/sbt_projects/junit_xml_listener
git push ssh://jettyuser@taupo/srv/git/junit_xml_listener.git

A little extra: detour via a staging server

As mentioned above, I always deploy everything on a staging server first to run a few last checks. As all our repos only consist of static files served by jetty, I simply use rsync to throw them on the actual server this blog runs on. So the following rsync commands actually deploy the maven repo, the git repos and the webapp:

rsync --delete --recursive --times --perms --compress --verbose --stats -e ssh /srv/blog/server/jetty/webapps/blog.war/ jettyuser@henkelmann.eu:/srv/blog/server/jetty/webapps/blog.war/
rsync --delete --recursive --times --perms --compress --verbose --stats -e ssh /srv/maven/ jettyuser@henkelmann.eu:/srv/maven/
rsync --delete --recursive --times --perms --compress --verbose --stats -e ssh /srv/git/ jettyuser@henkelmann.eu:/srv/git/

How to use the repos

Now that everything has been put to the live server, you can access the git repo via http:

git clone http://git.taupo/junit_xml_listener.git

To use the compiled lib in another project, the maven repo can be referenced like so:

val repo = "Christoph's Maven Repo" at "http://maven.henkelmann.eu/"

The library itself is included in your project or plugin configuration like this:

val junitXml = "eu.henkelmann" % "junit_xml_listener" % "0.1"</code></pre>

The first part of the reference (in my case eu.henkelmann) is the project.organization parameter in the project/build.properties file of your sbt project. The second and third part are the project.name and project.version parameters.

I hope this description helps a bit if you plan to do something similar.