Affichage des articles dont le libellé est Hadoop. Afficher tous les articles
Affichage des articles dont le libellé est Hadoop. Afficher tous les articles

samedi 15 décembre 2012

Run background process for your Maven integration tests

The Maven Failsaif plugin makes it very simple to run integration test written with JUnit or TestNG. This example shows how to use the Maven Failsafe Plugin to run Integration Tests that properly starts and stop a Jetty server during both the pre-integration-test phase and the post-integration-test phase. This solution can be easyly derivated to start and stop any background process that is well integrated as a Maven plugin :
<plugin>
    <groupId>${plugin.groupId}</groupId>
    <artifactId>${plugin.artifactId}</artifactId>
    <version>${plugin.version}</version>
    <configuration>
        <anything>...</anything>
    </configuration>
    <executions>
        <execution>
            <id>do-it-before</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>${plugin.start.goal}</goal>
            </goals>
            <configuration>
                <something>...</something>
            </configuration>
        </execution>
        <execution>
            <id>do-it-after</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>${plugin.stop.goal}</goal>
            </goals>
            <configuration>
                <whatever>...</whatever>
            </configuration>
        </execution>
    </executions>
</plugin>
Sometimes, there is no plugin or maven integration that you can use to start and stop the background process(es) required by your intergation test case to run. You can start and stop the desired process(es) manually but let see how to do this properly with Maven.

Maven exec plugin

The Maven exec plugin provides 2 goals to help execute system and Java programs. However, the programs execution are blocking. It is thus impossible to run tests during the intergation-test phase with running programs started during the pre-integration-test phase. Hopefully, the Maven antrun plugin will help us...

Maven antrun plugin

The Maven antrun plugin provides the ability to run Ant tasks from within Maven. It can do anything you can put into an ant build.xml script. In particular, it is possible to use the Ant exec task with all its parameters. The solution to our problem is the spawn parameter of the exec task that will run the specified executable asynchronously in its own process :
<exec executable="command-to-run"
      dir="base/dir/for/the/command"
      spawn="true"> <!-- run it asynchronously and in background baby -->
    <arg value="arg"/>
    <arg value="arg"/>
    ...
    <arg value="arg"/>
</exec>
Okay but now, we want to start third parties programs with the Maven antrun plugin during the pre-integration-test phase, keeping them running during the integration-test phase and to shut them down during the post-integration-test phase. Assuming we have plateform dependent scripts to start and stop Zookeeper, Kafka or anything else... our pom.xml will look like this :
<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>start-third-parties</id>
            <phase>pre-integration-test</phase>
            <configuration>
                <target>
                    <exec executable="${run.command}"
                          dir="${basedir}/../scripts"
                          spawn="true">
                        <arg value="${run.command.additionnal.arg}"/>
                        <arg value="${basedir}/../scripts/${zookeeper.start.script}"/>
                    </exec>
                    <exec executable="${run.command}"
                          dir="${basedir}/../scripts"
                          spawn="true">
                        <arg value="${run.command.additionnal.arg}"/>
                        <arg value="${basedir}/../scripts/${kafka.start.script}"/>
                    </exec>
                </target>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
        <execution>
            <id>stop-third-parties</id>
            <phase>post-integration-test</phase>
            <configuration>
                <target>
                    <exec executable="${run.command}"
                          dir="${basedir}/../scripts"
                          spawn="false">
                        <arg value="${run.command.additionnal.arg}"/>
                        <arg value="${basedir}/../scripts/${zookeeper.stop.script}"/>
                    </exec>
                    <exec executable="${run.command}"
                          dir="${basedir}/../scripts"
                          spawn="false">
                        <arg value="${run.command.additionnal.arg}"/>
                        <arg value="${basedir}/../scripts/${kafka.stop.script}"/>
                    </exec>
                </target>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
       </execution>
    </executions>
</plugin>
There is no need to spawn the shutdown of our third parties programs, but it's possible too...

