Index: src/main/java/geniusweb/partiesserver/RunParty.java
===================================================================
--- src/main/java/geniusweb/partiesserver/RunParty.java	(revision 5)
+++ src/main/java/geniusweb/partiesserver/RunParty.java	(revision 8)
@@ -60,5 +60,5 @@
 		String name = request.getPathInfo().substring(1);
 		log.log(Level.INFO, "request to run " + name);
-
+		System.setSecurityManager(new SimpleSecurityManager());
 		AvailableParty party = AvailablePartiesRepo.instance().get(name);
 		if (party == null) {
Index: src/main/java/geniusweb/partiesserver/SimpleSecurityManager.java
===================================================================
--- src/main/java/geniusweb/partiesserver/SimpleSecurityManager.java	(revision 8)
+++ src/main/java/geniusweb/partiesserver/SimpleSecurityManager.java	(revision 8)
@@ -0,0 +1,13 @@
+package geniusweb.partiesserver;
+
+import java.security.Permission;
+
+public class SimpleSecurityManager extends SecurityManager {
+	@Override
+	public void checkPermission(Permission permission) {
+		// System.out.println("Checking permission " + permission);
+		if (permission.getName().contains("exitVM")) {
+			throw new IllegalStateException("System.exit call not allowed");
+		}
+	}
+}
Index: src/main/java/geniusweb/partiesserver/repository/AvailablePartiesRepo.java
===================================================================
--- src/main/java/geniusweb/partiesserver/repository/AvailablePartiesRepo.java	(revision 5)
+++ src/main/java/geniusweb/partiesserver/repository/AvailablePartiesRepo.java	(revision 8)
@@ -12,5 +12,6 @@
  * Stores all currently available parties that can be run.
  */
-public class AvailablePartiesRepo extends DefaultListenable<String> implements Repository<String, AvailableParty> {
+public class AvailablePartiesRepo extends DefaultListenable<String>
+		implements Repository<String, AvailableParty> {
 	private static final AvailablePartiesRepo instance = new AvailablePartiesRepo();
 
@@ -37,5 +38,5 @@
 	public void put(AvailableParty entity) {
 		availableParties.put(entity.getID(), entity);
-		notifyChange(entity.getID());
+		notifyListeners(entity.getID());
 	}
 
@@ -43,5 +44,5 @@
 	public void remove(String id) {
 		availableParties.remove(id);
-		notifyChange(id);
+		notifyListeners(id);
 	}
 
Index: src/main/java/geniusweb/partiesserver/repository/RunningPartiesRepo.java
===================================================================
--- src/main/java/geniusweb/partiesserver/repository/RunningPartiesRepo.java	(revision 5)
+++ src/main/java/geniusweb/partiesserver/repository/RunningPartiesRepo.java	(revision 8)
@@ -74,5 +74,5 @@
 			runningParties.put(id, newParty);
 		}
-		notifyChange(id);
+		notifyListeners(id);
 	}
 
@@ -87,5 +87,5 @@
 			runningParties.put(id, newParty);
 		}
-		notifyChange(id);
+		notifyListeners(id);
 
 	}
@@ -125,5 +125,5 @@
 			}
 		}
-		notifyChange(id);
+		notifyListeners(id);
 	}
 
Index: src/main/java/geniusweb/partiesserver/repository/RunningParty.java
===================================================================
--- src/main/java/geniusweb/partiesserver/repository/RunningParty.java	(revision 5)
+++ src/main/java/geniusweb/partiesserver/repository/RunningParty.java	(revision 8)
@@ -9,8 +9,8 @@
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 
-import geniusweb.actions.Action;
 import geniusweb.actions.PartyId;
 import geniusweb.connection.DefaultConnection;
 import geniusweb.partiesserver.RunningPartiesUpdater;
+import geniusweb.partiesserver.websocket.PartySocket;
 import geniusweb.party.Party;
 import geniusweb.party.inform.Inform;
@@ -40,5 +40,5 @@
 	 * set when someone contacts our socket. null if not set.
 	 */
