Index: /pom.xml
===================================================================
--- /pom.xml	(revision 15)
+++ /pom.xml	(revision 16)
@@ -6,5 +6,5 @@
 	<artifactId>partiesserver</artifactId>
 	<packaging>war</packaging>
-	<version>1.4.1</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.4.2</version> <!-- must equal ${geniusweb.version} -->
 	<name>partiesserver Maven Webapp</name>
 	<url>http://maven.apache.org</url>
Index: /src/main/java/geniusweb/partiesserver/RunningPartiesUpdater.java
===================================================================
--- /src/main/java/geniusweb/partiesserver/RunningPartiesUpdater.java	(revision 15)
+++ /src/main/java/geniusweb/partiesserver/RunningPartiesUpdater.java	(revision 16)
@@ -12,7 +12,8 @@
  * 
  * This object keeps the {@link RunningPartiesRepo} up to date. Main job is to
- * remove parties that have been timed out. When a party times out,
- * it calls {@link Party#terminate()} but that might fail to stop a party. 
- * In the end, the protocol should close the connection after the deadline. Start and run only once.
+ * remove parties that have been timed out. When a party times out, it calls
+ * {@link Party#terminate()} but that might fail to stop a party. In the end,
+ * the protocol should close the connection after the deadline. Start and run
+ * only once.
  */
 public class RunningPartiesUpdater implements Runnable {
@@ -23,6 +24,6 @@
 	 * when a negotiation runs out of time, the protocol may want to inform all
 	 * parties about the failed session. If the parties are all killed at that
-	 * moment, this is impossible. Therefore we wait KILL_DELAY extra time before
-	 * really killing the parties.
+	 * moment, this is impossible. Therefore we wait KILL_DELAY extra time
+	 * before really killing the parties.
 	 */
 	private static final long KILL_DELAY = 2000;
@@ -31,7 +32,7 @@
 	 * @param repo   the {@link RunningPartiesRepo} that must be updated
 	 *               automatically
-	 * @param period the period with which to check time-out (ms). Recommended 1000.
-	 *               After constructing you can run this in separate thread or just
-	 *               call {@link #run()}
+	 * @param period the period with which to check time-out (ms). Recommended
+	 *               1000. After constructing you can run this in separate
+	 *               thread or just call {@link #run()}
 	 * 
 	 */
@@ -53,12 +54,14 @@
 			}
 		} catch (InterruptedException e) {
-			System.err.println("ERROR:  RunningPartiesUpdater was interrupted!");
+			System.err
+					.println("ERROR:  RunningPartiesUpdater was interrupted!");
 		}
 	}
 
 	/**
-	 * Called when some file that may be relevant has changed. Synchronized to avoid
-	 * weird states if filesystem changes rapidly. We ignore everything but jar
-	 * files.
+	 * Removes parties when they ran out of time. Called when some file that may
+	 * be relevant has changed, and periodically every {@link #period}.
+	 * Synchronized to avoid weird states if filesystem changes rapidly. We
+	 * ignore everything but jar files.
 	 */
 	void update() {
@@ -66,6 +69,13 @@
 			long now = System.currentTimeMillis();
 			if (now >= party.getEndDate().getTime() + KILL_DELAY) {
+				log.log(Level.WARNING,
+						"party " + party.getID() + " still running "
+								+ KILL_DELAY / 1000
+								+ "s after deadline. Removed now.");
 				running.remove(party.getID());
-				log.log(Level.WARNING, "party " + party.getID() + " still running 2s after deadline. Removed now.");
+				try {
+					Thread.sleep(20);
+				} catch (InterruptedException e) {
+				}
 			}
 		}
Index: /src/main/java/geniusweb/partiesserver/websocket/ActiveThreads.java
===================================================================
--- /src/main/java/geniusweb/partiesserver/websocket/ActiveThreads.java	(revision 16)
+++ /src/main/java/geniusweb/partiesserver/websocket/ActiveThreads.java	(revision 16)
@@ -0,0 +1,67 @@
+package geniusweb.partiesserver.websocket;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * Keeps track of threads active for a PartySocket. Needed so that we can kill
+ * all relevant threads when it comes to forced termination.
+ */
+public class ActiveThreads {
+	private final List<Thread> threads = new LinkedList<>();
+	private final Reporter log;
+
+	public ActiveThreads(Reporter log) {
+		this.log = log;
+	}
+
+	/**
+	 * Add the current thread as active thread
+	 */
+	public synchronized void add() {
+		threads.add(Thread.currentThread());
+	}
+
+	/**
+	 * Remove the current thread as active thread. To be called when thread
+	 * finished nicely.
+	 */
+	public synchronized void remove() {
+		threads.remove(Thread.currentThread());
+
+	}
+
+	/**
+	 * 
+	 * @return true if no threads currently running.
+	 */
+	public synchronized boolean isEmpty() {
+		return threads.isEmpty();
+	}
+
+	@SuppressWarnings("deprecation")
+	public synchronized void killall() {
+
+		/*
+		 * The method stop(Throwable) does not work anymore. we need to call
+		 * stop() which will result in nonsense stacktrace pointing to US
+		 * instead of the real problem. Only solution is to print stacktrace
+		 * ourselves.
+		 */
+		for (Thread thread : threads) {
+			String stacktrace = "";
+			for (StackTraceElement element : thread.getStackTrace()) {
+				stacktrace += "\t" + element.toString() + "\n";
+			}
+
+			log.log(Level.INFO,
+					"Killing thread with stack trace\n" + stacktrace);
+			/* the most powerful kill, deprecated but required here. */
+			thread.stop();
+		}
+		threads.clear();
+	}
+}
Index: /src/main/java/geniusweb/partiesserver/websocket/PartySocket.java
===================================================================
--- /src/main/java/geniusweb/partiesserver/websocket/PartySocket.java	(revision 15)
+++ /src/main/java/geniusweb/partiesserver/websocket/PartySocket.java	(revision 16)
@@ -44,8 +44,10 @@
 public class PartySocket implements ConnectionEnd<Inform, Action> {
 	private final static ObjectMapper jackson = new ObjectMapper();
+	private static final int MAX_MESSAGE_SIZE = 20 * 1024 * 1024;
+
 	private final Reporter log;
 	private final RunningPartiesRepo runningparties;
 	private final List<Listener<Inform>> listeners = new CopyOnWriteArrayList<Listener<Inform>>();
-	private static final int MAX_MESSAGE_SIZE = 20 * 1024 * 1024;
+	private final ActiveThreads threads;
 
 	// should all be final, except that we can only set them when start is
@@ -66,4 +68,5 @@
 		this.runningparties = parties;
 		this.log = reporter;
+		this.threads = new ActiveThreads(log);
 	}
 
@@ -128,4 +131,8 @@
 			throw new IllegalStateException("Unexpected change of session ID");
 		}
+		if (!threads.isEmpty()) {
+			log.log(Level.WARNING, "Party " + partyID
+					+ " is still busy with previous message");
+		}
 		Inform info = jackson.readValue(informmessage, Inform.class);
 		RunningParty party = runningparties.get(partyID);
@@ -135,14 +142,27 @@
 			log.log(Level.FINE, "Inform " + partyID + ": " + info);
 			try {
+				threads.add();
 				party.inform(info);
+				threads.remove();
 			} catch (Throwable e) {
-				log.log(Level.WARNING, "Party failed on inform.", e);
-				e.printStackTrace();
-				// severe as someone wants to debug that. Not severe for us
-				// we don't use jackson.writeValueAsString(e) here because
-				// CloseReason has 123 char limit.
-				// The error will be too large to fit and will be truncated
-				// therefore we send a plain string and hope that enough
-				// will arrive at the other side (the protocol)
+				threads.remove();
+				if (e instanceof ThreadDeath)
+					/*
+					 * ThreadDeath always prints stacktrace anwyay, stacktrace
+					 * is useless because it contains the thrower's stacktrace
+					 * instead of a useful message.
+					 */
+					log.log(Level.WARNING,
+							"Party was killed while handling inform.");
+				else
+					log.log(Level.WARNING, "Party failed on inform.", e);
+				/*
+				 * severe as someone wants to debug that. Not severe for us we
+				 * don't use jackson.writeValueAsString(e) here because
+				 * CloseReason has 123 char limit. The error will be too large
+				 * to fit and will be truncated therefore we send a plain string
+				 * and hope that enough will arrive at the other side (the
+				 * protocol)
+				 */
 				session.close(new CloseReason(
 						CloseReason.CloseCodes.CLOSED_ABNORMALLY,
@@ -186,8 +206,9 @@
 	public void onClose() throws IOException {
 		log.log(Level.INFO, "socket closed to " + partyID);
-		/*
-		 * for now just kill the party. Maybe we can do something with reconnect
-		 * but that will be complex.
-		 */
+		if (!threads.isEmpty()) {
+			log.log(Level.WARNING, "Party " + partyID
+					+ " failed to terminate. Trying to kill the remaining threads.");
+			threads.killall();
+		}
 		runningparties.remove(partyID);
 		outstream.stop();
Index: /src/test/java/geniusweb/partiesserver/JavaClientTest.java
===================================================================
--- /src/test/java/geniusweb/partiesserver/JavaClientTest.java	(revision 15)
+++ /src/test/java/geniusweb/partiesserver/JavaClientTest.java	(revision 16)
@@ -39,5 +39,5 @@
  */
 public class JavaClientTest {
-	private static final String RANDOMPARTY = "http://localhost:8080/partiesserver/run/randomparty-1.4.1";
+	private static final String RANDOMPARTY = "http://localhost:8080/partiesserver/run/randomparty-1.4.2";
 	// private static final String JSON = "{\"jobs\":[\"jobs1\"]}";
 //	private static final String JOBS1PROFILE = "{\"LinearAdditiveUtilitySpace\":{\"issueUtilities\":{\"lease car\":{\"discreteutils\":{\"valueUtilities\":{\"no\":0,\"yes\":1}}},\"permanent contract\":{\"discreteutils\":{\"valueUtilities\":{\"no\":0,\"yes\":1}}},\"career development opportunities\":{\"discreteutils\":{\"valueUtilities\":{\"high\":1,\"low\":0,\"medium\":0.5}}},\"fte\":{\"discreteutils\":{\"valueUtilities\":{\"1.0\":0.75,\"0.6\":0.25,\"0.8\":0.5}}},\"salary\":{\"discreteutils\":{\"valueUtilities\":{\"4000\":1.0,\"2500\":0.25,\"3500\":0.75,\"2000\":0,\"3000\":0.3}}},\"work from home\":{\"discreteutils\":{\"valueUtilities\":{\"1\":0.5,\"2\":0.666666666666,\"0\":0.333333333}}}},\"issueWeights\":{\"lease car\":0.06,\"permanent contract\":0.16,\"career development opportunities\":0.04,\"fte\":0.32,\"salary\":0.24,\"work from home\":0.18},\"domain\":{\"name\":\"jobs\",\"issuesValues\":{\"lease car\":{\"values\":[\"yes\",\"no\"]},\"permanent contract\":{\"values\":[\"yes\",\"no\"]},\"career development opportunities\":{\"values\":[\"low\",\"medium\",\"high\"]},\"fte\":{\"values\":[\"0.6\",\"0.8\",\"1.0\"]},\"salary\":{\"values\":[\"2000\",\"2500\",\"3000\",\"3500\",\"4000\"]},\"work from home\":{\"values\":[\"0\",\"1\",\"2\"]}}},\"name\":\"jobs1\"}}";