Okay but what if I want to use both Linux and Windows ?

No problem ! Maven profiles are here to help you. You just have to define two profiles wich <activation> relies on the os family like this :
<profile>
    <id>windows-properties</id>
    <activation>
        <os>
          <family>Windows</family>
        </os>
    </activation>
    <properties>
        <run.command>cmd</run.command>
        <run.command.additionnal.arg>/c</run.command.additionnal.arg>
        <zookeeper.start.script>start-zookeeper.bat</zookeeper.start.script>
        <zookeeper.stop.script>stop-zookeeper.bat</zookeeper.stop.script>
        <kafka.start.script>start-kafka.bat</kafka.start.script>
        <kafka.stop.script>stop-kafka.bat</kafka.stop.script>
    </properties>
</profile>
<profile>
    <id>linux-properties</id>
    <activation>
        <os>
          <family>unix</family>
        </os>
    </activation>
    <properties>
        <run.command>sh</run.command>
        <run.command.additionnal.arg></run.command.additionnal.arg>
        <zookeeper.start.script>start-zookeeper.sh</zookeeper.start.script>
        <zookeeper.stop.script>stop-zookeeper.sh</zookeeper.stop.script>
        <kafka.start.script>start-kafka.sh</kafka.start.script>
        <kafka.stop.script>stop-kafka.sh</kafka.stop.script>
    </properties>
</profile>
Et voila, the start and stop scripts properties will be set correctly depending on the runtime OS.

Okay but I don't want to run whole intergation tests all the time ?

This time again, you can use a Maven profile but without <activation>. Plugins will be activated only if you specify the profile inside the maven command :
mvn clean install -P integration-tests
<profiles>
    <profile>
        <id>integration-tests</id>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-failsafe-plugin</artifactId>
                    <version>2.12.2</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>integration-test</goal>
                                <goal>verify</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.6</version>
                    <executions>
                        <execution>
                            <id>start-third-parties</id>
                            <phase>pre-integration-test</phase>
                            <configuration>
                                <target>
                                    <!-- ANT stuff -->
                                </target>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>stop-third-parties</id>
                            <phase>post-integration-test</phase>
                            <configuration>
                                <target>
                                    <!-- ANT stuff -->
                                </target>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                       </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

About the stop scripts

Stop scripts are sometimes not easy to write... but there is always a way to kill a process ! The examples bellow are made for Kafka (replace kafka.Kafka by anything that can identify the process you started) :

on Linux

ps ax | grep -i 'kafka.Kafka' | grep -v grep | awk '{print $1}' | xargs kill -SIGTERM
You may have to pay to run it on Mac OS X indeed...

on Windows

wmic process where (commandline like "%%kafka.Kafka%%" and not name="wmic.exe") delete
You only need to double '%' if you want the command to be executed within a .bat file.

Conclusion

This solution might work for a lot of third parties appliations that you might want to run in background in order to pass integration tests on your whole application layers :
  • run a queue like Kafka or ZeroMQ that doesn't provide Maven integration yet
  • run a database like MySQL, Cassandra (Maven plugin available btw...) or HBase
  • run an independent instance of anything : Memcached, ElasticSearch or Solar for example
Tell me if you use it for something else ;-)

dimanche 23 septembre 2012

