2011-05-25

Hot deploy in Jetty a war file built by Jenkins

We use Jenkins to build our application as a war file and we wanted to deploy it in a Jetty server to provide a manual testing environment.
So, we installed Jetty on our Ubuntu machine using 'sudo apt-get jetty libjetty-extra'
By default, when you copy a war file to Jetty webapps directory, nothing happens because Jetty scans this directory only at startup. So, we could stop/start Jetty daemon but it requires granting some privileges to the user running Jenkins and also we may want to deploy more than one application without stoppîng the ones that are already running.

Another solution is to use Jetty's ContextDeployer hot deploy.

This means editing /etc/jetty/jetty.xml and setting the scanInterval of ContextDeployer to lowest possible value: 1 second and then restart Jetty.


<Call name="addLifeCycle">
<Arg>
<New class="org.mortbay.jetty.deployer.ContextDeployer">
<Set name="contexts"><Ref id="Contexts"/></Set>
<Set name="configurationDir"><SystemProperty name="jetty.home" default="."/>/contexts</Set>
<Set name="scanInterval">1</Set>
</New>
</Arg>
</Call>

Now we must create a context file for our war file in /etc/jetty/contexts directory. Let's call it myapp.xml and let's point it to the location of the war file from last successful build in Jenkins.

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/myapp</Set>
<Set name="war">/var/lib/jenkins/jobs/MyProject/lastSuccessful/archive/trunk/myapp/target/myapp-0.0.1-SNAPSHOT.war</Set>
</Configure>

In order to deploy we just have to update our context file which can be easily done by executing 'touch /etc/jetty/contexts/myapp.xml'. We can then add it as a step in our main job in Jenkins if we want want continuous deployment or we can create a separate job if we want to control when we deploy a new version.

2011-04-27

GWT testing using Maven and Eclipse

When developing an application using Google Web Toolkit, it's a good practice to have both unit tests which do not require GWT and GWT tests for custom widgets or integration.
If you use Maven, you should apply the default naming convention of the Maven GWT plugin : *Test.java for standard unit tests and GwtTest*.java for GWT test cases. This way you have nothing to configure in your pom.xml.

Then you'll get errors from the GWT compiler because your unit tests are located in same java packages as your GWT application code but in a different source directory (e.g. src/test/java) and the compiler cannot find their source files. While these errors are not fatal and could be ignored, they are annoying. You can get rid of them by excluding your unit test classes by file pattern in your .gwt.xml file like below:


<source path='client' excludes="**/*Test.java,**/Mock*.java" />
<source path='shared' excludes="**/*Test.java" />

When you will run your GWT application, you may see error messages like:
[ERROR] [MyApp] - Line 13: No source code is available for type com.google.gwt.junit.client.GWTTestCase; did you forget to inherit a required module?

It does not prevent your application from running but they are annoying.
You can get rid of them by moving your GWT test cases to a dedicated source directory that you will exclude from the classpath of your Eclipse run configuration.
Finally, you'll put your standard unit tests under "src/test/java" and your GWT tests under "src/gwt-test/java". In your pom.xml, you have to use the build-helper-plugin to add this extra test directory.


<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>add-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/gwt-test/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>


If you want to speed up your GWT tests, you can launch them from a GWT test suite but then you must configure the Maven plugin for running only your test suite otherwise you'd end up running same test twice. My naming convention is Gwt*Suite.java which works well with the two other conventions.



<plugin>
<groupid>org.codehaus.mojo</groupid>
<artifactid>gwt-maven-plugin</artifactid>
<version>2.2.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Run only test suites not individual test cases. -->
<includes>**/Gwt*Suite.java</includes>
</configuration>
</plugin>

2010-04-29

Installer Hudson sur un serveur OVH

Pour installer le serveur d'intégration continue Hudson sur un serveur dédié OVH tournant Ubuntu, le plus pratique c'est de partir du package Debian et de suivre les instructions.
Le script va créer un utilisateur hudson, installer les binaires, les fichiers de configuration et le script de démarrage/arrêt.

Par défaut, l'espace disque est sous /home.
Si vous n'avez pas repartitionné votre serveur, vous pouvez déplacer la home et les logs de hudson.

# Stoppons Hudson
sudo /etc/init.d/hudson stop
# Passons sous /home/hudson
sudo mv /var/lib/hudson /home
sudo ln -s /home/hudson /var/lib/hudson
sudo mkdir /home/hudson/log
sudo mv /var/log/hudson/hudson.log /home/hudson/log/hudson.log
sudo ln -s /home/hudson/log/hudson.log /var/log/hudson/hudson.log
sudo chown -R hudson:nogroup /home/hudson
# Demarrons Hudson
sudo /etc/init.d/hudson start

La version Debian est un peu en retard sur la dernière version d'Hudson, voici comment la mettre à jour:

# Stoppons Hudson
sudo /etc/init.d/hudson stop
# Sauvegarde du war existant et download de la derniere version
cd /usr/share/hudson
sudo mv hudson.war hudson.war.old
sudo wget http://hudson-ci.org/latest/hudson.war
# Demarrons Hudson
sudo /etc/init.d/hudson start


2010-04-17

Mise en place d'un serveur de build

Nous avions besoin d'un serveur de projet (wiki, intégration continue, subversion, ...), j'ai retenu 2 possibilités:
  • Une solution "Software as a Service"
  • Un serveur dédié
