diff --git a/.classpath b/.classpath
index 85eee6f8f01684e82fb18b8747592cd3e328747d..4720ad68e208eca3476a8a9e198bfb7f36be92f7 100644
--- a/.classpath
+++ b/.classpath
@@ -11,7 +11,6 @@
 	<classpathentry kind="lib" path="lib/velocity-1.6.3.jar"/>
 	<classpathentry kind="lib" path="lib/sqlite-jdbc-3.20.0.jar"/>
 	<classpathentry kind="lib" path="lib/junit-4.8.1.jar"/>
-	<classpathentry kind="lib" path="lib/drmaa.jar"/>
 	<classpathentry kind="lib" path="lib/stax2-api-4.0.0.jar"/>
 	<classpathentry kind="lib" path="lib/woodstox-core-5.0.3.jar"/>
 	<classpathentry kind="lib" path="lib/commons-logging-1.1.1.jar"/>
diff --git a/CHANGES b/CHANGES
index d2a4a2bc7d64a3944c570c80d88eef84e404ba86..bfc99b2dbd9f7ed1fb36e17969bdac62b518628e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,12 @@
+v2.2
+----
+
+- Praxis was really slow on machines where 'localhost' takes seconds
+  to resolve, due to some DNS (mis)configuration.  This has been
+  observed on Mac OS X "Sierra".
+  Thanks to Patrick Gasser (Singapore-ETH Centre) for reporting the
+  issue, and for his help to diagnose than fix the problem.
+
 v2.1
 ----
 