Mahout Taste walkthrough (initiation par l'exemple)

Ce post à pour objectif de défricher le fonctionnement du moteur de recommandation d'Apache Mahout Taste en étudiant les projets d’exemples fournis avec la version 0.7 d'Apache Mahout (L'API est susceptible d'évoluer en profondeur tant que la MileStone 1 n'est pas atteinte).
<dependency>
    <groupId>org.apache.mahout</groupId>
    <artifactId>mahout-core</artifactId>
    <version>0.7</version>
</dependency>

<dependency>
    <groupId>org.apache.mahout</groupId>
    <artifactId>mahout-examples</artifactId>
    <version>0.7</version>
</dependency>

Concepts

Mahout Taste permet de fournir des recommandation d'Items à des User. Users et Items étant tous deux représentés par des entiers. Afin de fournir des recommandations, le framework s'appuie sur un DataModel constitué de préférences. Une préférence est un nombre flottant représentant une affinité entre un User et un Item.


Le moteur de recommandation repose sur l'interface Recommender. Les implémentations de cette interface permettent de :
  • Récupérer une liste d'Items recommandées pour un User
  • D'estimer l'affinité entre un User et un Item
  • De modifier des préférences à la volée

Un exemple simple : ItemAverageRecommender

ItemAverageRecommender est une implémentation de l'interface Recommender qui estime l'affinité potentielle entre un User et un Item comme la moyenne des affinités des User ayant exprimés une préférence pour pour cet Item. Elle permet ainsi d'obtenir des recommandations indiférenciées par User.

Le BookCrossingRecommender

Un peu plus intéressant, le BookCrossingRecommender est un exemple concret de Recommender qui s'appuie sur un jeu de données réel :
public final class BookCrossingRecommender implements Recommender {

    private final Recommender recommender;

    public BookCrossingRecommender(DataModel bcModel) throws TasteException {
        UserSimilarity similarity = new CachingUserSimilarity(
                new EuclideanDistanceSimilarity(bcModel), bcModel);
        UserNeighborhood neighborhood = new NearestNUserNeighborhood(10, 0.2, similarity, bcModel, 0.2);
        recommender = new GenericUserBasedRecommender(bcModel, neighborhood, similarity);
    }
}
En regardant de plus près le code de ce BookCrossingRecommender on remarque la présence des concepts suivants :
UserSimilarity
qui représente la ressemblance entre deux User.
UserNeighborhood
qui représente le voisinage d'un User; les voisins étant déterminés en fonction de leurs ressemblance.
Des implémentations diverses de ces deux concepts sont disponibles par défaut dans Mahout Taste. Le BookCrossingRecommender utilise la classe EuclideanDistanceSimilarity comme implémentation de la ressemblance. Celle-ci permet de classer les Users selon la distance euclidienne entre leurs affinités communes (chaque Item pour lequel ils ont chacun exprimé une préférence étant une dimension).

Une fois la notion de ressemblance posée, il est alors possible de définir le voisinage d'un User. Dans le cas du BookCrossingRecommender, le voisinage d'un User est constitué des 10 Users qui lui sont le plus ressemblant. La classe NearestNUserNeighborhood permet de fixer un seuil minimal en dessous duquel la ressemblance entre deux Users leur interdit d'être voisins.

Enfin, le GenericUserBasedRecommender est utilisé afin de proposer des recommandations à un User en considérant les Items ayant l'affinité moyenne la plus grande parmi son voisinage...

Comment choisir (construire) une bonne implémentation

Si Taste propose une API simple permettant de monter rapidement un système de recommandation sur un jeux de données quasi quelconque, comment savoir si la notion de ressemblance retenue est la bonne, si le voisinage n'a pas été choisi trop vaste ou trop restreint et si la méthode de sélection des recommandations est pertinente ?

Quelques requêtes sur Google permettent de se rendre compte que de nombreuses recherches plus ou moins compréhensibles (plutôt moins que plus) ont été menées sur le sujet.

Pour le commun des mortels, Mahout Taste est livré avec un RecommenderEvaluator sui, comme son nom l'indique, permet d'estimer la qualité d'un Recommender. A partir d'un DataModel, le RecommenderEvaluator va construire le Recommender à évaluer en utilisant une partie du jeu de données : le "training percentage". Il va ensuite évaluer le Recommender sur un sous ensemble du jeu de données appelé "evaluation percentage" et retourner une note. Plus la note retournée est basse, plus le Recommender a proposé des recommandations proches des préférences réelles de l'"evaluation percentage". Une note de 0 indiquant des résultats identiques entre les recommandations du Recommender et l'"evaluation percentage".
Fork me on GitHub