-	private final transient DefaultConnection<Inform, Action> connection;
+	private final transient PartySocket connection;
 
 	/**
@@ -58,5 +58,5 @@
 	 */
 	protected RunningParty(Party party, PartyId id, String name, Date start,
-			Date end, DefaultConnection conn) {
+			Date end, PartySocket conn) {
 		if (party == null || id == null || name == null || start == null
 				|| end == null) {
@@ -101,6 +101,7 @@
 	 * @param name         The name, this should match the filename in the repo
 	 *                     (without the .jar).
-	 * @param maxRunTimeMS the maximum runtime for this party (ms). The {@link RunningPartiesUpdater} 
-	 * will keep an eye on the time and handle removal after time is up.
+	 * @param maxRunTimeMS the maximum runtime for this party (ms). The
+	 *                     {@link RunningPartiesUpdater} will keep an eye on the
+	 *                     time and handle removal after time is up.
 	 * 
 	 */
@@ -212,5 +213,5 @@
 	 */
 	public void inform(Inform info) {
-		connection.notifyChange(info);
+		connection.notifyListeners(info);
 	}
 
@@ -223,6 +224,5 @@
 	 * @throws IllegalStateException if the party is already connected.
 	 */
-	public RunningParty withConnection(
-			DefaultConnection<Inform, Action> newconn) {
+	public RunningParty withConnection(PartySocket newconn) {
 		if (newconn == null)
 			throw new NullPointerException("newconn must be not null");
Index: src/main/java/geniusweb/partiesserver/websocket/PartySocket.java
===================================================================
--- src/main/java/geniusweb/partiesserver/websocket/PartySocket.java	(revision 5)
+++ src/main/java/geniusweb/partiesserver/websocket/PartySocket.java	(revision 8)
@@ -5,4 +5,6 @@
 import java.net.URI;
 import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.logging.Level;
 
@@ -23,9 +25,8 @@
 import geniusweb.actions.Action;
 import geniusweb.actions.PartyId;
-import geniusweb.connection.DefaultConnection;
+import geniusweb.connection.ConnectionEnd;
 import geniusweb.partiesserver.repository.RunningPartiesRepo;
 import geniusweb.partiesserver.repository.RunningParty;
 import geniusweb.party.Party;
-import geniusweb.party.inform.Finished;
 import geniusweb.party.inform.Inform;
 import geniusweb.party.inform.Settings;
@@ -41,8 +42,9 @@
  */
 @ServerEndpoint("/party/{party}")
-public class PartySocket extends DefaultConnection<Inform, Action> {
+public class PartySocket implements ConnectionEnd<Inform, Action> {
 	private final static ObjectMapper jackson = new ObjectMapper();
 	private final Reporter log;
 	private final RunningPartiesRepo runningparties;
+	private final List<Listener<Inform>> listeners = new CopyOnWriteArrayList<Listener<Inform>>();
 
 	// should all be final, except that we can only set them when start is
@@ -86,5 +88,5 @@
 						runningparties.removeListener(this);
 						session.close(new CloseReason(CloseCodes.GOING_AWAY,
-								"party died"));
+								"detected that party vanished"));
 					} catch (IOException e) {
 						log.log(Level.WARNING,
@@ -131,8 +133,30 @@
 				party.inform(info);
 			} catch (Throwable e) {
-				// simple sandbox
-				log.log(Level.WARNING, "Party failed on inform:", e);
+				log.log(Level.WARNING, "Party failed on inform.", e);
+				// 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,
+						"party threw exception: " + collectReasons(e)));
 			}
-		} // else dead but that's handled in the listener above
+		} // else party is dead but that's handled in the listener above
+	}
+
+	/**
+	 * 
+	 * @param e an exception
+	 * @return collect all reasons and sub-reasons
+	 */
+	private String collectReasons(Throwable e) {
+		String reason = "";
+		do {
+			reason = reason + " : " + e.getClass().getSimpleName() + ":"
+					+ e.getMessage();
+			e = e.getCause();
+		} while (e != null);
+		return reason;
 	}
 
@@ -145,8 +169,7 @@
 	 */
 	private void sniff(Inform info, RunningParty party) throws IOException {
-		if (info instanceof Finished) {
-			session.close(new CloseReason(CloseCodes.GOING_AWAY,
-					"session reached agreement"));
-		} else if (info instanceof Settings) {
+		// do NOT sniff Finished messages and close sockets. Parties and their
+		// sockets need to stay running till all messages are handled
+		if (info instanceof Settings) {
 			Date end = ((Settings) info).getProgress().getTerminationTime();
 			runningparties.replace(party.withEndTime(end));
@@ -209,3 +232,29 @@
 	}
 
+	@Override
+	public void addListener(Listener<Inform> l) {
+		listeners.add(l);
+	}
+
+	@Override
+	public void removeListener(Listener<Inform> l) {
+		listeners.remove(l);
+	}
+
+	/**
+	 * This should only be called by the owner of the listenable, not by
+	 * listeners or others. Avoid calling this from synchronized blocks as a
+	 * notified listener might immediately make more calls to you.
+	 * <p>
+	 * Any listeners that throw an exception will be intercepted and their
+	 * stacktrace is printed.
+	 * 
+	 * @param data information about the change.
+	 */
+	public void notifyListeners(Inform data) {
+		for (Listener<Inform> l : listeners) {
+			l.notifyChange(data);
+		}
+	}
+
 }