diff --git a/ant/dependencies.xml b/ant/dependencies.xml
new file mode 100644
index 0000000000000000000000000000000000000000..939c070b9dbcea99104a5081134fd34323a7e66f
--- /dev/null
+++ b/ant/dependencies.xml
@@ -0,0 +1,148 @@
+<project name="praxis-variables">
+  <!-- If not called from an other project setting praxis.home, define it -->
+  <condition property="praxis.home" value="${basedir}">
+    <not>
+      <isset property="praxis.home" />
+    </not>
+  </condition>
+
+  <import file="macros.xml" />
+  <import file="praxis.core.xml"/>
+  <import file="praxis.swing_gui.xml"/>
+
+  <!-- Dependencies for the client -->
+
+  <selector id="praxis.client.dependencies.selector">
+    <or>
+      <filename name="args4j-2.33.jar"/>
+      <filename name="commons-logging-1.1.1.jar"/>
+      <filename name="javassist.jar"/>
+      <filename name="jaxen.jar"/>
+      <filename name="jdom.jar"/>
+      <filename name="org.apache.common.lang.SystemUtils.jar"/>
+      <filename name="rmiio-2.0.2.jar"/>
+      <filename name="saxpath.jar"/>
+      <filename name="sqlite-jdbc-3.20.0.jar"/>
+      <filename name="stax2-api-4.0.0.jar"/>
+      <filename name="woodstox-core-5.0.3.jar"/>
+      <filename name="velocity-1.6.3-dep.jar"/>
+      <filename name="velocity-1.6.3.jar"/>
+    </or>
+  </selector>
+
+  <fileset id="praxis.client.dependencies" dir="${praxis.core.lib}">
+    <selector refid="praxis.client.dependencies.selector" />
+  </fileset>
+
+  <path id="praxis.client.classpath">
+    <fileset refid="praxis.client.dependencies"/>
+  </path>
+  <pathconvert pathsep=" "
+               property="praxis.client.manifest.classpath"
+               refid="praxis.client.classpath">
+    <map from="${praxis.core.lib}" to="lib"/>
+  </pathconvert>
+
+
+  <!-- Dependencies for the server -->
+
+  <selector id="praxis.server.dependencies.selector">
+    <or>
+      <filename name="args4j-2.33.jar"/>
+      <filename name="javassist.jar"/>
+      <filename name="jaxen.jar"/>
+      <filename name="jdom.jar"/>
+      <filename name="org.apache.common.lang.SystemUtils.jar"/>
+      <filename name="saxpath.jar"/>
+      <filename name="sqlite-jdbc-3.20.0.jar"/>
+      <filename name="stax2-api-4.0.0.jar"/>
+      <filename name="woodstox-core-5.0.3.jar"/>
+    </or>
+  </selector>
+  <fileset dir="${praxis.core.lib}" id="praxis.server.dependencies">
+    <selector refid="praxis.server.dependencies.selector" />
+  </fileset>
+  <pathconvert dirsep="/" pathsep=" " property="praxis.server.manifest.classpath" refid="praxis.server.dependencies">
+    <map from="${praxis.core.lib}" to="lib"/>
+  </pathconvert>
+  <echo message="Server Classpath is ${praxis.server.manifest.classpath}" level="verbose"/>
+
+
+  <!-- Dependencies for the platform -->
+  <selector id="praxis.platform.dependencies.selector">
+    <or>
+      <filename name="args4j-2.33.jar" />
+      <filename name="jaxen.jar" />
+      <filename name="jdom.jar" />
+      <filename name="saxpath.jar" />
+      <filename name="org.apache.common.lang.SystemUtils.jar" />
+    </or>
+  </selector>
+  <fileset dir="${praxis.core.lib}" id="praxis.platform.dependencies">
+    <selector refid="praxis.platform.dependencies.selector" />
+  </fileset>
+  <pathconvert dirsep="/" pathsep=" " property="praxis.platform.manifest.classpath">
+    <map from="${praxis.core.lib}" to="lib"/>
+    <path>
+      <fileset refid="praxis.platform.dependencies"/>
+    </path>
+  </pathconvert>
+  <echo message="Platform Classpath is ${praxis.platform.manifest.classpath}" level="verbose"/>
+
+
+  <!-- Dependencies for server + platform -->
+  <selector id="praxis.server_platform.dependencies.selector">
+    <or>
+      <selector refid="praxis.server.dependencies.selector" />
+      <selector refid="praxis.platform.dependencies.selector" />
+    </or>
+  </selector>
+  <fileset dir="${praxis.core.lib}" id="praxis.server_platform.dependencies">
+    <selector refid="praxis.server_platform.dependencies.selector" />
+  </fileset>
+  <pathconvert dirsep="/" pathsep=" " property="praxis.server_platform.manifest.classpath">
+    <map from="${praxis.core.lib}" to="lib"/>
+    <path>
+      <fileset refid="praxis.server_platform.dependencies"/>
+    </path>
+  </pathconvert>
+  <echo message="Server+Platform Classpath is ${praxis.server_platform.manifest.classpath}" level="verbose"/>
+
+
+  <!-- Dependencies for all: client + server + platform -->
+  <selector id="praxis.all.dependencies.selector">
+    <or>
+      <selector refid="praxis.client.dependencies.selector" />
+      <selector refid="praxis.server.dependencies.selector" />
+      <selector refid="praxis.platform.dependencies.selector" />
+    </or>
+  </selector>
+  <fileset dir="${praxis.core.lib}" id="praxis.all.dependencies">
+    <selector refid="praxis.all.dependencies.selector" />
+  </fileset>
+  <pathconvert dirsep="/" pathsep=" " property="praxis.all.manifest.classpath">
+    <map from="${praxis.core.lib}" to="lib"/>
+    <path>
+      <fileset refid="praxis.all.dependencies"/>
+    </path>
+  </pathconvert>
+
+
+  <!-- All jars -->
+
+  <path id="praxis.core.all.libs">
+    <fileset dir="${praxis.core.lib}">
+      <include name="**/*.jar"/>
+    </fileset>
+  </path>
+
+
+  <target name="print-dependencies" description="Print Dependencies">
+    <echo message="praxis_home: ${basedir}"/>
+    <echo-fileset filesetref="praxis.client.dependencies" />
+    <echo-path pathref="praxis.client.classpath" />
+    <echo message="client.manifest.classpath: ${client.manifest.classpath}"/>
+
+  </target>
+
+</project>
diff --git a/ant/javadoc.xml b/ant/javadoc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a60136d498b04ad36afe3213fb0901dbec4b201c
--- /dev/null
+++ b/ant/javadoc.xml
@@ -0,0 +1,31 @@
+<project name="javadoc" default="javadoc" basedir="../..">
+  <property name="praxis.home" value="${basedir}"/>
+
+  <import file="dependencies.xml"/>
+
+  <target name="javadoc"
+		  description="Generates the javadoc">
+
+    <javadoc packagenames="eu.telecom_bretagne.praxis.*"
+             sourcepath="${praxis.core.src}:${praxis.swing_gui.src}"
+             source="8"
+             defaultexcludes="yes"
+             destdir="${praxis.core.home}/doc/api"
+             author="true"
+             encoding="utf8" docencoding="utf8" charset="utf8"
+             version="true"
+             use="true"
+             splitindex="true"
+             notree="false"
+             nodeprecated="false" nodeprecatedlist="false"
+             noindex="false" nonavbar="false"
+             windowtitle="Praxis API">
+      <doctitle><![CDATA[<h1>Praxis documentation</h1>]]></doctitle>
+      <bottom><![CDATA[<i>Copyright &#169; 2002-2017, Institut Mines Telecom - IMT Atlantique Bretagne Pays de la Loire. All Rights Reserved.</i>]]></bottom>
+      <tag name="implementation" scope="all" description="Implementation note: "/>
+      <tag name="category" scope="all" description="Category:"/>
+      <link href="https://docs.oracle.com/javase/8/docs/api/" />
+      <classpath refid="praxis.core.all.libs"/>
+    </javadoc>
+  </target>
+</project>
diff --git a/ant/macros.xml b/ant/macros.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f07fa1b491240753d59e117158123027462f1c62
--- /dev/null
+++ b/ant/macros.xml
@@ -0,0 +1,30 @@
+<project name="praxis-macros">
+  <!-- cf.
+http://blog.andrewbeacock.com/2005/08/pretty-printing-java-classpaths-using.html
+http://www.javalobby.org/java/forums/t71033.html
+  -->
+  <macrodef name="echo-path">
+	<attribute name="pathref" />
+    <sequential>
+      <pathconvert pathsep="${line.separator}  |- "
+                   property="@{pathref}.echopath"
+                   refid="@{pathref}"/>
+	  <echo>@{pathref} (path)</echo>
+	  <echo>  |- ${@{pathref}.echopath}</echo>
+	</sequential>
+  </macrodef>
+
+  <macrodef name="echo-fileset">
+	<attribute name="filesetref" />
+	<sequential>
+	  <pathconvert pathsep="${line.separator}  |- "
+                   property="@{filesetref}.echopath">
+		<path>
+		  <fileset refid="@{filesetref}" />
+		</path>
+	  </pathconvert>
+      <echo>@{filesetref} (fileset)</echo>
+	  <echo>  |- ${@{filesetref}.echopath}</echo>
+	</sequential>
+  </macrodef>
+</project>
diff --git a/ant/praxis.core.xml b/ant/praxis.core.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3c81b00a58c9c423c0946401e73caf4721e58ffd
--- /dev/null
+++ b/ant/praxis.core.xml
@@ -0,0 +1,90 @@
+<project name="praxis-core-common">
+
+  <import file="macros.xml" />
+
+  <property name="praxis.core.home" value="${praxis.home}/praxis-core"/>
+
+  <!-- -->
+  <property name="praxis.core.src"   location="${praxis.core.home}/src"/>
+  <property name="praxis.core.bin"   location="${praxis.core.home}/bin"/>
+  <property name="praxis.core.build" location="${praxis.core.home}/build"/>
+
+  <!-- get git revision. Written by "jmuc" on stackoverflow
+       http://stackoverflow.com/questions/2974106/how-to-lookup-the-latest-git-commit-hash-from-an-ant-build-script/4059546#4059546
+  -->
+  <available file="${praxis.core.home}/.git" type="dir" property="praxis.core.git.present"/>
+  <target name="praxis.core.git.revision" description="Store git revision in ${repository.version}" if="praxis.core.git.present">
+    <exec executable="git" outputproperty="praxis.core.git.revision"
+          failifexecutionfails="false" errorproperty=""
+          dir="${praxis.core.home}">
+      <arg value="describe"/>
+      <arg value="--tags"/>
+      <arg value="--always"/>
+      <arg value="HEAD"/>
+    </exec>
+    <condition property="praxis.core.repository.version" value="${praxis.core.git.revision}" else="${build.time}">
+      <and>
+        <isset property="praxis.core.git.revision"/>
+        <length string="${praxis.core.git.revision}" trim="yes" length="0" when="greater"/>
+      </and>
+    </condition>
+    <echo message="praxis.core.git.revision: ${praxis.core.git.revision}"/>
+    <echo message="praxis.core.repository.version: ${praxis.core.repository.version}"/>
+
+  </target>
+
+  <!--  Libraries  -->
+  <property name="praxis.core.lib" location="${praxis.core.home}/lib"/>
+
+  <import file="dependencies.xml" />
+
+  <target name="praxis.core.init" unless="praxis.core.init_ok">
+    <property name="praxis.core.init_ok" value="ok"/>
+    <tstamp/>
+    <mkdir dir="${praxis.core.bin}"/>
+    <mkdir dir="${praxis.core.build}"/>
+  </target>
+
+  <target name="praxis.core.compile" depends="praxis.core.init">
+<!--      description="Compile praxis.core sources">-->
+    <javac destdir="${praxis.core.bin}"
+           debug="off"
+           source="1.8"
+           target="1.8"
+           encoding="UTF-8"
+           includeantruntime="false"
+    >
+<!--    	   excludes="**/*Test*"-->
+      <classpath refid="praxis.core.all.libs"/>
+      <src path="${praxis.core.src}"/>
+    </javac>
+  </target>
+
+  <target name="praxis.core.clean">
+    <delete dir="${praxis.core.bin}"/>
+    <delete dir="${praxis.core.build}"/>
+  </target>
+
+  <target name="praxis.core.show-variables"
+          description="Print variables defined in praxis.core.common">
+    <echo message="praxis.core.home: ${praxis.core.home}"/>
+    <echo message="praxis.core.lib:  ${praxis.core.lib}"/>
+    <echo message="praxis.core.bin:  ${praxis.core.bin}"/>
+    <echo message="---- Client ----" />
+    <echo-fileset filesetref="praxis.client.dependencies" />
+    <echo-path pathref="praxis.client.classpath" />
+    <echo message="client.manifest.classpath: ${praxis.client.manifest.classpath}"/>
+    <echo message="---- Server ----" />
+    <echo-fileset filesetref="praxis.server.dependencies" />
+    <!--<echo-path pathref="praxis.server.classpath" />-->
+    <echo message="praxis.server.manifest.classpath: ${praxis.server.manifest.classpath}"/>
+    <echo message="---- Platform ----" />
+    <echo-fileset filesetref="praxis.platform.dependencies" />
+    <!--<echo-path pathref="praxis.server.classpath" />-->
+    <echo message="praxis.platform.manifest.classpath: ${praxis.platform.manifest.classpath}"/>
+    <echo message="---- All ----" />
+    <echo message="${praxis.all.dependencies}" />
+    <echo-fileset filesetref="praxis.all.dependencies" />
+  </target>
+
+</project>
diff --git a/ant/praxis.project.xml b/ant/praxis.project.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ba79d4ffb2e352440c843336770928d8f23cfc5a
--- /dev/null
+++ b/ant/praxis.project.xml
@@ -0,0 +1,123 @@
+<project name="">
+  <description>Builds Praxis projects</description>
+
+  <!-- Add one-jar ant task -->
+  
+  <!--
+  <property name="one-jar.dist.dir" value="/home0/extern/capexbio/praxis/one-jar/dist"/>
+-->
+<!-- ori
+  <property name="one-jar.dist.dir" value="/home/big/lang/java/one-jar.cvs/dist"/>
+  <property name="one-jar.version" value="0.98"/> -->
+
+<!-- ori
+  <taskdef name="one-jar" classname="com.simontuffs.onejar.ant.OneJarTask" 
+		classpath="${one-jar.dist.dir}/one-jar-ant-task-${one-jar.version}.jar" onerror="report"/>
+-->
+<!-- old
+  <taskdef name="one-jar" classname="com.simontuffs.onejar.ant.OneJarTask" 
+		classpath="${praxis.home}/praxis-core/lib/one-jar/one-jar-ant-task-${one-jar.version}.jar" onerror="report"/> 
+-->
+  <macrodef name="praxis_generate_java">
+    <attribute name="descriptions" />
+    <attribute name="configuration" />
+    <attribute name="target.src" />
+    <sequential>
+      <java
+         classname="eu.telecom_bretagne.praxis.preprocess.CoreDescriptionsGeneration"
+         fork="yes"
+         >
+         <arg value="@{descriptions}" />
+         <arg value="@{target.src}" />
+         <jvmarg value="-DCONFIGURATION=@{configuration}" />
+         <jvmarg value="-Djava.util.logging.config.file=logging.properties" />
+         <classpath>
+           <fileset refid="praxis.all.dependencies" />
+           <pathelement path="${praxis.jar}" />
+           <pathelement location="${praxis.core.lib}/velocity-1.6.3-dep.jar" />
+           <pathelement location="${praxis.core.lib}/velocity-1.6.3.jar" />
+           <pathelement location="${praxis.core.home}/" />
+           <pathelement location="." />
+         </classpath>
+      </java>
+    </sequential>
+
+  </macrodef>
+
+  <macrodef name="praxis_project_one_jar">
+	<attribute name="project.name" />
+    <attribute name="configuration" />
+    <attribute name="destfile" />
+    <element name="libs" />
+    <element name="includes" />
+    <sequential>
+    <manifest file="project.mf">
+      <attribute name="Main-Class" value="com.simontuffs.onejar.Boot"/>
+      <attribute name="One-Jar-Main-Class" value="eu.telecom_bretagne.praxis.common.Splasher"/>
+      <attribute name="Specification-Title" value="${project.name} / Standalone"/>
+    </manifest>
+
+    <copy file="@{configuration}"
+          tofile="configuration/configuration.cfg"
+          preservelastmodified="true"
+          overwrite="true"
+          />
+    <property name="one-jar.class.path" value="." />
+    <one-jar destfile="@{destfile}"
+             manifest="project.mf"
+             index="true">
+      <main jar="${praxis.jar}"/>
+      <lib>
+        <fileset refid="praxis.all.dependencies"/>
+<!--    <fileset refid="praxis.swing_gui.dependencies" /> -->
+        <libs />
+      </lib>
+      <includes />
+      <fileset dir="${praxis.home}/praxis-core" includes="configurations/**" />
+      <fileset dir="${praxis.home}/praxis-core" includes="data/**" excludes="**/.xvpics **/.xvpics/*" />
+    </one-jar>
+    <delete file="project.mf"/>
+
+    </sequential>
+  </macrodef>
+
+  <macrodef name="praxis_project_stress_one_jar">
+	<attribute name="project.name" />
+    <attribute name="configuration" />
+    <attribute name="destfile" />
+    <element name="libs" />
+    <element name="includes" />
+    <sequential>
+    <manifest file="project.mf">
+      <attribute name="Main-Class" value="com.simontuffs.onejar.Boot"/>
+      <attribute name="One-Jar-Main-Class" value="eu.telecom_bretagne.praxis.client.StressTst"/>
+      <attribute name="Specification-Title" value="${project.name} / Standalone"/>
+    </manifest>
+
+    <copy file="@{configuration}"
+          tofile="configuration/configuration.cfg"
+          preservelastmodified="true"
+          overwrite="true"
+          />
+    <property name="one-jar.class.path" value="." />
+    <one-jar destfile="@{destfile}"
+             manifest="project.mf"
+             index="true">
+      <main jar="${praxis.jar}"/>
+      <lib>
+        <fileset refid="praxis.all.dependencies"/>
+<!--    <fileset refid="praxis.swing_gui.dependencies" /> -->
+        <libs />
+      </lib>
+      <includes />
+<!--
+      <fileset dir="${praxis}" includes="configurations/**" />
+      <fileset dir="${praxis}" includes="data/**" excludes="**/.xvpics **/.xvpics/*" />
+-->
+    </one-jar>
+    <delete file="project.mf"/>
+
+    </sequential>
+  </macrodef>
+
+</project>
diff --git a/ant/praxis.swing_gui.xml b/ant/praxis.swing_gui.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4d275d7701fd850d7b3458357bff6000698d8ba6
--- /dev/null
+++ b/ant/praxis.swing_gui.xml
@@ -0,0 +1,107 @@
+<project name="praxis-swing-gui" basedir="..">
+
+  <!-- we expect praxis.home to be set -->
+  <fail unless="praxis.home"
+        message="Property praxis.home is not defined"/>
+
+  <!-- if not set, defaults to ../praxis-swing-gui/ -->
+  <condition property="praxis.swing_gui.home"
+             value="${praxis.home}/praxis-swing-gui">
+    <not>
+      <isset property="praxis.swing_gui.home" />
+    </not>
+  </condition>
+
+  <target name="praxis.swing_gui.check">
+    <fail message="Property praxis.swing_gui.home does not point to a valid directory (value: ${praxis.swing_gui.home})">
+      <condition>
+        <not>
+          <available file="${praxis.swing_gui.home}" type="dir" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <!-- -->
+  <property name="praxis.swing_gui.src"   location="${praxis.swing_gui.home}/src"/>
+  <property name="praxis.swing_gui.bin"   location="${praxis.swing_gui.home}/bin"/>
+
+  <!--  Libraries  -->
+  <property name="praxis.swing_gui.lib" location="${praxis.swing_gui.home}/lib"/>
+
+
+  <!-- All jars -->
+  <path id="praxis.swing_gui.libs">
+    <fileset dir="${praxis.swing_gui.home}/lib">
+      <include name="**/*.jar"/>
+      <exclude name="*junit.jar"/>
+    </fileset>
+  </path>
+
+
+
+  <!-- Dependencies -->
+  <fileset dir="${praxis.core.lib}" id="praxis.swing_gui.dependencies">
+      <include name="jaxen.jar"/>
+      <include name="jdom.jar"/>
+      <include name="saxpath.jar"/>
+  </fileset>
+  <path id="praxis.swing_gui.classpath">
+    <fileset refid="praxis.swing_gui.dependencies"/>
+  </path>
+
+  <pathconvert pathsep=" " property="praxis.swing_gui.manifest.classpath" refid="praxis.swing_gui.classpath">
+    <map from="${praxis.core.lib}" to="lib"/>
+    <map from="${praxis.swing_gui.lib}" to="lib"/>
+  </pathconvert>
+
+  <!-- targets -->
+
+  <target name="praxis.swing_gui.init"
+          unless="praxis.swing_gui.init_ok"
+          depends="praxis.swing_gui.check">
+    <property name="praxis.swing_gui.init_ok" value="ok"/>
+    <tstamp/>
+    <mkdir dir="${praxis.swing_gui.bin}"/>
+  </target>
+
+  <target name="praxis.swing_gui.compile"
+          depends="praxis.core.compile, praxis.swing_gui.init">
+    <!-- description="Compile Praxis Swing GUI" -->
+    <javac destdir="${praxis.swing_gui.bin}"
+           debug="off"
+           source="1.8"
+           target="1.8"
+           encoding="utf-8"
+           includeantruntime="false"
+     >
+<!--    	   excludes="**/*Test*"-->
+
+      <classpath location="${praxis.core.bin}" />
+      <classpath refid="praxis.swing_gui.libs" />
+      <classpath refid="praxis.swing_gui.classpath" />
+      <src path="${praxis.swing_gui.src}" />
+    </javac>
+  </target>
+
+  <target name="praxis.swing_gui.clean"
+          depends="praxis.swing_gui.check">
+<!--
+    description="Praxis Swing GUI: Removes class files, jars, zips, tmp files">
+  -->
+   <delete dir="${praxis.swing_gui.bin}"/>
+  </target>
+
+
+  <target name="praxis.swing_gui.show-variables"
+          description="Print variables defined in praxis.swing_gui">
+    <echo message="praxis.swing_gui.home: ${praxis.swing_gui.home}"/>
+    <echo message="praxis.swing_gui.lib:  ${praxis.swing_gui.lib}"/>
+    <echo message="praxis.swing_gui.bin:  ${praxis.swing_gui.bin}"/>
+    <echo-path pathref="praxis.swing_gui.dependencies"/>
+    <echo message="praxis.swing_gui.manifest.classpath: ${praxis.swing_gui.manifest.classpath}"/>
+    <echo-path pathref="praxis.swing_gui.libs" />
+    <echo-path pathref="praxis.swing_gui.classpath" />
+  </target>
+
+</project>
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86b9a62c3e83676b85c652d1621a3a3eee0c3ebc
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,129 @@
+<project name="praxis" default="praxis.build.standalone.jar" basedir="..">
+  <description>
+    Praxis build.xml
+  </description>
+
+  <tstamp>
+    <format property="build.time" pattern="yyyy-MM-dd'T'hh:mm:ss.SSSZ"/>
+  </tstamp>
+
+  <property name="praxis.version" value="1.11"/>
+  <!-- If not called from an other project setting praxis.home, define it.
+       In details:
+       - it is unset when directly building the jars of Praxis,
+       - it is set when imported from a build.xml in a Praxis project.
+    -->
+  <condition property="praxis.home" value="${basedir}">
+    <not>
+      <isset property="praxis.home" />
+    </not>
+  </condition>
+  <tstamp/>
+
+  <!-- -->
+  <import file="ant/dependencies.xml"/>
+  <import file="ant/praxis.core.xml"/>
+  <import file="ant/praxis.swing_gui.xml"/>
+  <import file="ant/javadoc.xml"/>
+  <import file="ant/praxis.project.xml" />
+
+  <target name="praxis.compile"
+          depends="praxis.core.compile, praxis.swing_gui.compile"/>
+
+  <property name="praxis.jar" value="${praxis.core.build}/praxis.jar" />
+
+  <!-- Common values for MANIFEST files -->
+  <manifest file="MANIFEST.MF">
+    <attribute name="Built-By" value="${user.name}"/>
+    <attribute name="Build-Date" value="${build.time}"/>
+    <attribute name="Specification-Title" value="Praxis"/>
+    <attribute name="Specification-Vendor" value="Institut Mines-Telecom / TELECOM Bretagne"/>
+    <attribute name="Specification-Version" value="${praxis.version}"/>
+<!--<attribute name="Implementation-Title" value="Praxis"/>-->
+<!--
+    <attribute name="Implementation-Version" value="${praxis.version} ${praxis.core.repository.version} ${build.time}"/>
+-->
+    <attribute name="Implementation-Vendor" value="Institut Mines-Telecom / TELECOM Bretagne"/>
+  </manifest>
+
+  <target name="praxis.build.server_platform.jar"
+          depends="praxis.core.compile, praxis.core.git.revision"
+          description="Builds the jar for the server+platform">
+    <jar jarfile="${praxis.core.build}/praxis_server_platform.jar" manifest="MANIFEST.MF" index="true">
+      <manifest>
+        <attribute name="Main-Class" value="eu.telecom_bretagne.praxis.common.Launcher"/>
+        <attribute name="Implementation-Title" value="Praxis Server and Platform"/>
+        <attribute name="Class-Path" value="./ ${server_platform.manifest.classpath}"/>
+      </manifest>
+      <fileset dir="${praxis.core.bin}">
+        <include name="eu/telecom_bretagne/praxis/platform/**"/>
+        <include name="eu/telecom_bretagne/praxis/common/**"/>
+        <include name="eu/telecom_bretagne/praxis/core/**"/>
+        <include name="eu/telecom_bretagne/praxis/server/**"/>
+        <exclude name="**/*Test*"/>
+      </fileset>
+      <fileset dir="." includes="data/i18n/**" excludes="**/*~"/>
+    </jar>
+  </target>
+
+  <target name="praxis.build.standalone.jar"
+          depends="praxis.compile, praxis.core.git.revision"
+          description="Builds the jar for the standalone version of praxis">
+    <jar jarfile="${praxis.jar}" manifest="MANIFEST.MF" index="false">
+      <manifest>
+        <attribute name="Main-Class" value="eu.telecom_bretagne.praxis.common.Launcher"/>
+        <attribute name="Implementation-Title" value="Praxis / Standalone"/>
+        <attribute name="Class-Path" value=". ${praxis.all.manifest.classpath}"/>
+        <attribute name="Implementation-Version" value="${praxis.version} ${praxis.core.git.revision} ${build.time}"/>
+         <!-- TODO: own dependencies -->
+      </manifest>
+      <fileset dir="${praxis.core.bin}">
+        <exclude name="**/*Test*"/>
+      </fileset>
+      <fileset dir="${praxis.swing_gui.bin}">
+        <exclude name="**/*Test*"/>
+      </fileset>
+      <fileset dir="${praxis.core.home}"
+               includes="data/i18n/praxis_*.properties" excludes="**/*~"/>
+      <fileset dir="${praxis.core.home}"
+               includes="data/dtd/**" excludes="**/*~"/>
+      <fileset dir="${praxis.core.home}"
+               includes="configurations/** templates/*.vm" excludes="**/*~"/>
+      <fileset dir="${praxis.swing_gui.home}"
+               includes="data/**" excludes="**/*~"/>
+    </jar>
+  </target>
+
+
+  <!-- what for ? -->
+  <target name="praxis.build.common.jar"
+            depends="praxis.core.compile"
+            unless="jar_generated"
+            description="Builds praxis's common.jar">
+  <!-- praxis.common, but NOT the sub packages such as praxis.common.events -->
+    <jar jarfile="${praxis.core.build}/praxis_common.jar" manifest="MANIFEST.MF" index="false">
+      <manifest>
+        <attribute name="Specification-Title" value="Praxis / Common"/>
+      </manifest>
+      <fileset dir="${praxis.core.bin}">
+        <include name="eu/telecom_bretagne/praxis/common/*"/>
+        <exclude name="*Launcher*"/>
+        <exclude name="**/*Test*"/>
+      </fileset>
+    </jar>
+  </target>
+
+<!--
+  <target name="build_jars"
+          depends="build_standalone_jar,build_client_jar,build_server_jar,build_platform_jar,build_common_jar"
+          description="Builds the 4 jars: client, server, platform and standalone/praxis">
+  </target>
+-->
+
+  <!-- ==== Clean ==== -->
+  <target name="clean"
+          description="Removes class files, jars and zips, temporary files"
+          depends="praxis.core.clean, praxis.swing_gui.clean">
+  </target>
+
+</project>
diff --git a/data/i18n/praxis_en.properties b/data/i18n/praxis_en.properties
index d8671b0fa95f8f7ad75935fb61c693693f4b97bc..c95ac3703b3cb535ee9bebadcb5a8b877c15b328 100644
--- a/data/i18n/praxis_en.properties
+++ b/data/i18n/praxis_en.properties
@@ -53,7 +53,7 @@ UI.actions.zoom_original                              = Zoom 1:1
 UI.actions.zoom_original_tt                           = Restores the default zoom factor
 UI.actions.zoom_out                                   = Zoom Out
 UI.actions.zoom_out_tt                                = Draws a smaller workflow to show a larger part of it
-UI.annotation.for_worflow                             = Annotation for workflow ''{0}''
+UI.annotation.for_workflow                            = Annotation for workflow ''{0}''
 UI.annotation.for_program                             = Annotation for program: ''{0}''
 UI.ctrlwf.run.file_does_not_exist                     = File {0} does not exist
 UI.ctrlwf.run.file_unreadable                         = File {0} cannot be read
diff --git a/data/i18n/praxis_fr.properties b/data/i18n/praxis_fr.properties
index a2d62869e666dd194e6afd82f0b290ae606b3eb6..4989361fdadd5771615bda4ade121f1795d4e215 100644
--- a/data/i18n/praxis_fr.properties
+++ b/data/i18n/praxis_fr.properties
@@ -48,7 +48,7 @@ UI.actions.valid_links_confirm                        = \u00CAtes-vous certain(e
 UI.actions.create_file_error                          = Impossible de cr\u00e9er le fichier {0}
 UI.actions.create_existing_file_error                 = Impossible de cr\u00e9er le fichier {0}: il existe d\u00e9j\u00e0
 UI.actions.zoom_in                                    = Zoom avant
-UI.actions.zoom_in_tt                                 = Dessine le workflow en plus grand pour mieux en distinguer les d\u00e9�tails
+UI.actions.zoom_in_tt                                 = Dessine le workflow en plus grand pour mieux en distinguer les d\u00e9tails
 UI.actions.zoom_original                              = Zoom 1:1
 UI.actions.zoom_original_tt                           = R\u00e9tablit le facteur de zoom par d\u00e9faut
 UI.actions.zoom_out                                   = Zoom arri\u00E8re
@@ -181,9 +181,9 @@ UI.dialog.wf.open_failed_no_workflow                  = Impossible d''ouvrir un
 UI.dialog.wf.open_failed_no_workspace                 = Impossible d''ouvrir un workflow: aucun workspace n''est disponible !
 UI.dialog.wf.open_failed_xml_not_conform_to_DTD       = Impossible d''ouvrir le workflow: le xml n''est pas conforme \u00E0 la DTD
 UI.dialog.wf.open_failure                             = Une erreur inattendue s''est produite lors de l''ouverture du workflow
+UI.dialog.wf.rename_save_before_confirm               = Le workflow a \u00e9t\u00e9 modifi\u00e9 et doit \u00eatre sauvegard\u00e9 avant d''\u00eatre renomm\u00e9.\n\
 UI.dialog.wf.open_failure_title                       = \u00C9chec de l''ouverture d''un workflow
 UI.dialog.wf.open_select_workflows                    = S\u00e9lectionnez le(s) workflow(s) \u00e0 ouvrir:
-UI.dialog.wf.rename_save_before_confirm               = Le workflow a \u00e9t\u00e9 modifi\u00e9 et doit \u00eatre sauvegard\u00e9 avant d''\u00eatre renomm\u00e9.\n\
 Souhaitez-vous le faire maintenant?
 UI.dialog.wf.rename_enter_new_name                    = Veuillez entrer le nouveau nom de ce workflow :
 UI.dialog.wf.rename_failure_title                     = \u00C9chec du renommage d''un workflow
@@ -350,9 +350,9 @@ UI.menu_items.ws_open_tt                              = Demande l''ouverture d''
 UI.menu_items.ws_rename                               = Renommer...
 UI.menu_items.ws_rename_tt                            = Change le nom de l''espace de travail actuel
 UI.menu_items.ws_save                                 = Sauvegarder
+UI.menu_items.ws_save_tt                              = Sauvegarde l''ensemble des workflows de l''espace de travail actuel
 UI.menu_items.ws_save_as                              = Sauvegarder sous...
 UI.menu_items.ws_save_as_tt                           = Sauvegarde l''espace de travail actuel (et tous ses workflows !) dans un nouvel espace de travail
-UI.menu_items.ws_save_tt                              = Sauvegarde l''ensemble des workflows de l''espace de travail actuel
 
 UI.menu_items.License                                 = Licence...
 UI.menu_items.OtherLicenses                           = Licences des programmes tiers...
diff --git a/src/eu/telecom_bretagne/praxis/client/SimpleCommandLine.java b/src/eu/telecom_bretagne/praxis/client/SimpleCommandLine.java
index 2304e16ddf5de96ea8884ada62f4d64d886043e8..30d01df560cef2254e64ff173337bf27b49be484 100644
--- a/src/eu/telecom_bretagne/praxis/client/SimpleCommandLine.java
+++ b/src/eu/telecom_bretagne/praxis/client/SimpleCommandLine.java
@@ -46,7 +46,7 @@ import eu.telecom_bretagne.praxis.core.workflow.WorkflowInput;
  * @implementation The main() in this class is a very simple command-line executing a workflow and retrieving the
  *                 results as a zip file. All other methods are here temporarily; they should be moved in a "façade",
  *                 client-side, encapsulating the communication level (using class {@link Client} and interface
- *                 {@link ServerMessageEvent}).
+ *                 {@link ServerToClientEvent}).
  * @author Sébastien Bigaret
  */
 public class SimpleCommandLine extends StorageListenerAdapter
diff --git a/src/eu/telecom_bretagne/praxis/client/event/ApplicationListener.java b/src/eu/telecom_bretagne/praxis/client/event/ApplicationListener.java
index 0a5f135a4c3d9e0f6d07d6786898939fa523f859..61a307ca43341d629e0b930b777331edbdf14575 100644
--- a/src/eu/telecom_bretagne/praxis/client/event/ApplicationListener.java
+++ b/src/eu/telecom_bretagne/praxis/client/event/ApplicationListener.java
@@ -12,7 +12,7 @@ public interface ApplicationListener extends EventListener
 	/**
 	 * Invoked when the application exits.  At this point there is no way to interrupt the process.
 	 * Code needing to know whether this event has already been fired will check
-	 * {@link eu.telecom_bretagne.praxis.common.Application.Application#exiting()}.
+	 * {@link eu.telecom_bretagne.praxis.common.Application#exiting()}.
 	 */
 	public void applicationExiting();
 }
diff --git a/src/eu/telecom_bretagne/praxis/common/Configuration.java b/src/eu/telecom_bretagne/praxis/common/Configuration.java
index a1f073b3118a93bcf62262b2653e44eca5b2e0ea..99d7fe2e0a9bce110ac5cba007d42a083ebee32a 100644
--- a/src/eu/telecom_bretagne/praxis/common/Configuration.java
+++ b/src/eu/telecom_bretagne/praxis/common/Configuration.java
@@ -9,6 +9,7 @@ import java.security.KeyStore;
 import java.util.ArrayList;
 import java.util.Locale;
 import java.util.Properties;
+import java.util.Scanner;
 import java.util.logging.Level;
 import java.util.logging.LogManager;
 import java.util.regex.Pattern;
@@ -27,6 +28,7 @@ import eu.telecom_bretagne.praxis.core.resource.ProgramDescription;
 
 public final class Configuration
 {
+	private static final String REVISION = "revision";
 	/*
 	 * Java par.8.7 Static Initializers The static initializers and class variable initializers are executed in
 	 * textual order.
@@ -255,10 +257,16 @@ public final class Configuration
 		// last: application_revision
 		try
 		{
-			configuration.setProperty("revision",
-			                          ""+Integer.parseInt(Configuration.get("revision").substring(6).replace('$', ' ').trim()));
+			String revision = Configuration.get(REVISION).trim();
+			if (revision.startsWith("$Rev: "))
+			{
+				// Old format: "$Rev: nnn$"
+				revision = revision.substring(6).replace('$', ' ').trim();
+			}
+			set(REVISION, revision);
+			checkInteger(REVISION, errors);
 		}
-		catch (Exception e)
+		catch (NullPointerException | NumberFormatException e)
 		{
 			errors.add("Invalid revision");
 		}
@@ -406,13 +414,26 @@ public final class Configuration
 	 */
 	private static void checkInteger(String key, ArrayList<String> errors)
 	{
-		try
+		final String value = get(key);
+		if ( value == null )
 		{
-			getInt(key);
+			errors.add("Key "+key+": not an integer (it is null)");
+			return;
 		}
-		catch (NumberFormatException e)
+		try (Scanner scanner = new Scanner(value.trim()))
 		{
-			errors.add("Key "+key+": not an integer (current value: "+get(key)+")");
+			boolean error = false;
+			if ( !scanner.hasNextInt() )
+				error = true;
+			else
+				scanner.nextInt(); // Let the scanner advance to the next token
+
+			error = ( !error ) && scanner.hasNext();
+			if ( error )
+			{
+				errors.add("Key " + key + ": not an integer (current value: " + value + ")");
+				return;
+			}
 		}
 	}
 
@@ -521,13 +542,14 @@ public final class Configuration
 
 	/**
 	 * Returns the color stored as a RGB hexadecimal string (e.g. 0xFF00FF).
-	 * @param key
+	 * @param hexRGBColor the hexadecimal RGB string
 	 * @return The corresponding color, or null if the key is {@link #isDefined(String) not defined} or if it is
 	 * invalid.
+	 * @throws IllegalArgumentException if {@hexRGBColor} is invalid
 	 */
-	public static Color getColor(String key) throws IllegalArgumentException
+	public static Color getColor(String hexRGBColor) throws IllegalArgumentException
 	{
-		String color_rgb_hex=get(key);
+		String color_rgb_hex=get(hexRGBColor);
 		return decodeColor(color_rgb_hex);
 	}
 
@@ -555,7 +577,7 @@ public final class Configuration
 
 	/**
 	 * A key is declared if and only if it is present in one of the configuration files
-	 * @param key 
+	 * @param key the key
 	 * @return true if the key is declared
 	 */
 	public static boolean isDeclared(String key)
@@ -565,7 +587,7 @@ public final class Configuration
 
 	/**
 	 * A key is said to be defined if and only if it is declared and it has a non-empty value
-	 * @param key
+	 * @param key the key to check
 	 * @return true if the key is defined
 	 */
 	public static boolean isDefined(String key)
@@ -581,7 +603,7 @@ public final class Configuration
 	/**
 	 * Resets the locale used in the application.
 	 * 
-	 * @param locale
+	 * @param language
 	 *            the lowercase two-letter ISO-639 code of the local to use. If {@code null}, "en" is used instead.
 	 * @see java.util.Locale
 	 * @see I18N
diff --git a/src/eu/telecom_bretagne/praxis/common/Launcher.java b/src/eu/telecom_bretagne/praxis/common/Launcher.java
index 54937114cabf9df69e09016c51bc0274451faa61..a3dae182eb7381c85f15621e519bd4ea39e25099 100644
--- a/src/eu/telecom_bretagne/praxis/common/Launcher.java
+++ b/src/eu/telecom_bretagne/praxis/common/Launcher.java
@@ -20,7 +20,7 @@ public class Launcher
 	 * This is the "root" delegate: "one delegate to rule them all"! Praxis projects defining their own delegates
 	 * should:
 	 * <ul>
-	 * <li>Subclass this class and override {@link #initialize()};
+	 * <li>Subclass this class and override {@link #initialize(boolean, boolean, boolean)};
 	 * <li>register the delegates within the <code>initialize()</code> method;
 	 * <li>declare the class in the configuration file, using key <code>"application.init"</code>.
 	 * </ul>
@@ -101,8 +101,11 @@ public class Launcher
 	}
 
 	/**
-	 * @param args
-	 * @throws 
+	 * This is the launcher for all praxis projects. It is responsible to launch the elements declared in the
+	 * configuration, such as: a regular client, a "bridge" client, the {@link Serveur server} and/or {@link Platform
+	 * platforms}.
+	 * 
+	 * @param args the options supplied when executing the launcher.
 	 */
 	public static void main(String[] args) throws Exception
 	{
diff --git a/src/eu/telecom_bretagne/praxis/common/NetUtils.java b/src/eu/telecom_bretagne/praxis/common/NetUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0124292a76d22b496a63df91eb81f810c69412ba
--- /dev/null
+++ b/src/eu/telecom_bretagne/praxis/common/NetUtils.java
@@ -0,0 +1,170 @@
+/**
+ * 
+ */
+package eu.telecom_bretagne.praxis.common;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Sébastien Bigaret
+ */
+public class NetUtils
+{
+    static private final String                       CLASS_NAME   = NetUtils.class.getName();
+
+    /**
+     * This map caches the result computed by {@link #isLocal(String)} and {@link #isLocal(String, boolean)}.
+     */
+    static private ConcurrentHashMap<String, Boolean> isLocalCache = new ConcurrentHashMap<>();
+
+
+    /**
+     * Stores {@link InetAddress#getLocalHost()}: when a machine has a misconfigured DNS, one call can take several
+     * seconds. At the time of writing (September 2017), this happens on some Mac OS X.
+     * 
+     * @see <a href=
+     *      "https://stackoverflow.com/questions/33289695/inetaddress-getlocalhost-slow-to-run-30-seconds/41847289#41847289">
+     *      InetAddress.getLocalHost() slow to run (30+ seconds)</a>
+     * @see <a href="https://github.com/thoeni/inetTester">inetTester, by Antonio Troina</a>
+     */
+    static private InetAddress   localhostInetAddress;
+
+    /**
+     * Same as {@link #localhostInetAddress}, but for {@code InetAddress.getAllByName("localhost")}.
+     */
+    static private InetAddress[] allLocalhostInetAddress;
+
+    static
+    {
+        try
+        {
+            localhostInetAddress = InetAddress.getLocalHost();
+        }
+        catch (UnknownHostException e)
+        {
+            localhostInetAddress = null;
+        }
+        try
+        {
+            allLocalhostInetAddress = InetAddress.getAllByName("localhost");
+        }
+        catch (UnknownHostException e)
+        {
+            allLocalhostInetAddress = new InetAddress[] {};
+        }
+    }
+
+    /**
+     * The real logic behind {@link #isLocal(String)} and {@link #isLocal(String, boolean)}. Exceptions like
+     * {@link UnknownHostException} or {@link SocketException} are ignored (and the return value is false).
+     * 
+     * @param host
+     *            the host name or its IP address.
+     * @return {@code true} if the supplied host corresponds to the local machine.
+     */
+    static private boolean _isLocal(final String host)
+    {
+        if ( host == null || host.startsWith("127.") || "localhost".equals(host)
+             || "::1".equals(host) || "0:0:0:0:0:0:0:1".equals(host) )
+            return true;
+        // from this point on, isLocal cannot be null
+        InetAddress hostAddr = null;
+        try
+        {
+            hostAddr = InetAddress.getByName(host);
+        }
+        catch (UnknownHostException e)
+        {
+            return false; /* We cannot do anything if we can't get the InetAdress */
+        }
+
+        /* loopback or any-local: this is local */
+        if ( hostAddr.isLoopbackAddress() || hostAddr.isAnyLocalAddress() )
+            return true;
+
+        /* Does it match the localhost? */
+        if ( hostAddr.equals(localhostInetAddress) )
+        {
+            Log.log.finest("This is InetAddress.getLocalHost()");
+            return true;
+        }
+
+        /* If we can find an interface for this hostAddr, it is local */
+        try
+        {
+            final NetworkInterface localInterface = NetworkInterface.getByInetAddress(hostAddr);
+            if ( localInterface != null )
+            {
+                Log.log.finest("Found a local interface");
+                return true;
+            }
+        }
+        catch (SocketException e)
+        { /* it could not be determined: let's continue */ }
+
+        /* last try: compare to every InetAddress associated to the hostname "localhost" */
+        for (InetAddress local: allLocalhostInetAddress)
+        {
+            Log.log.finest(() -> "Testing localhost: " + local);
+            if ( local.equals(hostAddr) )
+            {
+                Log.log.finest(() -> "Found localhost: " + local);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether a given host (either a host name or an IP address) corresponds to the local host. Equivalent
+     * to {@code isLocal(host, false)}.<br/>
+     * 
+     * @param host
+     *            a host name, or an IP address. If {@code null}, returns {@code true} (same semantics as in
+     *            {@link java.net.Socket#Socket(String, int)}).
+     * @return {@code true} if the host was found to be the local machine. When returning {@code false}, the method only
+     *         indicates that the supplied {@code host} name could not be determined as the local host: it does
+     *         <b>NOT</b> mean that the {@code host} is a valid host name or IP.
+     */
+    static public boolean isLocal(final String host)
+    {
+        return isLocal(host, false);
+    }
+
+    /**
+     * Determines whether a given host (either a host name or an IP address) corresponds to the local host.
+     * 
+     * @param host
+     *            a host name, or an IP address. If {@code null}, returns {@code true} (same semantics as in
+     *            {@link java.net.Socket#Socket(String, int)}).
+     * @param cached
+     *            If {@code true}, and if the method has already been called for the same host, the returned value is
+     *            the one that was computed on the previous call. If {@code false}, the cache value for this
+     *            {@code host} is invalidated and re-computed.
+     * @return {@code true} if the host was found to be the local machine.<br />
+     *         When returning {@code false}, the method only indicates that the supplied {@code host} name could not be
+     *         determined as the local host: it does <b>NOT</b> mean that the {@code host} is a valid host name or IP.
+     */
+    static public boolean isLocal(final String host, final boolean cached)
+    {
+        Boolean isLocal = null;
+        Log.log.entering(CLASS_NAME, "isLocal", new Object[] { host, cached });
+        if ( host == null )
+            isLocal = true;
+        else
+            isLocal = cached ? isLocalCache.get(host) : null;
+
+        if ( isLocal == null )
+        {
+            isLocal = _isLocal(host);
+            isLocalCache.put(host, isLocal);
+        }
+        Log.log.exiting(CLASS_NAME, "isLocal", isLocal);
+        return isLocal;
+    }
+
+}
diff --git a/src/eu/telecom_bretagne/praxis/common/RMIMasterSocketFactory.java b/src/eu/telecom_bretagne/praxis/common/RMIMasterSocketFactory.java
index 2550fb54f3308b06e4c32a4905cb5b33f13d85ef..54725bc95b3427ad6ba2383b1c83153ad0842437 100644
--- a/src/eu/telecom_bretagne/praxis/common/RMIMasterSocketFactory.java
+++ b/src/eu/telecom_bretagne/praxis/common/RMIMasterSocketFactory.java
@@ -5,8 +5,6 @@
 package eu.telecom_bretagne.praxis.common;
 
 import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.rmi.server.RMISocketFactory;
 import java.util.Vector;
 
@@ -86,42 +84,23 @@ class RMIMasterSocketFactory
 	 * @exception IOException
 	 *                if an I/O error occurs during server socket creation
 	 */
-	public java.net.Socket createSocket(String host, int port) throws java.io.IOException
+	public java.net.Socket createSocket(final String host, final int port) throws java.io.IOException
 	{
-		boolean isLocal = host==null || host.startsWith("127.") || "localhost".equals(host);
-		if (!isLocal)
-		{
-			try
-			{
-				InetAddress hostAddr = InetAddress.getByName(host);
-				if (InetAddress.getLocalHost().equals(hostAddr))
-				{
-					Log.log.finest("This is localhost");
-					isLocal = true;
-				}
-				else
-				{
-					for (InetAddress local: InetAddress.getAllByName("localhost"))
-					{
-						Log.log.finest(()->"Testing localhost: "+local);
-						if (local.equals(hostAddr))
-							isLocal = true;
-					}
-				}
-			}
-			catch (UnknownHostException e) {}
-		}
+	    final String hostname = (host==null?"<null>":host);
+		Log.log.finer(()->"Looking for "+hostname+":"+port);
+
+		final boolean isLocal = NetUtils.isLocal(host, true);
 		if (isLocal)
 		{
-			Log.log.finest(()->"direct connection to localhost: "+host+":"+port);
+			Log.log.finest(()->"direct connection to localhost: "+hostname+":"+port);
 			return new java.net.Socket(host, port);
 		}
 		if (factory != null)
 		{
-			Log.log.finest(()->"Using stored factory "+factory+" for "+host+":"+port);
+			Log.log.finest(()->"Using stored factory "+factory+" for "+hostname+":"+port);
 			return factory.createSocket(host, port);
 		}
-		Log.log.finest(()->"Calling super.createSocket <- "+host+":"+port);
+		Log.log.finest(()->"Calling super.createSocket <- "+hostname+":"+port);
 		java.net.Socket s = super.createSocket(host, port);
 		Log.log.finest(()->"super.createSocket returned -> "+( (s!=null)?s.getClass():"<null>") );
 		return s;
diff --git a/src/eu/telecom_bretagne/praxis/common/ReleaseInfo.java b/src/eu/telecom_bretagne/praxis/common/ReleaseInfo.java
index 5cdb26e109006691fa9e56add1b6601fffbd98b7..fe2277fb77467d7e246e9f0d16d1f00b73a9abea 100644
--- a/src/eu/telecom_bretagne/praxis/common/ReleaseInfo.java
+++ b/src/eu/telecom_bretagne/praxis/common/ReleaseInfo.java
@@ -7,13 +7,13 @@ package eu.telecom_bretagne.praxis.common;
  */
 public abstract class ReleaseInfo
 {
-	public static final String release              = "2.1";
+	public static final String release              = "2.2";
 
 	public static final int    revision             = 0;
 
 	public static final int    application_revision = Configuration.getInt("revision");
 
-	public static final String release_date         = "2017-09-07";
+	public static final String release_date         = "2017-10-18";
 
 	/** The date of the packaging. It has always been the same as {@link #release_date}: use the latter field instead */
 	@Deprecated
diff --git a/src/eu/telecom_bretagne/praxis/common/XMLConstants.java b/src/eu/telecom_bretagne/praxis/common/XMLConstants.java
index f8ab126609dfa890d939541d11e8351a53a2f4af..87afee059556d12e778941f5d436acce9529c58c 100644
--- a/src/eu/telecom_bretagne/praxis/common/XMLConstants.java
+++ b/src/eu/telecom_bretagne/praxis/common/XMLConstants.java
@@ -180,7 +180,7 @@ public interface XMLConstants
     
     /**
      * Path of the local file where the DTD specified by
-     * {@link #SC_DTD_PUBID} can be found.
+     * {@link #EXT_VIEWERS_DTD_PUBID} can be found.
      */
     public static final String
     EXT_VIEWERS_DTD_FILE = "data/dtd/external_viewers.dtd";
diff --git a/src/eu/telecom_bretagne/praxis/core/execution/Activity.java b/src/eu/telecom_bretagne/praxis/core/execution/Activity.java
index 43bdf8da455a5937b860f0ea87129272e02a4588..20da507ced6e26044f1b167f7f86bd9959573a60 100644
--- a/src/eu/telecom_bretagne/praxis/core/execution/Activity.java
+++ b/src/eu/telecom_bretagne/praxis/core/execution/Activity.java
@@ -43,7 +43,6 @@ public class Activity
 	/**
 	 * 
 	 * @param process
-	 * @param program
 	 */
 	public Activity(Process process)
 	{
diff --git a/src/eu/telecom_bretagne/praxis/core/execution/ResultStore.java b/src/eu/telecom_bretagne/praxis/core/execution/ResultStore.java
index cba5f5d421e291fb518eacd598dec809ccc3990f..36d08889885790d85bd4961915a87893d1bca450 100644
--- a/src/eu/telecom_bretagne/praxis/core/execution/ResultStore.java
+++ b/src/eu/telecom_bretagne/praxis/core/execution/ResultStore.java
@@ -5,9 +5,11 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InvalidClassException;
 import java.sql.Connection;
+import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -18,6 +20,7 @@ import java.util.List;
 import java.util.logging.Level;
 
 import eu.telecom_bretagne.praxis.common.Log;
+import eu.telecom_bretagne.praxis.common.PraxisPreferences;
 import eu.telecom_bretagne.praxis.common.Utile;
 import eu.telecom_bretagne.praxis.core.execution.Result.Status;
 import eu.telecom_bretagne.praxis.core.workflow.WorkflowID;
@@ -66,7 +69,7 @@ public class ResultStore
 	 */
 	private static transient final DateFormat RESULT_date_fmt = new SimpleDateFormat("yyyyMMdd_HHmmss.SSSZ");
 
-	protected Connection       db_connection;
+	private Connection db_connection;
 
 	/**
 	 * Take care of deserializing the Result object.
@@ -82,33 +85,37 @@ public class ResultStore
 		return (Result) Utile.deserializeObject(result_asHexStr);
 	}
 
-	/**
-	 * @param c
-	 *            the connection to use
-	 * @throws NullPointerException
-	 *             if c is null
-	 */
-	public ResultStore(Connection c) throws NullPointerException, java.sql.SQLException
+	public ResultStore() throws java.sql.SQLException
 	{
-		if (c == null)
-			throw new NullPointerException("Invalid null connection");
-		db_connection = c;
+		final String workspace = PraxisPreferences.get(PraxisPreferences.rootNode, "prefix");
+		final String path = workspace + "/serverResultStore.bdb";
+		boolean createDB = !new File(path).exists();
+		try
+		{
+			Class.forName("org.sqlite.JDBC").newInstance();
+		}
+		catch (InstantiationException | IllegalAccessException | ClassNotFoundException e)
+		{
+			Log.log.log(Level.SEVERE, "Could not load or create sqlite JDBC driver", e);
+		}
+		db_connection = DriverManager.getConnection("jdbc:sqlite:/" + path);
+		if (createDB)
+		{
+			db_connection.createStatement().execute(ResultStore.RESULT_db_schema);
+		}
+		try ( final Statement statement = db_connection.createStatement() )
+		{
+			statement.execute("PRAGMA synchronous = NORMAL;");
+			statement.execute("PRAGMA journal_mode=WAL;");
+		}
+		catch(SQLException e)
+		{
+			Log.log.log(Level.WARNING, "Unable to set journal_mode to WAL w/ synchronous=normal", e);
+		}
 		db_connection.setAutoCommit(false);
 		this.consistencyCheck();
 	}
 
-	/**
-	 * Returns the connection with the underlying database. Callers must acquire a lock on the object before using it;
-	 * callers should NOT attempt to close the connection. <br>
-	 * Note: this method is currently only here so that the standalone version of praxis can upgrade the db schema,
-	 * see {@link eu.telecom_bretagne.praxis.common.ReleaseInfo#update_ServerDB_0_686()}.
-	 * @return the connection to the database
-	 */
-	public Connection getConnection()
-	{
-		return db_connection;
-	}
-
 	/**
 	 * Checks basic features in the DB, and removes elements that are invalid.
 	 * See source code for details.
diff --git a/src/eu/telecom_bretagne/praxis/core/workflow/Parameter.java b/src/eu/telecom_bretagne/praxis/core/workflow/Parameter.java
index fa1763d8aaa2804afec80e7de8441eb4de779390..87f96840854b58492fd4c5b3880a70b288838fa2 100644
--- a/src/eu/telecom_bretagne/praxis/core/workflow/Parameter.java
+++ b/src/eu/telecom_bretagne/praxis/core/workflow/Parameter.java
@@ -27,7 +27,7 @@ import eu.telecom_bretagne.praxis.core.workflow.Workflow.XMLWarnings;
  */
 public class Parameter implements Cloneable, PropertyChangeListener, java.io.Serializable
 {
-	
+
 	private static final long serialVersionUID = -7236633502138745978L;
 	public static final String INPUT_PROPERTY = "input";
 	public static final String OUTPUT_PROPERTY = "outputs";
@@ -36,41 +36,41 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 	public boolean isInput() {
 		return getDescription().getType().isInput();
 	}
-	
+
 	public boolean isOutput() {
 		return getDescription().getType().isOutput();
 	}
-	
+
 	public List<IOType> types() {
 		return getDescription().getTypes();
 	}
-	
-	
-	
+
+
+
 	protected static final String PARAM_ID   = "idref";
 	public    static final String INFO       = "info";
 	public    static final String INFO_NAME  = "name";
 	public    static final String INFO_VALUE = "value";
-	
+
 	/** Id pointing to the corresponding ParameterDescription */
 	protected String ref_id;
-	
+
 	/** The enclosing Program */
 	protected Program program;
-	
+
 	/** Value for the parameter, only for {@link ParameterType#INT},
 	 * {@link ParameterType#FLOAT}, {@link ParameterType#STRING}, {@link ParameterType#TEXT},
 	 * and {@link ParameterType#BOOLEAN} parameters */
 	String data = null;
-	
+
 	/** Used only by {@link ParameterType#ENUM} parameters */
 	Item item;
-	
+
 	/** Used only by {@link ParameterType#isInput() input} parameters
 	 * @see #io
 	 */
 	WorkflowInput input;
-	
+
 	/** Used only by {@link ParameterType#isInput() input} and 
 	 * {@link ParameterType#isOutput() output} parameters.
 	 * 
@@ -94,19 +94,26 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 	protected PropertyChangeSupport propChgSupport = new PropertyChangeSupport(this);
 
 	/**
-	 * @throws InvalidXMLException  */
+	 * @throws InvalidXMLException
+	 *             if the object can not be built from the informations included in the supplied XML node
+	 * @throws NullPointerException
+	 *             if parameter parent_prg is {@code null}.
+	 */
 	public Parameter(Element xml, Program parent_prg, XMLWarnings warnings)
 	throws InvalidXMLException
 	{
+		if ( parent_prg == null )
+			throw new NullPointerException("Parameter parent_prg cannot be null");
+
 		InvalidXMLException xml_exc = new InvalidXMLException(ELEMENT_TYPE.PARAMETER);
-		
+
 		ref_id = xml.getAttributeValue(PARAM_ID);
 		if (ref_id == null)
 			throw xml_exc;
 		xml_exc.setName(ref_id);
 		program = parent_prg;
 		Element info = xml.getChild(INFO);
-		
+
 		if ( getDescription() == null )
 		{
 			if (info!=null) /* if null, the parameter was not activated, so it is not use to report it */
@@ -116,7 +123,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 				                            info.getAttributeValue(INFO_VALUE), });
 			throw xml_exc.setMsg("Unable to find a valid description for parameter").setIsFatal(false);
 		}
-		
+
 		switch (getDescription().getType()) {
 			case FLOAT:
 			case INT:
@@ -167,16 +174,22 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 			case COMMAND:
 				break;
 		}
-		
 	}
 
 	/**
-	 *
-	 * @param description the corresponding parameter
+	 * Builds a new parameter.
+	 * 
+	 * @param description
+	 *            the corresponding description
 	 * @param parent_prg
+	 *            the program this parameter belongs to
+	 * @throws NullPointerException
+	 *             if parameter parent_prg is {@code null}.
 	 */
 	public Parameter(ParameterDescription description, Program parent_prg)
 	{
+		if ( parent_prg == null )
+			throw new NullPointerException("Parameter parent_prg cannot be null");
 		program = parent_prg;
 		init(description);
 	}
@@ -213,21 +226,21 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 				eu.telecom_bretagne.praxis.common.Utile.unimplemented();
 		}
 	}
-	
+
 	/** */
 	public Element toXML()
 	{
 		Element param_xml = new Element("parameter");
-		
+
 		param_xml.setAttribute(PARAM_ID, ref_id);
-		
+
 		Element data_xml = new Element("data");
-		
+
 		ParameterDescription param_desc = getDescription();
 		String param_desc_displayName=param_desc.getDisplayName();
 		if (param_desc_displayName==null)
 			param_desc_displayName="";
-		
+
 		if ( isInput() && input != null)
 			data_xml.setAttribute("input_id", ""+program.workflow.getIdForInput(input));
 		else if ( isInput() )
@@ -259,33 +272,30 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 		param_xml.addContent(data_xml);
 		return param_xml;
 	}
-	
-	/** Returns the 'id' used in the xml representation of an output parameter:
-	 * @return a string made w/ the concatenation of the program's id, the 
-	 * character point <tt>"."</tt> and the receiver's ref_id; it is used in
-	 * {@link #toXML()}
-	 * @throw {@link IllegalStateException} if the receiver is not an
-	 * {@link #isOutput() output parameter}.
+
+	/**
+	 * Returns the 'id' used in the xml representation of an output parameter:
+	 * 
+	 * @return a string made w/ the concatenation of the program's id, the character point <tt>"."</tt> and the
+	 *         receiver's ref_id; it is used in {@link #toXML()}
+	 * @throws IllegalStateException
+	 *             if the receiver is not an {@link #isOutput() output parameter}.
 	 */ 
 	public String output_xml_id() { // TODO make it visibility-package again (same for workflow.getIdForProgram())
 		if ( ! isOutput() )
 			throw new IllegalStateException("receiver is not an output");
 		return program.workflow.getIdForProgram(program)+"."+ref_id;
 	}
-	
+
 	/** Returns the corresponding ParameterDescription
 	 * 
 	 * @return the corresponding ParameterDescription
-	 * @throws IllegalStateException
 	 */
-	public ParameterDescription getDescription() throws IllegalStateException
+	public ParameterDescription getDescription()
 	{
-		if ( program == null )
-			throw new IllegalStateException("");
-		
 		return  program.getDescription().getParameterWithId(this.ref_id);
 	}
-	
+
 	/**
 	 * Evaluates the dependencies of the receiver and, given the values of
 	 * the other parameters in the {@link #getProgram() receiver's program},
@@ -305,7 +315,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 			throw new UnsupportedOperationException("isActivated() raised on "+prg_param, e);
 		}
 	}
-	
+
 	/**
 	 * Tells whether the value hold by this parameter is valid with respect to the parameter's description. Depending on
 	 * the context, callers may be also interested in knowing whether the parameter is {@link #isActivated() activated}
@@ -323,22 +333,24 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 	public String getData() {
 		return data;
 	}
-	
+
 	public void setData(String data) {
 		propChgSupport.firePropertyChange("data", this.data, this.data = data);
 	}
-	
+
 	/** Returns the selected item for a {@link ParameterType#ENUM} parameter.
 	 * @return one of the receiver's {@link ParameterDescription#getItems() possible items}.
 	 */ 
 	public Item getItem() {
 		return item;
 	}
-	
-	/** Sets the selected item
-	 * @throw {@link IllegalArgumentException} if the item is not compatible
-	 * with the {@link ParameterDescription#hasItem(Item) parameter's description},
-	 * or if the receiver is not a {@link ParameterType#ENUM} parameter
+
+	/**
+	 * Sets the selected item
+	 * 
+	 * @throws IllegalArgumentException
+	 *             if the item is not compatible with the {@link ParameterDescription#hasItem(Item) parameter's
+	 *             description}, or if the receiver is not a {@link ParameterType#ENUM} parameter
 	 */
 	public void setItem(Item item) {
 		if ( getDescription().hasItem(item) )
@@ -346,14 +358,14 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 		else
 			throw new IllegalArgumentException("parameter item is not valid");
 	}
-	
+
 	/**
 	 * @return Returns the program.
 	 */
 	public Program getProgram() {
 		return program;
 	}
-	
+
 	/** Tells whether an input parameter and an output parameter are compatible
 	 * @see ParameterDescription#isCompatibleWith(ParameterDescription) the
 	 * corresponding method in ParameterDescription
@@ -361,11 +373,11 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 	public boolean isCompatibleWith(Parameter parameter) {
 		return getDescription().isCompatibleWith(parameter.getDescription());
 	}
-	
+
 	public WorkflowInput getInput() {
 		return input;
 	}
-	
+
 	public void setInput(WorkflowInput input) {
 		if (this.input!=null)
 		{
@@ -375,8 +387,8 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 			input.addOutput(this);
 		propChgSupport.firePropertyChange(INPUT_PROPERTY, this.input, this.input = input);
 	}
-	
-	
+
+
 	/**
 	 * Returns the input parameter connected to this (output) parameter.
 	 * @return the connected parameter, or null if it is not connected.
@@ -384,7 +396,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 	public Parameter getPrgInput() {
 		return prg_input;
 	}
-	
+
 	/**
 	 * Returns the output parameters connected to this (input) parameter.
 	 * @return An array of the connected parameters.
@@ -455,7 +467,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 			throw new IllegalArgumentException("setPrgInput() only for input parameters");
 		if ( output_parameter!=null && !output_parameter.isOutput() )
 			throw new IllegalArgumentException("setPrgInput() param must be an output parameter");
-		
+
 		Parameter old_input = prg_input;
 		/* disconnect any existing link */
 		if ( prg_input != null )
@@ -469,7 +481,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 		}
 		propChgSupport.firePropertyChange(PRG_INPUT_PROPERTY, old_input, this.input);
 	}
-	
+
 	/**
 	 * Adds the supplied parameter from the list of this parameter's outputs. This method also takes care of calling
 	 * {@link #setPrgInput(Parameter)} with <code>this</code> as its parameter.<br>
@@ -481,7 +493,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 	 * @throws IllegalArgumentException
 	 *             if <code>this</code> is not an output parameter or if the supplied input parameter is not an input
 	 *             parameter
-	 * @throws NullPointerExcoutput_parametereption
+	 * @throws NullPointerException
 	 *             if input_parameter is null
 	 * @see #setPrgInput(Parameter)
 	 * @see #removeOutput(Parameter)
@@ -492,10 +504,10 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 			throw new IllegalArgumentException("addOutput() only for output parameters");
 		if ( !input_parameter.isInput() ) // NullPointerExc. if null
 			throw new IllegalArgumentException("addOutput() param must be an input parameter");
-		
+
 		input_parameter.setPrgInput(this);
 	}
-	
+
 	/**
 	 * Internally used to simply add the supplied input parameter to the outputs of this object; no extra operation is
 	 * done, except that property change listeners are notified.<br>
@@ -513,7 +525,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 			propChgSupport.firePropertyChange(OUTPUT_PROPERTY, null, getOutputs());
 		}
 	}
-	
+
 	/**
 	 * Removes the supplied parameter from the list of this parameter's outputs. This method also takes care of
 	 * resetting the input parameter by calling {@link #setPrgInput(Parameter)} with a null value.<br>
@@ -538,7 +550,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 		
 		input_parameter.setPrgInput(null);
 	}
-	
+
 	/**
 	 * Internally used to simply remove the supplied input parameter from this' outputs; no extra operation is done,
 	 * except that property change listeners are notified.<br>
@@ -553,7 +565,7 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 		if ( outputs.remove(input_parameter) )
 			propChgSupport.firePropertyChange(OUTPUT_PROPERTY, null, getOutputs());
 	}
-	
+
 	/**
 	 * Sets this program's {@link #getInput() "workflow input"} to <code>null</code>, and removes any
 	 * {@link #getPrgInput() input connections} and {@link #getOutputs() output connections} this parameter may model.<br>
@@ -570,9 +582,18 @@ public class Parameter implements Cloneable, PropertyChangeListener, java.io.Ser
 		for (Parameter p: outputs_copy) // we need a copy here: p.setPrgInput() modifies this.outputs
 			p.setPrgInput(null);
 	}
-	
-	/** TODO: add big fat warning, do not use, for GUI only */
+
+	/**
+	 * TODO: add big fat warning, do not use, for GUI only
+	 * 
+	 * @param prg
+	 *            the program to which the shallow copy should be attached. It must be non-null.
+	 * @throws NullPointerException
+	 *             if parameter prg is {@code null}.
+	 */
 	public Parameter shallowCopyFor(Program prg) {
+		if ( prg == null )
+			throw new NullPointerException("parameter prg cannot be null");
 		Parameter p = null;
 		try {
 			p = (Parameter) this.clone();
diff --git a/src/eu/telecom_bretagne/praxis/core/workflow/Program.java b/src/eu/telecom_bretagne/praxis/core/workflow/Program.java
index 6b61e6f0daa543b2c193bb9d3f43e20bf2f74852..af302180535dc55fb0b2d9ab73c560f1d40669d0 100644
--- a/src/eu/telecom_bretagne/praxis/core/workflow/Program.java
+++ b/src/eu/telecom_bretagne/praxis/core/workflow/Program.java
@@ -88,12 +88,17 @@ public class Program
 	
 	/**
 	 * Builds a new program
+	 * 
 	 * @param xml_node
+	 *            the XML element describing the program
 	 * @param warnings
+	 *            the object is which warnings aimed at the user are stored
 	 * @param parent
 	 *            the workflow containing the program. It cannot be {@code null}.
 	 * @throws InvalidXMLException
-	 * @throws NullPointerException if {@code parent} is {@code null}.
+	 *             if the object cannot be created from the supplied XML element.
+	 * @throws NullPointerException
+	 *             if {@code parent} is {@code null}.
 	 */
 	public Program(Element xml_node, Workflow parent, XMLWarnings warnings) throws InvalidXMLException
 	{
@@ -103,7 +108,9 @@ public class Program
 	
 	/**
 	 * Builds a new program
+	 * 
 	 * @param prgDesc
+	 *            this program's description
 	 * @param parent
 	 *            the workflow containing the program. It cannot be {@code null}.
 	 * @throws NullPointerException
@@ -235,12 +242,16 @@ public class Program
 	/**
 	 * Reads the provided xml element and builds the corresponding Program. During this operation, the receiver is
 	 * completely reinitialized.
+	 * 
 	 * @param xml_node
+	 *            the XML element describing the program
+	 * @param warnings
+	 *            the object is which warnings aimed at the user are stored
 	 * @throws InvalidXMLException
 	 *             TODO pointer to DTD
 	 * @see #toXML()
 	 */
-	public Map<String, String> fromXML(Element xml_node, XMLWarnings warnings) throws InvalidXMLException
+	public void fromXML(Element xml_node, XMLWarnings warnings) throws InvalidXMLException
 	{
 		List<Element> params;
 		InvalidXMLException xml_exc = new InvalidXMLException(ELEMENT_TYPE.PROGRAM);
@@ -335,14 +346,13 @@ public class Program
 				if (p.getDescription().getId().equals(paramDesc.getId()))
 					parameters.add(p);
 		}
-		return null;
 	}
 	
 	/**
 	 * Builds and returns the XML representation of self.
 	 * This is equivalent to {@link #toXML(boolean) toXML(false)}
 	 * @return the xml equivalent for <tt>this</tt>
-	 * @see #fromXML(Element)
+	 * @see #fromXML(Element, XMLWarnings)
 	 */
 	public Element toXML()
 	{
@@ -357,7 +367,7 @@ public class Program
 	 *            are included.
 	 * @return the xml equivalent for <tt>this</tt>, minus parameters that should not be exported if
 	 *         {@code includeUserBoundParameters} is {@code true}.
-	 * @see #fromXML(Element)
+	 * @see #fromXML(Element, XMLWarnings)
 	 */
 	public Element toXML(boolean includeUserBoundParameters)
 	{
@@ -392,7 +402,6 @@ public class Program
 	 * from XML) expect that the receiver always returns the same object --see
 	 * {@link RemoteComponents#cacheResourceRepositoryAnswers} for details.
 	 * @return the description attached to this program.
-	 * @see #description
 	 */
 	public ProgramDescription getDescription()
 	{
@@ -515,7 +524,7 @@ public class Program
 	/**
 	 * Returns the ExecutionSet this activity belongs to. Execution sets are built when the execution is planned.<br/>
 	 * Activities are assigned to or removed from an execution set by the sets themselves, see
-	 * {@link ExecutionSet#add(eu.telecom_bretagne.praxis.core.execution.Activity)} and {@link ExecutionSet#remove(Object)}.
+	 * {@link ExecutionSet#add(eu.telecom_bretagne.praxis.core.workflow.Program)} and {@link ExecutionSet#remove(Object)}.
 	 * @return the execution set this activity belongs to
 	 */
 	public ExecutionSet getExecutionSet()
diff --git a/src/eu/telecom_bretagne/praxis/core/workflow/ProgramDelegate.java b/src/eu/telecom_bretagne/praxis/core/workflow/ProgramDelegate.java
index 28cd9f88fbb5fe9e349da0280095ec567e123696..1848d6cea2b322007966de43d037133bb39a2981 100644
--- a/src/eu/telecom_bretagne/praxis/core/workflow/ProgramDelegate.java
+++ b/src/eu/telecom_bretagne/praxis/core/workflow/ProgramDelegate.java
@@ -14,7 +14,7 @@ import eu.telecom_bretagne.praxis.core.resource.ProgramDescription;
 import eu.telecom_bretagne.praxis.core.workflow.Workflow.XMLWarnings;
 
 /**
- * A delegate for the class {@link eu.telecom_bretagne.praxis.core.resource.Program}.
+ * A delegate for the class {@link eu.telecom_bretagne.praxis.core.workflow.Program}.
  * @author Sébastien Bigaret
  */
 public class ProgramDelegate
@@ -68,12 +68,10 @@ public class ProgramDelegate
 	 *            null is sufficient.
 	 * @return the requested description, or null if none can be found. When non-null, the returned description will
 	 *         be used to build the program.
-	 * @throws InvalidXMLException
 	 */
 	public ProgramDescription findAlternateProgramDescription(String prg_description_id,
 	                                                          String prg_description_full_name,
 	                                                          XMLWarnings warnings, InvalidXMLException xml_exc)
-	        throws InvalidXMLException
 	{
 
 		ProgramDescription prgDesc = null;
diff --git a/src/eu/telecom_bretagne/praxis/core/workflow/Workflow.java b/src/eu/telecom_bretagne/praxis/core/workflow/Workflow.java
index 32a2643c66f08f4b44a1833ceadc943f44a24a9a..36a734ed0917bf1da87c1b1f9ac569682216cd77 100644
--- a/src/eu/telecom_bretagne/praxis/core/workflow/Workflow.java
+++ b/src/eu/telecom_bretagne/praxis/core/workflow/Workflow.java
@@ -96,8 +96,8 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
 		private ArrayList<I18NCouple> warnings = new ArrayList<I18NCouple>();
 		/**
 		 * Adds a new warning.
-		 * @param warning
-		 * @param params
+		 * @param warning a string describing the warning, with placeholders for the params
+		 * @param params the parameters to insert into the warning's placeholders
 		 */
 		public void add(String warning, String[] params)
 		{
@@ -304,7 +304,7 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
     }
     
     /** Returns the program corresponding to the supplied id
-     * @param prg_id
+     * @param prg_id a program id
      * @return {@code null} if no such program exists
      */
     public Program getProgramWithId(String prg_id) {
@@ -324,7 +324,7 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
     /** Returns the input (file or database) corresponding to the supplied id.
      * Note that ids are internal stuff, only needed when storing and restoring
      * from xml files.
-     * @param input_id
+     * @param input_id an input id
      * @return {@code null} if no such input exists
      */
     public WorkflowInput getInputWithId(long input_id) { // TODO back to: package-visibility, not public
@@ -338,9 +338,11 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
      * from xml files.
      * @param input the {@link WorkflowInput}
      * @return the corresponding id
-     * @throw {@link IllegalArgumentException} if no such WorkflowInput object is referenced within this Workflow.
+     * @throws IllegalArgumentException if no such WorkflowInput object is referenced within this Workflow.
      */
-    public long getIdForInput(WorkflowInput input) { // TODO back to: package-visibility, not public
+    public long getIdForInput(WorkflowInput input) // TODO back to: package-visibility, not public
+    throws IllegalArgumentException
+    {
         for (long input_id: inputs.keySet())
             if ( inputs.get(input_id)==input )
                 return input_id;
@@ -355,9 +357,11 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
      * from xml files.
      * @param program the {@link Program} for which the id is needed
      * @return the corresponding id
-     * @throw {@link IllegalArgumentException} if no such Program object is referenced within this Workflow.
+     * @throws IllegalArgumentException if no such Program object is referenced within this Workflow.
      */
-    public String getIdForProgram(Program program) {
+    public String getIdForProgram(Program program)
+    throws IllegalArgumentException
+    {
         for (String prg_id: programs.keySet())
             if ( programs.get(prg_id)==program )
                 return prg_id;
@@ -399,6 +403,9 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
         return prg;
     }
      
+    /* (non-Javadoc)
+     * @see java.lang.Object#clone()
+     */
     @Override
     public Workflow clone() {
         Workflow clone;
@@ -720,9 +727,10 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
 	 * Returns the workflow serialized as XML, in a {@link Document}
 	 * 
 	 * @param includeUserBoundParameters
-	 *            Indicates whether parameters that are marked as "userBound" should be included.
+	 *            Indicates whether parameters that are marked as
+	 *            {@link eu.telecom_bretagne.praxis.core.resource.ParameterDescription#isUserBound() "userBound"} should
+	 *            be included.
 	 * @return the serialized workflow.
-	 * @see #toXML()
 	 */
     public Document toXMLDocument(boolean includeUserBoundParameters)
     {
@@ -743,7 +751,7 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
     /**
      * Save the workflow in a zipped file, along with all its (valid) input files.
      * The zip contains the workflow, named workflow.<ext>, and its input files
-     * @param zipFile
+     * @param zipFile the file into which the workflow and its inputs are zipped.
      * @throws IOException in case any error occurs when writing the zip. When this happens, the
      * zipFile, is created, is deleted before the exception is raised.
      */
@@ -759,6 +767,7 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
 	 * <ul>
 	 * <li>can contain the workflow itself,
 	 * <li>can put everything at the top-level, or within a specified directory the workflow itself
+	 * 
 	 * @param zipFile
 	 *            the zip file to be created
 	 * @param zipDirectory
@@ -771,13 +780,19 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
 	 *            if non-null, the workflow is also put in the zip with the provided filename. If {@code null}, the
 	 *            workflow is not put into the zip.
 	 * @param abortOnErrors
-	 *            if {@code true}, any error happening when trying to add an input file is fatal. Otherwise, the zip
-	 *            is build and ignores input files that cannot be found or read. NBote that this does not avoid
+	 *            if {@code true}, any error happening when trying to add an input file is fatal. Otherwise, the zip is
+	 *            build and ignores input files that cannot be found or read. NBote that this does not avoid
 	 *            {@code IOException} at all: if the zip could not be written, the exception is always thrown.
+	 * @param includeUserBoundParameters
+	 *            Indicates whether parameters that are marked as
+	 *            {@link eu.telecom_bretagne.praxis.core.resource.ParameterDescription#isUserBound() "userBound"} should
+	 *            be included.
 	 * @return the workflow that was saved. Note that this workflow is <b>not</b> the same as {@code this}, it is a
 	 *         clone of this object where input files are adapted so that they refer to the same filenames as those
 	 *         stored in the zip, relative to the zip's root directory.
 	 * @throws IOException
+	 *             in case any error occurs when writing the zip. When this happens, the zipFile, is created, is deleted
+	 *             before the exception is raised.
 	 */
 	public Workflow exportWorkflowWithInput(File zipFile, String zipDirectory, File copyToDirectory,
 	                                        String workflow_filenameInZip, boolean abortOnErrors,
@@ -1004,6 +1019,10 @@ public class Workflow implements java.lang.Cloneable, java.io.Serializable
 		return true;
 	}
 
+	/**
+	 * Returns the workflow's {@link #getName() name}.
+	 * @see java.lang.Object#toString()
+	 */
 	@Override
 	public String toString()
 	{
diff --git a/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/ProgramParser.java b/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/ProgramParser.java
index 485c11289fec1e60987eb2bcf5c0cf579a209c08..aebf2b45519eea037b8491f11720c430934ffedd 100644
--- a/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/ProgramParser.java
+++ b/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/ProgramParser.java
@@ -29,15 +29,21 @@ public class ProgramParser
     static final String PARAMETER = "parameter";
 
     /**
-     * @param workflow
-     *            The attached workflow. It must not be {@code null}. If the method is successful, this workflow is
-     *            <b>modified</b>: the return program is added to the workflow just before being returned.
-     * @param startElement
-     * @param eventReader
-     * @param warnings
-     * @return
-     * @throws InvalidXMLException
-     */
+	 * Builds a program from the informations stored in an XML structure.
+	 * 
+	 * @param workflow
+	 *            The attached workflow. It must not be {@code null}. If the method is successful, this workflow is
+	 *            <b>modified</b>: the return program is added to the workflow just before being returned.
+	 * @param startElement
+	 *            the root element {@code <program>} containing the informations
+	 * @param eventReader
+	 *            the event reader with which the parsing of the supplied workflow has begun
+	 * @param warnings
+	 *            the object is which warnings intended for the user should be stored.
+	 * @return the built Program object
+	 * @throws InvalidXMLException
+	 *             if case the XML is not valid
+	 */
     public static Program fromXML(Workflow workflow, final StartElement startElement, XMLEventReader eventReader,
                                   XMLWarnings warnings)
     throws InvalidXMLException
diff --git a/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/WorkflowParser.java b/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/WorkflowParser.java
index c1162f94de4c7b3a033e988a2bba612935ffde78..f0157e0e86b61e799477490d84cfc6ab5649c40b 100644
--- a/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/WorkflowParser.java
+++ b/src/eu/telecom_bretagne/praxis/core/workflow/io/xml/WorkflowParser.java
@@ -38,12 +38,32 @@ public abstract class WorkflowParser
 
     static final String INPUTS = "inputs";
 
-    public static Workflow create(String filename, Workflow.XMLWarnings xmlWarnings) throws Throwable
+    /**
+	 * Builds a new workflow by reading a file
+	 * 
+	 * @param filename
+	 *            the path of the xml file to read
+	 * @param xmlWarnings
+	 *            the object is which warnings intended for the user should be stored.
+	 * @return the new workflow
+	 */
+    public static Workflow create(String filename, Workflow.XMLWarnings xmlWarnings)
+    throws InvalidXMLException, IOException, XMLStreamException
     {
         return create(new File(filename), xmlWarnings);
     }
 
-    public static Workflow create(File file, Workflow.XMLWarnings xmlWarnings) throws Throwable
+    /**
+	 * Builds a new workflow by reading an XML file.
+	 * 
+	 * @param file
+	 *            the file containing the workflow
+	 * @param xmlWarnings
+	 *            the object is which warnings intended for the user should be stored.
+	 * @return the new workflow
+	 */
+    public static Workflow create(File file, Workflow.XMLWarnings xmlWarnings)
+    throws InvalidXMLException, IOException, XMLStreamException
     {
         if ( ! (file.exists() && file.isFile() && file.canRead()) )
             throw new IllegalArgumentException("invalid workflow file");
@@ -342,7 +362,8 @@ public abstract class WorkflowParser
     }
 
     /**
-     * @param args
+     * Simple test for Workflow read/write
+     * @param args (ignored)
      */
     public static void main(String[] args) throws Throwable
     {
diff --git a/src/eu/telecom_bretagne/praxis/server/Server.java b/src/eu/telecom_bretagne/praxis/server/Server.java
index 6b43a53a89154bd0aa55db220ea057a4ef7e55f8..5fca1c8de241750c70c27eb3462dbcccc0013d4a 100644
--- a/src/eu/telecom_bretagne/praxis/server/Server.java
+++ b/src/eu/telecom_bretagne/praxis/server/Server.java
@@ -73,11 +73,13 @@ implements PlatformToServerEventListener, ClientToServerEventListener
 
     /* **** Communicating w/ the platform **** */
 	/**
-	 * The zipped file is deleted after the event has been sent.
+	 * Request an execution. Note that the zipped file is deleted after the event has been sent.
+	 * 
 	 * @param execEngine
+	 *            the execution engine prepared for executing the task
 	 * @param zip
-	 *            A zip file containing the necessary input files. If no file is needed for the execution, it equals
-	 *            to <code>null</code>. <br>
+	 *            A zip file containing the necessary input files. If no file is needed for the execution, it equals to
+	 *            <code>null</code>. <br>
 	 *            <b>Please note that the zip file is deleted after the event has been sent</b>.
 	 */
     public void requestExecution(ExecutionEngine execEngine, File zip)
diff --git a/src/eu/telecom_bretagne/praxis/server/Serveur.java b/src/eu/telecom_bretagne/praxis/server/Serveur.java
index ff8e0be2e3b3c3ad65cbd883f23f31049ad93039..bb7a22186f0c3d220583090d15e41984e7af9752 100644
--- a/src/eu/telecom_bretagne/praxis/server/Serveur.java
+++ b/src/eu/telecom_bretagne/praxis/server/Serveur.java
@@ -7,10 +7,6 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.rmi.RemoteException;
 import java.rmi.server.UnicastRemoteObject;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -68,8 +64,6 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
 	 */
 	static private List<Server>                 platforms  = Collections.synchronizedList(new ArrayList<Server>());
 	
-	static private final String                 resultStoreDBFilename;
-	
 	static private ResultStore                  resultStore;
 	
 	static private String                       tmpDir;
@@ -87,9 +81,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
 
 	static
 	{
-		String workspace = PraxisPreferences.get(PraxisPreferences.rootNode, "prefix");
-		tmpDir = workspace + "/tmp";
-		resultStoreDBFilename = workspace + "/serverResultStore.bdb";
+		tmpDir = PraxisPreferences.get(PraxisPreferences.rootNode, "prefix") + "/tmp";
 		
 		try
 		{
@@ -98,26 +90,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
 				serveur    = new Serveur("unused_use_ssl"); // moved here because of RemoteException
 			else
 				serveur    = new Serveur();
-			boolean createDB = !new File(resultStoreDBFilename).exists();
-			Class.forName("org.sqlite.JDBC").newInstance();
-			@SuppressWarnings("resource") // does not leak! it is used by the ResultStore object! do NOT close it!
-			Connection c = DriverManager.getConnection("jdbc:sqlite:/" + resultStoreDBFilename);
-			if (createDB)
-			{
-				c.createStatement().execute(ResultStore.RESULT_db_schema);
-			}
-			try
-			{
-				final Statement statement = c.createStatement();
-				statement.execute("PRAGMA synchronous = NORMAL;");
-				statement.execute("PRAGMA journal_mode=WAL;");
-				statement.close();
-			}
-			catch(SQLException e)
-			{
-				Log.log.log(Level.WARNING, "Unable to set journal_mode to WAL w/ synchronous=normal", e);
-			}
-			resultStore = new ResultStore(c);
+			resultStore = new ResultStore();
 		}
 		catch (Exception e)
 		{
@@ -330,7 +303,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
 
 	/**
 	 * Tells whether there is a client currently connected to the server with the supplied login.
-	 * @param login
+	 * @param login a client's login
 	 * @return {@code true} if such a client exists, {@code false} otherwise
 	 */
 	public static boolean isClientRegistered(String login)
@@ -376,7 +349,6 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
 
 	/**
 	 * Starts the socket server on the specified port.
-	 * @return true if the server was successfully launched, false otherwise
 	 */
 	public static void startSocketServer()
 	{
@@ -400,7 +372,6 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
 	
 	/**
 	 * Starts the SSL socket server on the specified port.
-	 * @return true if the server was successfully launched, false otherwise
 	 */
 	public static void startSSLSocketServer()
 	{
diff --git a/src/eu/telecom_bretagne/praxis/server/execution/ExecutionEngine.java b/src/eu/telecom_bretagne/praxis/server/execution/ExecutionEngine.java
index d1d7cd7a5a3f6afa6b8960ea032d4dae34450682..df304aee153b808056c3ffa15613359554c8e29b 100644
--- a/src/eu/telecom_bretagne/praxis/server/execution/ExecutionEngine.java
+++ b/src/eu/telecom_bretagne/praxis/server/execution/ExecutionEngine.java
@@ -17,7 +17,6 @@ import eu.telecom_bretagne.praxis.core.workflow.Program;
 import eu.telecom_bretagne.praxis.core.workflow.WorkflowID;
 import eu.telecom_bretagne.praxis.server.execution.platform.PlatformDescription;
 import eu.telecom_bretagne.praxis.server.execution.platform.ProgramExecutionEngine;
-import eu.telecom_bretagne.praxis.server.execution.platform.SGEExecutionEngine;
 import eu.telecom_bretagne.praxis.server.execution.platform.ShellExecutionEngine;
 import eu.telecom_bretagne.praxis.server.execution.platform.WorkflowExecutionEngine;
 import eu.telecom_bretagne.praxis.server.execution.platform.PlatformDescription.CannotExecuteException;
@@ -104,7 +103,7 @@ public abstract class ExecutionEngine
 	 */
 	private static Map<String, ExecutionEngineConfigurationFactory> factories = null;
 
-	/** An executionprogress monitor doing nothing whose operations are no-op */
+	/** An execution progress monitor doing nothing whose operations are no-op */
 	public static final ExecutionProgressMonitor noopProgressMonitor = new ExecutionProgressMonitor()
 	{
 		@Override public void setProgress(Result result) { }
@@ -115,14 +114,13 @@ public abstract class ExecutionEngine
 	 * <ul>
 	 * <li> {@link ProgramExecutionEngine.ProgramExecutionEngineConfigurationFactory},
 	 * <li> {@link ShellExecutionEngine.ShellExecutionEngineConfigurationFactory},
-	 * <li> {@link SGEExecutionEngine.SGEExecutionEngineConfigurationFactory}.
+	 * <li> {@link WorkflowExecutionEngine.WorkflowExecutionEngineConfigurationFactory}.
 	 * </ul>
 	 */
 	static
 	{
 		factories = new Hashtable<String, ExecutionEngineConfigurationFactory>();
 		registerConfigurationFactory(new ShellExecutionEngine.ShellExecutionEngineConfigurationFactory());
-		registerConfigurationFactory(new SGEExecutionEngine.SGEExecutionEngineConfigurationFactory());
 		registerConfigurationFactory(new ProgramExecutionEngine.ProgramExecutionEngineConfigurationFactory());
 		registerConfigurationFactory(new WorkflowExecutionEngine.WorkflowExecutionEngineConfigurationFactory());
 	}
@@ -223,6 +221,7 @@ public abstract class ExecutionEngine
 		
 		targetName = targetName.trim().toUpperCase(Locale.ENGLISH);
 		return Enum.valueOf(getTargets(), targetName);
+		//return Target.valueOf(targetName);
 	}
 	
 	/**
diff --git a/src/eu/telecom_bretagne/praxis/server/execution/SimpleFormatterPlatform.java b/src/eu/telecom_bretagne/praxis/server/execution/SimpleFormatterPlatform.java
index 7ced7f21ea40c3953d5b948a61b40d8244676745..7c692c56b6fddabf9a7adb8924f023be158be5ad 100644
--- a/src/eu/telecom_bretagne/praxis/server/execution/SimpleFormatterPlatform.java
+++ b/src/eu/telecom_bretagne/praxis/server/execution/SimpleFormatterPlatform.java
@@ -404,7 +404,7 @@ public abstract class SimpleFormatterPlatform
 	 * offered to subclasses, since subclasses may as well override {@link #generateScript(Resource)}, and iterate on
 	 * the {@link Resource#getAllConcreteResources() concrete resources} after calling super implementation.
 	 */
-	public void generateScript_justProcessed(Activity activity) {}
+	protected void generateScript_justProcessed(Activity activity) {}
 	
 	protected Enum<?> getTarget(ParameterDescription description, Parameter parameter)
 	{
diff --git a/src/eu/telecom_bretagne/praxis/server/execution/platform/SGEExecutionEngine.java b/src/eu/telecom_bretagne/praxis/server/execution/platform/SGEExecutionEngine.java
deleted file mode 100644
index 9a160d39e17f79ad7625005107e7139459ba0ee0..0000000000000000000000000000000000000000
--- a/src/eu/telecom_bretagne/praxis/server/execution/platform/SGEExecutionEngine.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/* License: please refer to the file README.license located at the root directory of the project */
-package eu.telecom_bretagne.praxis.server.execution.platform;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Level;
-
-import org.ggf.drmaa.DrmaaException;
-import org.ggf.drmaa.ExitTimeoutException;
-import org.ggf.drmaa.JobInfo;
-import org.ggf.drmaa.JobTemplate;
-import org.ggf.drmaa.Session;
-import org.ggf.drmaa.SessionFactory;
-
-import eu.telecom_bretagne.praxis.common.Environment;
-import eu.telecom_bretagne.praxis.common.Log;
-import eu.telecom_bretagne.praxis.common.Utile;
-import eu.telecom_bretagne.praxis.core.execution.ExecutionID;
-import eu.telecom_bretagne.praxis.core.execution.ProgramResult;
-import eu.telecom_bretagne.praxis.core.execution.Result;
-import eu.telecom_bretagne.praxis.core.workflow.WorkflowID;
-import eu.telecom_bretagne.praxis.server.execution.ExecutionEngine;
-
-
-
-public class SGEExecutionEngine
-    extends ShellExecutionEngine
-{
-	private static final long serialVersionUID = -6531803324180143746L;
-
-	/**
-	 * In addition to the keys accepted by the superclass, the configuration file for the SGE execution engine accepts
-	 * the following key:
-	 * <ul>
-	 * <li> {@value #JOB_CATEGORY}: if set, the jobs will be launched in the SGE queue corresponding to this
-	 * category.</li>
-	 * </ul>
-	 * @author Sébastien Bigaret
-	 */
-	@SuppressWarnings("serial")
-    public static final class SGEExecutionEngineConfiguration
-	extends ShellExecutionEngineConfiguration
-	{
-		/** The name of the configuration item for {@link #getJobCategory()} is {@value}. */
-		public static final String JOB_CATEGORY = "job-category";
-		
-		public SGEExecutionEngineConfiguration(PlatformDescription platform)
-		{
-			super(platform);
-		}
-		
-		/**
-		 * Returns the DRMAA job category to use for all jobs launched by the SGE exec. engine.
-		 * @return the job category as specified in the platform's descriptions. It may be null or empty. When it is
-		 *         non-null, the returned value is {@link String#trim() trimmed}.
-		 */
-		public String getJobCategory()
-		{
-			final String category = platform.getConfiguration(JOB_CATEGORY);
-			return category!=null ? category.trim() : null;
-		}
-
-		@Override
-		public ExecutionEngine getEngine(WorkflowID workflowID, ExecutionID executionID)
-		{
-			return new SGEExecutionEngine(this, workflowID, executionID);
-		}
-	}
-
-	public static class SGEExecutionEngineConfigurationFactory
-	implements ExecutionEngine.ExecutionEngineConfigurationFactory
-	{
-		/** Returns <code>"SGE"</code> */
-		public String key() { return "SGE"; }
-		
-		/**
-		 * Builds and returns a new SGE Platform
-		 * @exception IllegalArgumentException
-		 *                if the supplied document's root element has no attribute <code>os</code> or if its value
-		 *                is not a valid argument for {@link SGEExecutionEngine#set_OS(String)}.
-		 */
-		public ExecutionEngineConfiguration build(PlatformDescription platform)
-		{
-			return new SGEExecutionEngineConfiguration(platform);
-		}
-	}
-
-	/* ---- static methods for managing the only Session objet that can be created ---- */
-	protected static ArrayList<String> runningJobsIDs = new ArrayList<String>();
-	protected static Session session;
-
-	/** The file into which the SGE job will dump the standard output */
-	protected static final String stdout_filename = "praxis_capexbio_sge.stdout";
-	/** The file into which the SGE job will dump the standard error */
-	protected static final String stderr_filename = "praxis_capexbio_sge.stderr";
-	
-	/**
-	 * Runs a new job.<br>
-	 * <b>IMPORTANT</b> any job created by this method must be correctly handled; this implies that the caller must
-	 * make sure that it calls the two following methods: <li>
-	 * <ul> {@link #getJobInfo(String)} </ul>
-	 * <ul> {@link #forgetJob(String)}  </ul>
-	 * </li>
-	 * @param scriptPath the path of the script that should be executed
-	 * @param executionDir the path of the directory where the script should be executed
-	 * @return the job's ID, or null if the job couldn't be started.
-	 * @see #getJobInfo(String)
-	 * @see #forgetJob(String)
-	 */
-	protected static synchronized String runJob(String scriptPath, String executionDir, String jobCategory)
-	{
-		String jobID;
-		boolean init_session = false;
-		if (session==null)
-		{
-			Log.log.fine("Opening a org.ggf.drmaa.Session");
-			SessionFactory factory = SessionFactory.getFactory();
-			session = factory.getSession();
-			init_session = true;
-		}
-		else
-		{
-			Log.log.fine("A org.ggf.drmaa.Session is already opened, fine");
-		}
-		try
-		{
-			if (init_session)
-				session.init("");
-			JobTemplate jt = session.createJobTemplate();
-			jt.setRemoteCommand(scriptPath);
-			jt.setArgs(Arrays.asList());
-			jt.setWorkingDirectory(executionDir);
-			jt.setErrorPath(":"+stderr_filename);
-			jt.setOutputPath(":"+stdout_filename);
-			if (jobCategory!=null && !"".equals(jobCategory))
-				jt.setJobCategory(jobCategory);
-			jobID = session.runJob(jt);
-			
-			//System.out.println("Your job has been submitted with id " + id);
-			session.deleteJobTemplate(jt);
-		}
-		catch (DrmaaException e)
-		{
-			Log.log.log(Level.SEVERE, "Exception raised", e);
-			/* ignore */
-        	return null;
-		}
-		runningJobsIDs.add(jobID);
-		return jobID;
-	}
-
-	/**
-	 * Returns the JobInfo object corresponding to the requested job, or null if the job is not terminated yet.
-	 * @param jobID
-	 *            id of a job, as returned by {@link #runJob(String, String)}
-	 * @return the JobInfo object, or null if the job isn't terminated yet
-	 */
-	protected static synchronized JobInfo getJobInfo(String jobID)
-	{
-		if (session==null)
-			throw new IllegalStateException("session is null: this should not happen!?!");
-		JobInfo info = null;
-		try
-        {
-	        info = session.wait(jobID, Session.TIMEOUT_NO_WAIT);
-        }
-        catch (ExitTimeoutException e)
-        {
-        	/* not yet */
-        }
-        catch (DrmaaException e)
-        {
-        	Log.log.log(Level.SEVERE, "session.wait() failed", e);
-        }
-        /* Implementation note:
-         * we /may/ session.exit() if runningJobs.isEmpty()... but we don't really know if a JobInfo object can
-         * safely be accessed once the session has exited (JobInfo.methods throw DrmaaException). Since we couldn't
-         * find this information, we choose to wait until the execution has examined the JobInfo before forgetJob()
-         * is called and as a result, before the session is ended.
-         */
-        return info;
-	}
-
-	/**
-	 * Callers should call this method when they are finished with the handling of a job, i.e. when the job is finished
-	 * <b>AND</b> after they do not need anymore the JobInfo object returned by {@link #getJobInfo(String)}.
-	 * If there is no more jobs waiting, the method takes care of ending the session.
-	 * @param jobID the finished job's id, as returned by {@link #runJob(String, String)}.
-	 */
-	protected static synchronized void forgetJob(String jobID)
-	{
-		runningJobsIDs.remove(jobID);
-		if ( runningJobsIDs.isEmpty() )
-		{
-			try
-            {
-				Log.log.fine("No more jobs running, closing our org.ggf.drmaa.Session");
-                session.exit();
-            }
-            catch (DrmaaException e)
-            {
-            	// 1. NoActiveSessionException won't happen,
-                // 2. don't really know what to do on DrmsExitException...
-            	Log.log.log(Level.SEVERE, "session.exit() failed", e);
-            }
-			session=null;
-		}
-	}
-	
-	/* ---- SGEExecutionEngine instance method ---- */
-	/**
-	 * 
-	 * @param configuration
-	 * @param workflowID
-	 * @param executionID
-	 */
-	protected SGEExecutionEngine(ExecutionEngineConfiguration configuration, WorkflowID workflowID, ExecutionID executionID)
-	{
-		super(configuration, workflowID, executionID);
-	}
-	
-	@Override
-	public SGEExecutionEngineConfiguration configuration()
-	{
-		return (SGEExecutionEngineConfiguration) configuration;
-	}
-	
-	@Override
-	public Result execute(File executionDirectory, File zippedInputFiles) throws InterruptedException,
-	        InterruptedIOException
-	{
-		// this is only used to identify the current exec. within log messages
-		String log_exec_id = executionDirectory.toString();
-		
-		// uncompress the script and the related files into the appropriate dir
-		if (zippedInputFiles!=null) // do not remove it, we need to restore it before every execution
-			Utile.unzip(zippedInputFiles, executionDirectory, false, false);
-		
-		List<String> sentFiles = new ArrayList<String>(Arrays.asList(executionDirectory.list()));
-		// sentFiles.remove("script.txt");
-		
-		// time to execute the script
-		// TODO: treat IOException + InterruptedException individually AND return a useful status to the server!!
-		Log.log.info(()->"Executing workflow: " + log_exec_id);
-		
-		for (String prgID: scripts_for_prg.keySet())
-		{
-			result.setExecStatus(prgID, ProgramResult.ProgramStatus.RUNNING);
-			getProgressMonitor().setRunningExecutionProgress(result);
-
-			Result prgResult = new Result(result.workflowID(), null);
-			
-			/* Write the script */
-			// dos2unix = Runtime.getRuntime().exec("dos2unix "+scriptFile);
-			File scriptFile = new File(executionDirectory, "script.txt");
-			try
-			{
-				FileOutputStream out = new FileOutputStream(scriptFile);
-				out.write(scriptForID(prgID).getBytes());
-				out.write("\n".getBytes()); // tcsh peculiarity: it does not execute the last line, if not terminated by EOL
-				out.flush();
-				out.close();
-			}
-			catch (IOException ioe)
-			{
-				ioe.printStackTrace();
-			}
-			
-			/* Move input files */
-			File[] createdFiles = prepareInputFiles(prgID, executionDirectory, prgResult);
-			// TODO check prg_result.status
-
-			/* Submit the new task to SGE */
-			prgResult.setResultForPrg(prgID, "start", ""+new java.util.Date());
-			StringBuilder msgs = new StringBuilder();
-			int exitStatus = sge_execute(scriptFile.getAbsolutePath(), executionDirectory.getAbsolutePath(), msgs);
-			prgResult.setResultForPrg(prgID, "end", ""+new java.util.Date());
-
-			Log.log.fine(()->"SGE process finished. Exit value for prg_id: "+prgID+": " + exitStatus);
-			
-			/* Put into result the relevant informations */
-
-			// IllegalStateException will be raised by stdout()/stderr() if the process couldn't be executed
-			//prg_result.addResultForPrg(prg_id, "stdout", logs.toString());
-			//prg_result.addResultForPrg(prg_id, "stderr", errs.toString());
-			prgResult.setResultForPrg(prgID, "script", scriptForID(prgID));
-			prgResult.setResultForPrg(prgID, "exit_value", ""+exitStatus);
-			prgResult.setResultForPrg(prgID, "files_not_found", "");
-			if (msgs.length()!=0)
-				prgResult.setResultForPrg(prgID, "sge_messages", msgs.toString());
-
-			// get back stdout and stderr
-			String str = Utile.getContentOfTextFile(new File(executionDirectory, stdout_filename));
-			if ( str != null )
-				prgResult.addInfoForPrg(prgID, "stdout", str);
-			str = Utile.getContentOfTextFile(new File(executionDirectory, stderr_filename));
-			if ( str != null )
-				prgResult.addInfoForPrg(prgID, "stderr", str);
-
-			if (exitStatus == 0)
-			{
-				prgResult.setStatus(Result.Status.OK);
-				prgResult.setExecStatus(prgID, ProgramResult.ProgramStatus.OK);
-			}
-			else
-			{
-				prgResult.setStatus(Result.Status.ERROR);
-				prgResult.setExecStatus(prgID, ProgramResult.ProgramStatus.ERROR);
-				prgResult.setResultForPrg(prgID, "failure_reason",
-				                           "Voir les sorties stderr/stdout pour plus de détails sur l'échec\n"+
-				                           "Refer to sections stderr & stdout for further details on the failure"); // i18n devrait être fait côté client, cf. note similaire dans Resource
-			}
-			
-			/* time to move the files */
-			renameOutputFiles(prgID, executionDirectory, prgResult);
-			for (File createdFile: createdFiles)
-				Utile.deleteRecursively(createdFile); // may be a directory
-
-			/* move all EXTRA files into the prgID/ directory (i.e. that were not identified as output files) */
-			// TODO quick fix here, renameOutputFiles() should probably take care of this, e.g. being cleanup/handleOutputFiles() instead
-			// TODO voir aussi ShellExecEngine... et mettre tout ça ensemble plutôt que de répliquer le execute()...
-			for (File f: executionDirectory.listFiles())
-				if (f.isFile() && ( ! "script.txt".equals(f.getName()) ) && (!sentFiles.contains(f.getName())))
-					Utile.renameFile(f, new File(new File(executionDirectory, prgID), f.getName())); // TODO set WARNING state if it returns false?
-			result.mergeWith(prgResult);
-			getProgressMonitor().setRunningExecutionProgress(result);
-			prgResult.dumpContent(executionDirectory);
-
-			if (zippedInputFiles!=null) // Restore the input files before the next program begins
-				Utile.unzip(zippedInputFiles, executionDirectory, false, false);
-		}
-		
-		
-		
-		Log.log.info(()->"End of execution: " + log_exec_id + "status: " + result.getStatus());
-		if (zippedInputFiles!=null)
-			zippedInputFiles.delete();
-		// TODO if false, this means that the execution has been stopped
-		// if(platform.executeWorkflow(executionID))
-		{
-			File zippedResults = new File(executionDirectory, "resultat.zip");
-			Log.log.info(()->"Sending results to the server: " + zippedResults);
-			
-			// add to 'sentFiles' any file you do NOT want to send back
-			sentFiles.add("script.txt");
-			sentFiles.add(stdout_filename);
-			sentFiles.add(stderr_filename);
-			Utile.zipDirectory(executionDirectory, zippedResults, sentFiles);
-			result.setZipFile(zippedResults);
-			return result;
-		}
-		// else return null;
-	}
-	
-	protected int sge_execute(String scriptPath, String executionDir, StringBuilder messages)
-	{
-		String jobID = null;
-		try {
-			new File(scriptPath).setExecutable(true);
-			
-			jobID = runJob(scriptPath, executionDir, configuration().getJobCategory());
-			if ( jobID==null )
-				return Integer.MIN_VALUE;
-
-			Log.log.log(Level.FINE, "Job has been submitted with id %s", jobID);
-			
-			JobInfo info = getJobInfo(jobID);
-			while (info == null)
-			{
-				try { Thread.sleep(100); }
-                catch (InterruptedException e) { /* ignore */ }
-                info = getJobInfo(jobID);
-			}
-			
-			if (info.wasAborted()) {
-				messages.append("Job ").append(info.getJobId()).append(" never ran");
-				Log.log.fine(messages.toString());
-				return Integer.MIN_VALUE;
-			} else if (info.hasExited()) {
-				messages.append("Job ").append(info.getJobId()).append(" finished regularly with exit status ").append(info.getExitStatus());
-				Log.log.fine(messages.toString());
-				return info.getExitStatus();
-			} else if (info.hasSignaled()) {
-				messages.append("Job ").append(info.getJobId()).append(" finished due to signal ").append(info.getTerminatingSignal());
-				Log.log.fine(messages.toString());
-				return Integer.MIN_VALUE;
-			} else {
-				messages.append("Job ").append(info.getJobId()).append(" finished with unclear conditions");
-				Log.log.fine(messages.toString());
-				return Integer.MIN_VALUE;
-			}
-			
-			/*
-			 * from http://gridengine.sunsource.net/howto/drmaa_java.html
-			 */
-			/*
-	         System.out.println("Job Usage:");
-
-	         Map<String, String> rmap = info.getResourceUsage();
-	         Iterator<String> i = rmap.keySet().iterator();
-
-	         while (i.hasNext()) {
-	            String name = i.next();
-	            String value = rmap.get(name);
-
-	            System.out.println("  " + name + "=" + value);
-	         }
-			 */
-		} catch (DrmaaException e) {
-			messages.append("Unhandled error: ").append(e.getMessage());
-			Log.log.log(Level.SEVERE, "Unhandled error", e.getMessage());
-			return Integer.MIN_VALUE;
-		}
-		finally
-		{
-			if (jobID!=null)
-				forgetJob(jobID);
-		}
-	}	
-	
-	/**
-	 * Waits until the current process is cancelled
-	 */
-	@Override
-	public void cancel()
-	{
-		Utile.unimplemented();
-	}
-
-	/**
-	 * Returns an execution directory located in the user's home directory: a script run by the Sun Grid Engine should
-	 * be accessible from every node in the grid (we suppose that the user's home directory has this property)
-	 * @return the execution directory 
-	 */
-	@Override
-	public File getExecutionDirectory()
-	{
-		return Utile.createTempDirectory("tmp.exec", ".bsx", new File(Environment.getUserHome()));
-	}
-
-}
diff --git a/src/eu/telecom_bretagne/praxis/server/execution/platform/WorkflowExecutionEngine.java b/src/eu/telecom_bretagne/praxis/server/execution/platform/WorkflowExecutionEngine.java
index 333b8cdb23a67501d0191bf8f7781c0266495a2b..81f07d0d3b88f0736e4caf2960ec7f59d2900347 100644
--- a/src/eu/telecom_bretagne/praxis/server/execution/platform/WorkflowExecutionEngine.java
+++ b/src/eu/telecom_bretagne/praxis/server/execution/platform/WorkflowExecutionEngine.java
@@ -34,8 +34,7 @@ public class WorkflowExecutionEngine extends ExecutionEngine
 
 		/**
 		 * 
-		 * @param platform
-		 * @param xml the xml description of the platform.
+		 * @param platform the description of the platform
 		 */
 		public WorkflowExecutionEngineConfiguration(PlatformDescription platform)
 		{