Software as a Service

On trouve beaucoup d'offres très bon marché (moins de 10 euros par mois pour une petite équipe) incluant un dépôt Subversion et une gestion de projet basée sur Trac ou Redmine mais il manque l'intégration continue

Atlassian propose avec JIRA Studio une solution complète a un prix correct et c'est probablement la solution que nous aurions retenue si nous n'avions pas aussi eu à installer d'autres applications comme Nexus. Donc nous sommes partis sur l'option serveur dédié en tout cas pour quelques mois.

Serveur dédié

J'ai regardé 3 solutions françaises: Dédibox, Gandi et OVH.

Gandi est une offre cloud computing très souple basée sur Xen, on achète des parts de serveur 12 € HT/mois qui correspondent à du CPU, de la RAM et du disque.
On peut provisionner plus de parts à l'heure près.
Cette offre me semblait sous dimensionnée en terme de CPU, disque et bande passante et dès qu'on rajoute des parts, la facture monte vite.

J'ai ensuite comparé un Dedibox XL et un OVH Super plan mini qui sont assez proches à 49.99€ HT/mois.
Au final j'ai opté pour OVH principalement parce qu'ils incluaient 100 Go d'espace de sauvegarde contre 10 chez Dedibox. De manière générale, quand on a beaucoup de données à sauvegarder, les prix grimpent.

Mon serveur était disponible moins d'une heure plus tard.






2009-04-09

Créer des interfaces java est-il anti agile?

Créer des interfaces java facilite le découplage, permet d'utiliser des mocks pour les test unitaires, de changer d'implémentation, de créer des points d'interception mais peut-on considérer que c'est une pratique anti agile? Ma réponse est bien sur "Ça dépend" ;)

Oui si on considère que c'est du sur design (YAGNI) et qu'on attend d'avoir complètement défini les interfaces pour commencer a implémenter les classes concrètes.

Le Test Driven Design conduit à créer des classes concrètes, introduire des interfaces dans ce processus itératif le ralentit.

Par contre, on peut au cours d'une session TDD avoir besoin de créer une implémentation mock d'un DAO par exemple et au final en extraire une interface par refactoring quand le design s'est stabilise. On obtient ainsi une interface dont la conception a émergé du TDD et un mock utile pour les test unitaires des classes consommant cette interface.

Il ne s'agit pas non plus de créer des interfaces a tout bout de champ mais la où ça fait sens comme la couche d'accès aux données (d'où mon exemple sur un DAO) ou tout point d'intégration avec un module ou un composant de l'infrastructure.

Donc c'est plutôt le moment ou l'on crée une interface et la façon dont on la crée qui peut être non agile.

2008-08-10

FindBugs on a Wicket + Spring application

FindBugs is a great static analysis tool and it helped me to find several bugs but recently we switched to Wicket 1.3 framework for web development and we got few false positives which were related to using Spring with Wicket.

Serialization

We had Wicket page classes with fields being injected by Spring using the @SpringBean annotation.
FindBugs complained that these fields should have been serializable or transient (SE_BAD_FIELD error) and this seemed an issue as Wicket does use serialization a lot to save pages into its session.
In fact, it turned out to be a false postive because Wicket manages this by using Spring dynamic proxies.


Unitialized fields in constructor

Another FindBugs complaint in Wicket page classes was about de-referencing fields in constructor before having initialize them (UR_UNINIT_READ error).
Again, this turned out to be a false positive because our pages did extend WebPage class that takes care of injecting all SpringBean annotated fields using a PropertyResolver.
Something to remember: the default PropertyResolver is able to initialize private fields and ignores setters.

Conclusion

Using Spring beans in Wicket pages introduces a lot of dynamicity that defeats static analysis when dealing with object initialization and serialization.
FindBugs is so valuable that it is worth excluding these rules on Pages and having a naming convention for these classes makes it easier to do. Our page classes are now named with the "Page" suffix.

2008-04-19

My first (useful) script in Groovy

We had a large number of XML files to modify and found that Groovy with its GPath syntax was the right tool. Here is an example that takes a (simplified) XML file and changes the value of one attribute for a subset of nodes.

<?xml version="1.0"?>
<design>
<process>
<variable id="V1" visible="true" />
<variable id="V2" visible="false" />
<variable id="V3" visible="false" />
</process>
</design>


And the script to change all XML files in the current directory.
I found it easier to write and debug it than using a mix of java and XSL.


def basedir = new File( ".")

// Create a directory for patched files
new File("patched").mkdir()

// Get files with ".xml" extension
files = basedir.listFiles().grep(~/.*\.xml$/)

// Iterate on the files
files.each {
patchXML(it)
}

def patchXML(file) {
println "-------------------"
println "Patching $file.name"

def design = new XmlParser().parse(file)
def modified = false

for (variable in design.process.variable) {
switch (variable.@id) {
case "V1":
case "V2":
if (variable.@visible == "false") {
variable.@visible = "true"
modified = true
}
}
}
if (modified) {
new File("patched/$file.name").withPrintWriter() {
new XmlNodePrinter(it).print(design)
}
println "Patched $file.name is in \"patched\" directory"
} else {
println "$file.name was not modified"
}
println "-------------------"
}