Index: /bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java
===================================================================
--- /bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java	(revision 9)
+++ /bidspace/src/main/java/geniusweb/bidspace/BidsWithUtility.java	(revision 10)
@@ -12,5 +12,5 @@
 import geniusweb.issuevalue.Domain;
 import geniusweb.issuevalue.Value;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import tudelft.utilities.immutablelist.AbstractImmutableList;
 import tudelft.utilities.immutablelist.FixedList;
@@ -22,11 +22,10 @@
 /**
  * Tool class containing functions dealing with utilities of all bids in a given
- * {@link LinearAdditiveUtilitySpace}. This class caches previously computed
- * values to accelerate the calls and subsequent calls. Re-use the object to
- * keep/reuse the cache.
+ * {@link LinearAdditive}. This class caches previously computed values to
+ * accelerate the calls and subsequent calls. Re-use the object to keep/reuse
+ * the cache.
  * <h1>Rounding</h1> Internally, utilities of bids are rounded to the given
  * precision. This may cause inclusion/exclusion of some bids in the results.
- * See {@link #BidsWithUtility(LinearAdditiveUtilitySpace, int)} for more
- * details
+ * See {@link #BidsWithUtility(LinearAdditive, int)} for more details
  */
 public class BidsWithUtility {
@@ -44,10 +43,10 @@
 	 * Default constructor, uses default precision 6. This value seems practical
 	 * for the common range of issues, utilities and weights. See
-	 * {@link #BidsWithUtil(LinearAdditiveUtilitySpace, int)} for more details
-	 * on the precision.
-	 * 
-	 * @param space the {@link LinearAdditiveUtilitySpace} to analyze
-	 */
-	public BidsWithUtility(LinearAdditiveUtilitySpace space) {
+	 * {@link #BidsWithUtil(LinearAdditive, int)} for more details on the
+	 * precision.
+	 * 
+	 * @param space the {@link LinearAdditive} to analyze
+	 */
+	public BidsWithUtility(LinearAdditive space) {
 		this(space, 6);
 	}
@@ -55,5 +54,5 @@
 	/**
 	 * 
-	 * @param space     the {@link LinearAdditiveUtilitySpace} to analyze
+	 * @param space     the {@link LinearAdditive} to analyze
 	 * @param precision the number of digits to use for computations. In
 	 *                  practice, 6 seems a good default value.
@@ -77,5 +76,5 @@
 	 * 
 	 */
-	public BidsWithUtility(LinearAdditiveUtilitySpace space, int precision) {
+	public BidsWithUtility(LinearAdditive space, int precision) {
 		this(getInfo(space, precision), precision);
 	}
@@ -119,4 +118,18 @@
 	public List<IssueInfo> getInfo() {
 		return Collections.unmodifiableList(issueInfo);
+	}
+
+	/**
+	 * 
+	 * @param isMax the extreme bid required
+	 * @return the extreme bid, either the minimum if isMax=false or maximum if
+	 *         isMax=true
+	 */
+	public Bid getExtremeBid(boolean isMax) {
+		Map<String, Value> map = new HashMap<>();
+		for (IssueInfo info : issueInfo) {
+			map.put(info.getName(), info.getExtreme(isMax));
+		}
+		return new Bid(map);
 	}
 
@@ -154,5 +167,5 @@
 	}
 
-	private static List<IssueInfo> getInfo(LinearAdditiveUtilitySpace space2,
+	private static List<IssueInfo> getInfo(LinearAdditive space2,
 			int precision) {
 		Domain dom = space2.getDomain();
Index: /bidspace/src/main/java/geniusweb/bidspace/Interval.java
===================================================================
--- /bidspace/src/main/java/geniusweb/bidspace/Interval.java	(revision 9)
+++ /bidspace/src/main/java/geniusweb/bidspace/Interval.java	(revision 10)
@@ -137,10 +137,13 @@
 			if (other.max != null)
 				return false;
-		} else if (!max.equals(other.max))
+
+		} else if (max.compareTo(other.max) != 0)
+			// THIS WAS FIXED MANUALLY TO USE COMPARETO
 			return false;
 		if (min == null) {
 			if (other.min != null)
 				return false;
-		} else if (!min.equals(other.min))
+		} else if (min.compareTo(other.min) != 0)
+			// THIS WAS FIXED MANUALLY TO USE COMPARETO
 			return false;
 		return true;
Index: /bidspace/src/main/java/geniusweb/bidspace/IssueInfo.java
===================================================================
--- /bidspace/src/main/java/geniusweb/bidspace/IssueInfo.java	(revision 9)
+++ /bidspace/src/main/java/geniusweb/bidspace/IssueInfo.java	(revision 10)
@@ -49,4 +49,36 @@
 	public Interval getInterval() {
 		return interval;
+	}
+
+	/**
+	 * 
+	 * @param isMax
+	 * @return the extreme value, either the minimum if isMax=false or maximum
+	 *         if isMax=true
+	 */
+	public Value getExtreme(boolean isMax) {
+		BigDecimal extremeutil = null;
+		Value extremeval = null;
+		for (Value val : values) {
+			BigDecimal util = weightedUtils.get(val);
+			if (extremeval == null) {
+				extremeutil = weightedUtils.get(val);
+				extremeval = val;
+			} else {
+				if (isMax) {
+					if (util.compareTo(extremeutil) > 0) {
+						extremeutil = util;
+						extremeval = val;
+					}
+				} else {
+					if (util.compareTo(extremeutil) < 0) {
+						extremeutil = util;
+						extremeval = val;
+					}
+
+				}
+			}
+		}
+		return extremeval;
 	}
 
Index: /bidspace/src/main/java/geniusweb/bidspace/pareto/ParetoLinearAdditive.java
===================================================================
--- /bidspace/src/main/java/geniusweb/bidspace/pareto/ParetoLinearAdditive.java	(revision 9)
+++ /bidspace/src/main/java/geniusweb/bidspace/pareto/ParetoLinearAdditive.java	(revision 10)
@@ -10,13 +10,13 @@
 import geniusweb.issuevalue.Domain;
 import geniusweb.profile.Profile;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 
 /**
- * pareto frontier implementation for {@link LinearAdditiveUtilitySpace}.
- * Simplistic implementation that iterates over all bids.
+ * pareto frontier implementation for {@link LinearAdditive}. Simplistic
+ * implementation that iterates over all bids.
  */
 public class ParetoLinearAdditive implements ParetoFrontier {
 
-	private final List<LinearAdditiveUtilitySpace> utilSpaces;
+	private final List<LinearAdditive> utilSpaces;
 	private Set<Bid> points = null;
 	// all issues in a deterministic order
@@ -24,9 +24,8 @@
 	/**
 	 * 
-	 * @param utilSpaces. Must contain at least two
-	 *                    {@link LinearAdditiveUtilitySpace}s and all must be
-	 *                    defined on the same donain.
+	 * @param utilSpaces. Must contain at least two {@link LinearAdditive}s and
+	 *                    all must be defined on the same donain.
 	 */
-	public ParetoLinearAdditive(List<LinearAdditiveUtilitySpace> utilSpaces) {
+	public ParetoLinearAdditive(List<LinearAdditive> utilSpaces) {
 		if (utilSpaces == null || utilSpaces.size() < 2) {
 			throw new IllegalArgumentException(
@@ -34,5 +33,5 @@
 		}
 		Domain domain = utilSpaces.get(0).getDomain();
-		for (LinearAdditiveUtilitySpace space : utilSpaces) {
+		for (LinearAdditive space : utilSpaces) {
 			if (!space.getDomain().equals(domain)) {
 				throw new IllegalArgumentException(
Index: /bidspace/src/main/java/geniusweb/bidspace/pareto/ParetoPoint.java
===================================================================
--- /bidspace/src/main/java/geniusweb/bidspace/pareto/ParetoPoint.java	(revision 9)
+++ /bidspace/src/main/java/geniusweb/bidspace/pareto/ParetoPoint.java	(revision 10)
@@ -10,5 +10,5 @@
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.Value;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 
 /**
@@ -26,10 +26,10 @@
 	 * 
 	 * @param bid    a (possibly partial) {@link Bid}
-	 * @param spaces the {@link LinearAdditiveUtilitySpace}s to consider
+	 * @param spaces the {@link LinearAdditive}s to consider
 	 */
-	public ParetoPoint(Bid bid, List<LinearAdditiveUtilitySpace> spaces) {
+	public ParetoPoint(Bid bid, List<LinearAdditive> spaces) {
 		this.bid = bid;
 		utilities = new ArrayList<>();
-		for (LinearAdditiveUtilitySpace space : spaces) {
+		for (LinearAdditive space : spaces) {
 			utilities.add(space.getUtility(bid));
 		}
Index: /bidspace/src/main/java/geniusweb/bidspace/pareto/PartialPareto.java
===================================================================
--- /bidspace/src/main/java/geniusweb/bidspace/pareto/PartialPareto.java	(revision 9)
+++ /bidspace/src/main/java/geniusweb/bidspace/pareto/PartialPareto.java	(revision 10)
@@ -8,5 +8,5 @@
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.Value;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 
 /**
@@ -28,5 +28,5 @@
 	 * implemented recursively (O(log(#issues))).
 	 * 
-	 * @param utilSpaces the {@link LinearAdditiveUtilitySpace}s
+	 * @param utilSpaces the {@link LinearAdditive}s
 	 * @param issues     the issues (subset of all issues in the space) to be
 	 *                   used for this pareto
@@ -34,6 +34,6 @@
 	 *         given issues.
 	 */
-	public static PartialPareto create(
-			List<LinearAdditiveUtilitySpace> utilSpaces, List<String> issues) {
+	public static PartialPareto create(List<LinearAdditive> utilSpaces,
+			List<String> issues) {
 
 		if (issues.size() == 1) {
Index: /bidspace/src/test/java/geniusweb/bidspace/IssueInfoTest.java
===================================================================
--- /bidspace/src/test/java/geniusweb/bidspace/IssueInfoTest.java	(revision 10)
+++ /bidspace/src/test/java/geniusweb/bidspace/IssueInfoTest.java	(revision 10)
@@ -0,0 +1,68 @@
+package geniusweb.bidspace;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.DiscreteValueSet;
+import geniusweb.issuevalue.ValueSet;
+import geniusweb.profile.utilityspace.DiscreteValueSetUtilities;
+import geniusweb.profile.utilityspace.ValueSetUtilities;
+
+public class IssueInfoTest {
+
+	private static final String NAME = "name";
+	private static final DiscreteValue VAL1 = new DiscreteValue("v1"),
+			VAL2 = new DiscreteValue("v2"), VAL3 = new DiscreteValue("v3");
+	private static final BigDecimal N03 = new BigDecimal("0.3");
+	private static final BigDecimal N04 = new BigDecimal("0.4");
+	private static final BigDecimal N05 = new BigDecimal("0.5");
+	private static final BigDecimal N06 = new BigDecimal("0.6");
+	private IssueInfo info;
+
+	@Before
+	public void before() {
+		Set<DiscreteValue> issuevalues = new HashSet<>();
+		issuevalues.add(VAL1);
+		issuevalues.add(VAL2);
+		issuevalues.add(VAL3);
+		ValueSet values = new DiscreteValueSet(issuevalues);
+		Map<DiscreteValue, BigDecimal> valueUtils = new HashMap<>();
+		valueUtils.put(VAL1, N03);
+		valueUtils.put(VAL2, N04);
+		valueUtils.put(VAL3, N05);
+		ValueSetUtilities utils = new DiscreteValueSetUtilities(valueUtils);
+		info = new IssueInfo(NAME, values, utils, N06, 6);
+	}
+
+	@Test
+	public void smokeTest() {
+
+	}
+
+	@Test
+	public void testGetExtremeMin() {
+
+		assertEquals(VAL1, info.getExtreme(false));
+		assertEquals(VAL3, info.getExtreme(true));
+	}
+
+	@Test
+	public void getNameTest() {
+		assertEquals(NAME, info.getName());
+	}
+
+	@Test
+	public void getIntervalTest() {
+		assertEquals(new Interval(N03.multiply(N06), N05.multiply(N06)),
+				info.getInterval());
+	}
+}
Index: /bidspace/src/test/java/geniusweb/bidspace/pareto/ParetoE2Etest.java
===================================================================
--- /bidspace/src/test/java/geniusweb/bidspace/pareto/ParetoE2Etest.java	(revision 9)
+++ /bidspace/src/test/java/geniusweb/bidspace/pareto/ParetoE2Etest.java	(revision 10)
@@ -19,8 +19,6 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import geniusweb.bidspace.pareto.GenericPareto;
-import geniusweb.bidspace.pareto.ParetoFrontier;
-import geniusweb.bidspace.pareto.ParetoLinearAdditive;
 import geniusweb.profile.Profile;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
 
@@ -61,5 +59,5 @@
 				.readValue(file2, Profile.class);
 
-		List<LinearAdditiveUtilitySpace> profiles = new LinkedList<>();
+		List<LinearAdditive> profiles = new LinkedList<>();
 		profiles.add(profile1);
 		profiles.add(profile2);
Index: /bidspace/src/test/java/geniusweb/bidspace/pareto/ParetoPointTest.java
===================================================================
--- /bidspace/src/test/java/geniusweb/bidspace/pareto/ParetoPointTest.java	(revision 9)
+++ /bidspace/src/test/java/geniusweb/bidspace/pareto/ParetoPointTest.java	(revision 10)
@@ -15,8 +15,7 @@
 import org.junit.Test;
 
-import geniusweb.bidspace.pareto.ParetoPoint;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.Value;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 
 public class ParetoPointTest {
@@ -26,7 +25,6 @@
 	private static final BigDecimal D0_4 = BigDecimal.valueOf(0.4);
 	private static final BigDecimal D0_5 = BigDecimal.valueOf(0.5);
-	private final LinearAdditiveUtilitySpace space1 = mock(
-			LinearAdditiveUtilitySpace.class),
-			space2 = mock(LinearAdditiveUtilitySpace.class);
+	private final LinearAdditive space1 = mock(LinearAdditive.class),
+			space2 = mock(LinearAdditive.class);
 	private final Bid bid11 = mock(Bid.class), bid12 = mock(Bid.class),
 			bid21 = mock(Bid.class), bid22 = mock(Bid.class);
Index: /bidspace/src/test/java/geniusweb/bidspace/pareto/PartialParetoTest.java
===================================================================
--- /bidspace/src/test/java/geniusweb/bidspace/pareto/PartialParetoTest.java	(revision 9)
+++ /bidspace/src/test/java/geniusweb/bidspace/pareto/PartialParetoTest.java	(revision 10)
@@ -16,10 +16,9 @@
 import org.junit.Test;
 
-import geniusweb.bidspace.pareto.PartialPareto;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.Domain;
 import geniusweb.issuevalue.Value;
 import geniusweb.issuevalue.ValueSet;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 
 public class PartialParetoTest {
@@ -31,7 +30,6 @@
 	private static final BigDecimal D0_5 = BigDecimal.valueOf(0.5);
 
-	private final LinearAdditiveUtilitySpace space1 = mock(
-			LinearAdditiveUtilitySpace.class),
-			space2 = mock(LinearAdditiveUtilitySpace.class);
+	private final LinearAdditive space1 = mock(LinearAdditive.class),
+			space2 = mock(LinearAdditive.class);
 
 	private Domain domain = mock(Domain.class);
Index: /events/src/main/java/geniusweb/actions/Action.java
===================================================================
--- /events/src/main/java/geniusweb/actions/Action.java	(revision 9)
+++ /events/src/main/java/geniusweb/actions/Action.java	(revision 10)
@@ -12,6 +12,9 @@
  */
 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT)
-@JsonSubTypes({ @JsonSubTypes.Type(value = EndNegotiation.class), @JsonSubTypes.Type(value = Offer.class),
-		@JsonSubTypes.Type(value = Accept.class) })
+@JsonSubTypes({ @JsonSubTypes.Type(value = EndNegotiation.class),
+		@JsonSubTypes.Type(value = Offer.class),
+		@JsonSubTypes.Type(value = Accept.class),
+		@JsonSubTypes.Type(value = Comparison.class),
+		@JsonSubTypes.Type(value = ElicitComparison.class) })
 public interface Action {
 	/**
Index: /events/src/main/java/geniusweb/actions/ActionWithBid.java
===================================================================
--- /events/src/main/java/geniusweb/actions/ActionWithBid.java	(revision 9)
+++ /events/src/main/java/geniusweb/actions/ActionWithBid.java	(revision 10)
@@ -7,5 +7,6 @@
 
 /**
- * An {@link Action} containing a bid.
+ * An {@link Action} containing a bid. Note that the presense of a bid does not
+ * necessarily mean that an offer was placed for this bid.
  * 
  */
Index: /events/src/main/java/geniusweb/actions/Comparison.java
===================================================================
--- /events/src/main/java/geniusweb/actions/Comparison.java	(revision 10)
+++ /events/src/main/java/geniusweb/actions/Comparison.java	(revision 10)
@@ -0,0 +1,94 @@
+package geniusweb.actions;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.issuevalue.Bid;
+
+/**
+ * A statement from a Party that some bids are better and worse than
+ * {@link #getBid()} bid. Typically this is a response to a CompareWithBid
+ * inform
+ *
+ */
+public class Comparison extends ActionWithBid {
+
+	private List<Bid> better;
+	private List<Bid> worse;
+
+	/**
+	 * @param id     the party id that made this comparison
+	 * @param bid    the bid that is compared wiht
+	 * @param better list of bids that are better than bid
+	 * @param worse  list of bids that are worse than bid
+	 */
+	@JsonCreator
+	public Comparison(@JsonProperty("id") PartyId id,
+			@JsonProperty("bid") Bid bid,
+			@JsonProperty("better") List<Bid> better,
+			@JsonProperty("worse") List<Bid> worse) {
+		super(id, bid);
+		if (bid == null || better == null || worse == null)
+			throw new IllegalArgumentException(
+					"bid, better and worse must not be null");
+		this.better = better;
+		this.worse = worse;
+	}
+
+	/**
+	 * @return All bids that are better than the bid. Maybe empty
+	 * 
+	 */
+	public List<Bid> getBetter() {
+		return better;
+	}
+
+	/**
+	 * @return All bids that are worse than the bid. Maybe empty
+	 * 
+	 */
+
+	public List<Bid> getWorse() {
+		return worse;
+	}
+
+	@Override
+	public String toString() {
+		return "Comparison[" + getActor() + "," + getBid() + ",better=" + better
+				+ ",worse=" + worse + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + ((better == null) ? 0 : better.hashCode());
+		result = prime * result + ((worse == null) ? 0 : worse.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Comparison other = (Comparison) obj;
+		if (better == null) {
+			if (other.better != null)
+				return false;
+		} else if (!better.equals(other.better))
+			return false;
+		if (worse == null) {
+			if (other.worse != null)
+				return false;
+		} else if (!worse.equals(other.worse))
+			return false;
+		return true;
+	}
+
+}
Index: /events/src/main/java/geniusweb/actions/ElicitComparison.java
===================================================================
--- /events/src/main/java/geniusweb/actions/ElicitComparison.java	(revision 10)
+++ /events/src/main/java/geniusweb/actions/ElicitComparison.java	(revision 10)
@@ -0,0 +1,80 @@
+package geniusweb.actions;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.issuevalue.Bid;
+
+/**
+ * Request a comparison from the cob party. The party can do a
+ * {@link Comparison} action as a response.
+ */
+public class ElicitComparison extends ActionWithBid {
+	private final List<Bid> options = new LinkedList<>();
+
+	/**
+	 * @param id   the eliciting party.
+	 * @param bid  the bid to compare the options with
+	 * @param opts the available options that may be chosen from. Must not be
+	 *             null and must have at least 2 options.
+	 */
+	@JsonCreator
+	public ElicitComparison(@JsonProperty("actor") PartyId id,
+			@JsonProperty("bid") Bid bid,
+			@JsonProperty("options") List<Bid> opts) {
+		super(id, bid);
+		if (bid == null)
+			throw new IllegalArgumentException(
+					"Bid to compare with must not be null");
+		if (opts == null || opts.size() < 1) {
+			throw new IllegalArgumentException(
+					"opts must not be null and have at least 1 option");
+		}
+		this.options.addAll(opts);
+	}
+
+	/**
+	 * 
+	 * @return the list of {@link Bid}s to compare {@link #getBid()} with
+	 */
+	public List<Bid> getOptions() {
+		return Collections.unmodifiableList(options);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + ((options == null) ? 0 : options.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		ElicitComparison other = (ElicitComparison) obj;
+		if (options == null) {
+			if (other.options != null)
+				return false;
+		} else if (!options.equals(other.options))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "ElicitComparison[" + getActor() + "," + getBid() + "," + options
+				+ "]";
+
+	}
+
+}
Index: /events/src/test/java/actions/ComparisonTest.java
===================================================================
--- /events/src/test/java/actions/ComparisonTest.java	(revision 10)
+++ /events/src/test/java/actions/ComparisonTest.java	(revision 10)
@@ -0,0 +1,100 @@
+package actions;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.Comparison;
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.NumberValue;
+import geniusweb.issuevalue.Value;
+import tudelft.utilities.junit.GeneralTests;
+
+public class ComparisonTest extends GeneralTests<Comparison> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private static final PartyId id = new PartyId("party1");
+	private static final PartyId id2 = new PartyId("party2");
+	private final static Map<String, Value> issuevalues = new HashMap<String, Value>();
+	private final static Map<String, Value> issuevaluesb = new HashMap<String, Value>();
+	private static Bid bid;
+	private static Bid bidb;
+	private final static String ISSUE1 = "issue1";
+	private final static Value VALUE1 = new DiscreteValue("value1");
+	private final static String ISSUE2 = "issue2";
+	private final static Value VALUE2 = new NumberValue("10");
+	// issue 2 is NUMBER and thus serializes with leading '='
+	private final String acceptstring = "{\"Comparison\":{\"bid\":{\"issuevalues\":{\"issue2\":10,\"issue1\":\"value1\"}},\"better\":[],\"worse\":[],\"actor\":\"party1\"}}";
+
+	private static Comparison accept, accept1, accept2, acceptb, acceptc,
+			acceptd;
+
+	@Before
+	public void before() {
+		issuevalues.put(ISSUE1, VALUE1);
+		issuevalues.put(ISSUE2, VALUE2);
+		bid = new Bid(issuevalues);
+		List<Bid> better = Collections.EMPTY_LIST;
+		List<Bid> worse = Collections.EMPTY_LIST;
+		accept = new Comparison(id, bid, better, worse);
+		accept1 = new Comparison(id, bid, better, worse);
+
+		accept2 = new Comparison(id2, bid, better, worse);
+
+		// values swapped, so different issuevalues.
+		issuevaluesb.put(ISSUE1, VALUE2);
+		issuevaluesb.put(ISSUE2, VALUE2);
+		bidb = new Bid(issuevaluesb);
+		acceptb = new Comparison(id, bidb, better, worse);
+
+		List<Bid> better1 = Arrays.asList(bid);
+		List<Bid> worse1 = Arrays.asList(bidb);
+		acceptc = new Comparison(id, bidb, better1, worse);
+		acceptd = new Comparison(id, bidb, better, worse1);
+
+	}
+
+	@Override
+	public List<List<Comparison>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(accept, accept1),
+				Arrays.asList(accept2), Arrays.asList(acceptb),
+				Arrays.asList(acceptc), Arrays.asList(acceptd));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"Comparison.*Bid.*issue2=10.*issue1=\"value1\".*better=\\[\\].*worse=\\[\\].*",
+				"Comparison.*party2.*Bid.*issue2=10.*issue1=\"value1\".*better=\\[\\].*worse=\\[\\].*",
+				"Comparison.*party1.*Bid.*issue2=10.*issue1=10.*better=\\[\\].*worse=\\[\\].*",
+				"Comparison.*party1.*Bid.*issue2=10.*issue1=10.*better=\\[Bid.*issue2=10.*issue1=\"value1\".*\\].*worse=\\[\\].*",
+				"Comparison.*party1.*Bid.*issue2=10.*issue1=10.*better=\\[\\].*worse=\\[Bid.*issue2=10.*issue1=10.*\\].*");
+	}
+
+	@Test
+	public void serializeAcceptTest() throws JsonProcessingException {
+		System.out.println(jackson.writeValueAsString(accept));
+		assertEquals(acceptstring, jackson.writeValueAsString(accept));
+	}
+
+	@Test
+	public void deserializeAcceptTest() throws IOException {
+		Action act = jackson.readValue(acceptstring, Action.class);
+		assertEquals(accept, act);
+	}
+
+}
Index: /events/src/test/java/actions/ElicitComparisonTest.java
===================================================================
--- /events/src/test/java/actions/ElicitComparisonTest.java	(revision 10)
+++ /events/src/test/java/actions/ElicitComparisonTest.java	(revision 10)
@@ -0,0 +1,92 @@
+package actions;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.ElicitComparison;
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.NumberValue;
+import geniusweb.issuevalue.Value;
+import tudelft.utilities.junit.GeneralTests;
+
+public class ElicitComparisonTest extends GeneralTests<ElicitComparison> {
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private static final PartyId id = new PartyId("party1");
+	private static final PartyId id2 = new PartyId("party2");
+	private final static Map<String, Value> issuevalues = new HashMap<String, Value>();
+	private final static Map<String, Value> issuevaluesb = new HashMap<String, Value>();
+	private static Bid bid;
+	private static Bid bidb;
+	private final static String ISSUE1 = "issue1";
+	private final static Value VALUE1 = new DiscreteValue("value1");
+	private final static String ISSUE2 = "issue2";
+	private final static Value VALUE2 = new NumberValue("10");
+	// issue 2 is NUMBER and thus serializes with leading '='
+	private final String acceptstring = "{\"ElicitComparison\":{\"actor\":\"party1\",\"bid\":{\"issuevalues\":{\"issue2\":10,\"issue1\":\"value1\"}},\"options\":[{\"issuevalues\":{\"issue2\":10,\"issue1\":\"value1\"}}]}}";
+
+	private static ElicitComparison accept1, accept1a, accept2, accept3,
+			accept4;
+
+	@Before
+	public void before() {
+		issuevalues.put(ISSUE1, VALUE1);
+		issuevalues.put(ISSUE2, VALUE2);
+
+		issuevaluesb.put(ISSUE1, VALUE2);
+		bid = new Bid(issuevalues);
+		bidb = new Bid(issuevaluesb);
+
+		List<Bid> opts = Arrays.asList(bid);
+		List<Bid> opts2 = Arrays.asList(bidb);
+
+		accept1 = new ElicitComparison(id, bid, opts);
+		accept1a = new ElicitComparison(id, bid, opts);
+		accept2 = new ElicitComparison(id2, bid, opts);
+		accept3 = new ElicitComparison(id, bidb, opts);
+		accept4 = new ElicitComparison(id, bid, opts2);
+	}
+
+	@Override
+	public List<List<ElicitComparison>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(accept1, accept1a),
+				Arrays.asList(accept2), Arrays.asList(accept3),
+				Arrays.asList(accept4));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"ElicitComparison.*party1.*Bid.*10.*value1.*\\[.*Bid.*10.*value1.*\\].*",
+				"ElicitComparison.*party2.*Bid.*10.*value1.*\\[.*Bid.*10.*value1.*\\].*",
+				"ElicitComparison.*party1.*Bid.*10.*\\[.*Bid.*10.*value1.*\\].*",
+				"ElicitComparison.*party1.*Bid.*10.*value1.*\\[.*Bid.*10.*\\].*");
+	}
+
+	@Test
+	public void serializeAcceptTest() throws JsonProcessingException {
+		System.out.println(jackson.writeValueAsString(accept1));
+		assertEquals(acceptstring, jackson.writeValueAsString(accept1));
+	}
+
+	@Test
+	public void deserializeAcceptTest() throws IOException {
+		Action act = jackson.readValue(acceptstring, Action.class);
+		assertEquals(accept1, act);
+	}
+
+}
Index: /exampleparties/comparebids/pom.xml
===================================================================
--- /exampleparties/comparebids/pom.xml	(revision 10)
+++ /exampleparties/comparebids/pom.xml	(revision 10)
@@ -0,0 +1,289 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>geniusweb.exampleparties</groupId>
+	<artifactId>comparebids</artifactId>
+	<version>1.0.0</version>
+	<packaging>jar</packaging>
+
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<maven.compiler.target>1.8</maven.compiler.target>
+		<maven.compiler.source>1.8</maven.compiler.source>
+		<basedir>.</basedir>
+		<passwd>${env.ARTIFACTORY_PASS}</passwd>
+		<jackson-2-version>2.9.6</jackson-2-version>
+	</properties>
+
+	<distributionManagement>
+		<repository>
+			<id>artifactory.ewi.tudelft.nl</id>
+			<url>http://artifactory.ewi.tudelft.nl/artifactory/libs-release</url>
+		</repository>
+	</distributionManagement>
+
+
+	<dependencies>
+
+
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>party</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>bidspace</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>profileconnection</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>logging</artifactId>
+			<version>1.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>immutablelist</artifactId>
+			<version>1.0.1</version>
+		</dependency>
+
+		<!-- the core, which includes Streaming API, shared low-level abstractions 
+			(but NOT data-binding) -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- Just the annotations; use this dependency if you want to attach annotations 
+			to classes without connecting them to the code. -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- databinding; ObjectMapper, JsonNode and related classes are here -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- smile (binary JSON). Other artifacts in this group do other formats. -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.dataformat</groupId>
+			<artifactId>jackson-dataformat-smile</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<!-- JAX-RS provider -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.jaxrs</groupId>
+			<artifactId>jackson-jaxrs-json-provider</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<!-- Support for JAX-B annotations as additional configuration -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-jaxb-annotations</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-all</artifactId>
+			<version>1.9.5</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>junit</artifactId>
+			<version>1.0.1</version>
+			<scope>test</scope>
+		</dependency>
+
+	</dependencies>
+
+
+	<repositories>
+		<repository>
+			<id>artifactory.ewi.tudelft.nl</id>
+			<url>http://artifactory.ewi.tudelft.nl/artifactory/libs-release</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+
+	</repositories>
+
+	<pluginRepositories>
+		<pluginRepository>
+			<id>jcenter</id>
+			<url>http://jcenter.bintray.com</url>
+		</pluginRepository>
+	</pluginRepositories>
+
+
+	<build>
+
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.8.0</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>3.1.0</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>3.2.0</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>geniusweb.exampleparties.comparebids.CompareBids</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-javadoc-plugin</artifactId>
+				<version>2.10.1</version>
+				<executions>
+					<execution>
+						<id>attach-javadocs</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+						<configuration>
+							<additionalparam>${javadoc.opts}</additionalparam>
+							<additionalparam>-Xdoclint:none</additionalparam>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.jfrog.buildinfo</groupId>
+				<artifactId>artifactory-maven-plugin</artifactId>
+				<version>2.6.1</version>
+				<executions>
+					<execution>
+						<id>build-info</id>
+						<goals>
+							<goal>publish</goal>
+						</goals>
+						<configuration>
+							<publisher>
+								<contextUrl>http://artifactory.ewi.tudelft.nl/artifactory</contextUrl>
+								<repoKey>libs-release</repoKey>
+								<username>wouter</username>
+								<password>${passwd}</password>
+							</publisher>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.4.1</version>
+				<configuration>
+					<!-- get all project dependencies -->
+					<descriptorRefs>
+						<descriptorRef>jar-with-dependencies</descriptorRef>
+					</descriptorRefs>
+					<archive>
+						<manifest>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+							<mainClass>geniusweb.exampleparties.comparebids.CompareBids</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<!-- bind to the packaging phase -->
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
+
+		</plugins>
+
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>org.jfrog.buildinfo</groupId>
+										<artifactId>artifactory-maven-plugin</artifactId>
+										<versionRange>[1.0.0,)</versionRange>
+										<goals>
+											<goal>publish</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore />
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+
+
+	</build>
+</project>
Index: /exampleparties/comparebids/src/main/java/geniusweb/exampleparties/comparebids/CompareBids.java
===================================================================
--- /exampleparties/comparebids/src/main/java/geniusweb/exampleparties/comparebids/CompareBids.java	(revision 10)
+++ /exampleparties/comparebids/src/main/java/geniusweb/exampleparties/comparebids/CompareBids.java	(revision 10)
@@ -0,0 +1,103 @@
+package geniusweb.exampleparties.comparebids;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.Comparison;
+import geniusweb.actions.ElicitComparison;
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+import geniusweb.party.Capabilities;
+import geniusweb.party.DefaultParty;
+import geniusweb.party.inform.ActionDone;
+import geniusweb.party.inform.Finished;
+import geniusweb.party.inform.Inform;
+import geniusweb.party.inform.Settings;
+import geniusweb.profile.PartialOrdering;
+import geniusweb.profile.Profile;
+import geniusweb.profileconnection.ProfileConnectionFactory;
+import geniusweb.profileconnection.ProfileInterface;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * A party following the COB protocol and thus answers {@link ElicitComparison}
+ * questions. This party does not use a GUI but uses the given profile to
+ * determine the answer.
+ */
+public class CompareBids extends DefaultParty {
+
+	private PartyId me;
+	protected ProfileInterface profileint;
+
+	public CompareBids() {
+	}
+
+	public CompareBids(Reporter reporter) {
+		super(reporter); // for debugging
+	}
+
+	@Override
+	public void notifyChange(Inform info) {
+		try {
+			if (info instanceof Settings) {
+				Settings settings = (Settings) info;
+				this.profileint = ProfileConnectionFactory
+						.create(settings.getProfile().getURI(), getReporter());
+				this.me = settings.getID();
+			} else if (info instanceof ActionDone) {
+				Action action = ((ActionDone) info).getAction();
+				if (action instanceof ElicitComparison) {
+					reply((ElicitComparison) action);
+				}
+			} else if (info instanceof Finished) {
+			} else {
+				reporter.log(Level.WARNING, "COB party ignores " + info);
+			}
+		} catch (Exception e) {
+			throw new RuntimeException("Failed to handle info", e);
+		}
+	}
+
+	@Override
+	public Capabilities getCapabilities() {
+		return new Capabilities(new HashSet<>(Arrays.asList("COB")));
+	}
+
+	@Override
+	public String getDescription() {
+		return "compares a bid with a given list of other bids.";
+	}
+
+	/**
+	 * Handle the {@link ElicitComparison} and reply with {@link Comparison}.
+	 * This shoul dbe fast (assuming the request is reasonably sized) so we
+	 * handle this in-line instead of in separate thread.
+	 */
+	private void reply(ElicitComparison compareRequest) throws IOException {
+		List<Bid> better = new LinkedList<>();
+		List<Bid> worse = new LinkedList<>();
+		Profile profile1 = profileint.getProfile();
+		if (!(profile1 instanceof PartialOrdering)) {
+			throw new IllegalStateException(
+					"Profile must be a partial ordering");
+		}
+		PartialOrdering profile = (PartialOrdering) profile1;
+
+		Bid bid = compareRequest.getBid();
+		for (Bid otherbid : compareRequest.getOptions()) {
+			boolean betteroreq = profile.isPreferredOrEqual(otherbid, bid);
+			boolean worseoreq = profile.isPreferredOrEqual(bid, otherbid);
+			if (betteroreq && !worseoreq)
+				better.add(otherbid);
+			else if (worseoreq && !betteroreq)
+				worse.add(otherbid);
+		}
+		getConnection().send(new Comparison(me, bid, better, worse));
+	}
+
+}
Index: /exampleparties/comparebids/src/test/java/geniusweb/exampleparties/comparebids/CompareBidsTest.java
===================================================================
--- /exampleparties/comparebids/src/test/java/geniusweb/exampleparties/comparebids/CompareBidsTest.java	(revision 10)
+++ /exampleparties/comparebids/src/test/java/geniusweb/exampleparties/comparebids/CompareBidsTest.java	(revision 10)
@@ -0,0 +1,179 @@
+package geniusweb.exampleparties.comparebids;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.bidspace.AllBidsList;
+import geniusweb.connection.ConnectionEnd;
+import geniusweb.issuevalue.Bid;
+import geniusweb.party.Capabilities;
+import geniusweb.party.inform.ActionDone;
+import geniusweb.party.inform.Inform;
+import geniusweb.party.inform.Settings;
+import geniusweb.profile.Profile;
+import geniusweb.profile.utilityspace.LinearAdditive;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.references.Parameters;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+import geniusweb.references.Reference;
+import tudelft.utilities.listener.DefaultListenable;
+
+public class CompareBidsTest {
+
+	private static final String COB = "COB";
+	private static final PartyId otherparty = new PartyId("other");
+	private static final String PROFILE = "src/test/resources/testprofile.json";
+	private final static ObjectMapper jackson = new ObjectMapper();
+
+	private CompareBids party;
+	private final TestConnection connection = new TestConnection();
+	private final ProtocolRef protocol = mock(ProtocolRef.class);
+	private final ProgressRounds progress = mock(ProgressRounds.class);
+	private Settings settings;
+	private LinearAdditive profile;
+	private final Parameters parameters = new Parameters();
+
+	@Before
+	public void before() throws JsonParseException, JsonMappingException,
+			IOException, URISyntaxException {
+		party = new CompareBids();
+		settings = new Settings(new PartyId("party1"),
+				new ProfileRef(new URI("file:" + PROFILE)), protocol, progress,
+				parameters);
+
+		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
+				StandardCharsets.UTF_8);
+		profile = (LinearAdditive) jackson.readValue(serialized, Profile.class);
+
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void getDescriptionTest() {
+		assertNotNull(party.getDescription());
+	}
+
+	@Test
+	public void getCapabilitiesTest() {
+		Capabilities capabilities = party.getCapabilities();
+		assertFalse("party does not define protocols",
+				capabilities.getBehaviours().isEmpty());
+	}
+
+	@Test
+	public void testInformConnection() {
+		party.connect(connection);
+		// agent should not start acting just after an inform
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testInformSettings() {
+		party.connect(connection);
+		connection.notifyListeners(settings);
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testInformAndConnection() {
+		party.connect(connection);
+		party.notifyChange(settings);
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testOtherWalksAway() {
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		party.notifyChange(new ActionDone(new EndNegotiation(otherparty)));
+
+		// party should not act at this point
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testGetCapabilities() {
+		assertTrue(party.getCapabilities().getBehaviours().contains(COB));
+	}
+
+	private Bid findGoodBid() {
+		for (Bid bid : new AllBidsList(profile.getDomain())) {
+			if (profile.getUtility(bid)
+					.compareTo(BigDecimal.valueOf(0.7)) > 0) {
+				return bid;
+			}
+		}
+		throw new IllegalStateException(
+				"Test can not be done: there is no good bid with utility>0.7");
+	}
+
+}
+
+/**
+ * A "real" connection object, because the party is going to subscribe etc, and
+ * without a real connection we would have to do a lot of mocks that would make
+ * the test very hard to read.
+ *
+ */
+class TestConnection extends DefaultListenable<Inform>
+		implements ConnectionEnd<Inform, Action> {
+	private List<Action> actions = new LinkedList<>();
+
+	@Override
+	public void send(Action action) throws IOException {
+		actions.add(action);
+	}
+
+	@Override
+	public Reference getReference() {
+		return null;
+	}
+
+	@Override
+	public URI getRemoteURI() {
+		return null;
+	}
+
+	@Override
+	public void close() {
+
+	}
+
+	@Override
+	public Error getError() {
+		return null;
+	}
+
+	public List<Action> getActions() {
+		return actions;
+	}
+
+}
Index: /exampleparties/comparebids/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/comparebids/src/test/resources/testprofile.json	(revision 10)
+++ /exampleparties/comparebids/src/test/resources/testprofile.json	(revision 10)
@@ -0,0 +1,45 @@
+{
+	"LinearAdditiveUtilitySpace": {
+		"issueUtilities": {
+			"issue2": {
+				"numberutils": {
+					"lowValue": 12,
+					"lowUtility": 0.3,
+					"highValue": 18,
+					"highUtility": 0.9
+				}
+			},
+			"issue1": {
+				"discreteutils": {
+					"valueUtilities": {
+						"issue1value1": 0.2,
+						"issue1value2": 0.8
+					}
+				}
+			}
+		},
+		"issueWeights": {
+			"issue2": 0.4,
+			"issue1": 0.6
+		},
+		"domain": {
+			"name": "test",
+			"issuesValues": {
+				"issue2": {
+					"range": ["12", "18", "1"]
+				},
+				"issue1": {
+					"values": ["issue1value1", "issue1value2"]
+				}
+			}
+		},
+		"name": "testprofile",
+				"reservationBid": {
+			"issuevalues": {
+				"issue1": "issuevalue1",
+				"issue2": "12"
+			}
+		}
+		
+	}
+}
Index: /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/HumanGui.java
===================================================================
--- /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/HumanGui.java	(revision 9)
+++ /exampleparties/humangui/src/main/java/geniusweb/exampleparties/humangui/HumanGui.java	(revision 10)
@@ -2,6 +2,4 @@
 
 import java.awt.BorderLayout;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -23,5 +21,4 @@
 import geniusweb.profileconnection.ProfileConnectionFactory;
 import geniusweb.profileconnection.ProfileInterface;
-import geniusweb.references.ProtocolRef;
 import tudelft.utilities.logging.Reporter;
 
@@ -85,12 +82,5 @@
 	@Override
 	public Capabilities getCapabilities() {
-		try {
-			return new Capabilities(new HashSet<>(
-					Arrays.asList(new ProtocolRef(new URI("SAOP")))));
-		} catch (URISyntaxException e) {
-			getReporter().log(Level.SEVERE, "Failed to create capabilities URI",
-					e);
-			return null;
-		}
+		return new Capabilities(new HashSet<>(Arrays.asList("SAOP")));
 	}
 
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java	(revision 9)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/BiddingInfoTest.java	(revision 10)
@@ -32,5 +32,5 @@
 import geniusweb.party.inform.Settings;
 import geniusweb.profile.Profile;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.profileconnection.ProfileConnectionFactory;
 import geniusweb.profileconnection.ProfileInterface;
@@ -61,5 +61,5 @@
 	private final ProgressRounds progress = mock(ProgressRounds.class);
 	private Settings settings;
-	private LinearAdditiveUtilitySpace profile, profile2;
+	private LinearAdditive profile, profile2;
 	private final Parameters parameters = new Parameters();
 	private Reporter reporter = mock(Reporter.class);
@@ -76,9 +76,8 @@
 		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
 				StandardCharsets.UTF_8);
-		profile = (LinearAdditiveUtilitySpace) jackson.readValue(serialized,
-				Profile.class);
+		profile = (LinearAdditive) jackson.readValue(serialized, Profile.class);
 		String serialized2 = new String(Files.readAllBytes(Paths.get(PROFILE2)),
 				StandardCharsets.UTF_8);
-		profile2 = (LinearAdditiveUtilitySpace) jackson.readValue(serialized2,
+		profile2 = (LinearAdditive) jackson.readValue(serialized2,
 				Profile.class);
 
Index: /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/HumanGuiTest.java
===================================================================
--- /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/HumanGuiTest.java	(revision 9)
+++ /exampleparties/humangui/src/test/java/geniusweb/exampleparties/humangui/HumanGuiTest.java	(revision 10)
@@ -34,5 +34,5 @@
 import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.progress.ProgressRounds;
 import geniusweb.references.Parameters;
@@ -57,5 +57,5 @@
 	private final ProgressRounds progress = mock(ProgressRounds.class);
 	private Settings settings;
-	private LinearAdditiveUtilitySpace profile;
+	private LinearAdditive profile;
 	private final Parameters parameters = new Parameters();
 
@@ -70,6 +70,5 @@
 		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
 				StandardCharsets.UTF_8);
-		profile = (LinearAdditiveUtilitySpace) jackson.readValue(serialized,
-				Profile.class);
+		profile = (LinearAdditive) jackson.readValue(serialized, Profile.class);
 
 	}
@@ -93,10 +92,10 @@
 		Capabilities capabilities = party.getCapabilities();
 		assertFalse("party does not define protocols",
-				capabilities.getProtocols().isEmpty());
+				capabilities.getBehaviours().isEmpty());
 	}
 
 	@Test
 	public void testGetCapabilities() {
-		assertTrue(party.getCapabilities().getProtocols().contains(SAOP));
+		assertTrue(party.getCapabilities().getBehaviours().contains(SAOP));
 	}
 
Index: /exampleparties/pom.xml
===================================================================
--- /exampleparties/pom.xml	(revision 9)
+++ /exampleparties/pom.xml	(revision 10)
@@ -23,4 +23,6 @@
 		<module>boulware</module>
 		<module>linear</module>
+		<module>simpleshaop</module>
+		<module>comparebids</module>
 	</modules>
 </project>
Index: /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java
===================================================================
--- /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java	(revision 9)
+++ /exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java	(revision 10)
@@ -3,6 +3,4 @@
 import java.io.IOException;
 import java.math.BigInteger;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -30,5 +28,4 @@
 import geniusweb.progress.Progress;
 import geniusweb.progress.ProgressRounds;
-import geniusweb.references.ProtocolRef;
 import tudelft.utilities.logging.Reporter;
 
@@ -81,12 +78,5 @@
 	@Override
 	public Capabilities getCapabilities() {
-		try {
-			return new Capabilities(new HashSet<>(
-					Arrays.asList(new ProtocolRef(new URI("SAOP")))));
-		} catch (URISyntaxException e) {
-			getReporter().log(Level.SEVERE, "Failed to create capabilities URI",
-					e);
-			return null;
-		}
+		return new Capabilities(new HashSet<>(Arrays.asList("SAOP")));
 	}
 
@@ -115,5 +105,5 @@
 	}
 
-	private boolean isGood(Bid bid) {
+	private boolean isGood(Bid bid) throws IOException {
 		if (bid == null)
 			return false;
Index: /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java
===================================================================
--- /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java	(revision 9)
+++ /exampleparties/randomparty/src/test/java/geniusweb/exampleparties/randomparty/RandomPartyTest.java	(revision 10)
@@ -42,5 +42,5 @@
 import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.progress.ProgressRounds;
 import geniusweb.references.Parameters;
@@ -53,5 +53,5 @@
 public class RandomPartyTest {
 
-	private static final ProtocolRef SAOP = new ProtocolRef("SAOP");
+	private static final String SAOP = "SAOP";
 	private static final PartyId otherparty = new PartyId("other");
 	private static final String PROFILE = "src/test/resources/testprofile.json";
@@ -63,5 +63,5 @@
 	private final ProgressRounds progress = mock(ProgressRounds.class);
 	private Settings settings;
-	private LinearAdditiveUtilitySpace profile;
+	private LinearAdditive profile;
 	private final Parameters parameters = new Parameters();
 
@@ -76,6 +76,5 @@
 		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
 				StandardCharsets.UTF_8);
-		profile = (LinearAdditiveUtilitySpace) jackson.readValue(serialized,
-				Profile.class);
+		profile = (LinearAdditive) jackson.readValue(serialized, Profile.class);
 
 	}
@@ -94,5 +93,5 @@
 		Capabilities capabilities = party.getCapabilities();
 		assertFalse("party does not define protocols",
-				capabilities.getProtocols().isEmpty());
+				capabilities.getBehaviours().isEmpty());
 	}
 
@@ -176,5 +175,5 @@
 	@Test
 	public void testGetCapabilities() {
-		assertTrue(party.getCapabilities().getProtocols().contains(SAOP));
+		assertTrue(party.getCapabilities().getBehaviours().contains(SAOP));
 	}
 
Index: /exampleparties/randompartypy/src/main/resources/RandomParty.py
===================================================================
--- /exampleparties/randompartypy/src/main/resources/RandomParty.py	(revision 9)
+++ /exampleparties/randompartypy/src/main/resources/RandomParty.py	(revision 10)
@@ -65,9 +65,5 @@
 	# Override
 	def getCapabilities(self): # -> Capabilities
-		try:
-			return Capabilities(HashSet([ ProtocolRef(URI("SAOP"))]))
-		except:
-			getReporter().log(Level.SEVERE, "Failed to create capabilities URI",e);
-		return None
+			return Capabilities(HashSet([ "SAOP"]))
 
 	# Override
Index: /exampleparties/randompartypy/src/test/java/geniusweb/exampleparties/randompartypy/RandomPartyTest.java
===================================================================
--- /exampleparties/randompartypy/src/test/java/geniusweb/exampleparties/randompartypy/RandomPartyTest.java	(revision 9)
+++ /exampleparties/randompartypy/src/test/java/geniusweb/exampleparties/randompartypy/RandomPartyTest.java	(revision 10)
@@ -39,5 +39,5 @@
 import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.progress.ProgressRounds;
 import geniusweb.pythonadapter.PythonPartyAdapter;
@@ -51,5 +51,5 @@
 
 	private static final String PROFILE = "src/test/resources/testprofile.json";
-	private static final ProtocolRef SAOP = new ProtocolRef("SAOP");
+	private static final String SAOP = "SAOP";
 	private static final PartyId otherparty = new PartyId("other");
 	private final static ObjectMapper jackson = new ObjectMapper();
@@ -62,5 +62,5 @@
 	private final Parameters parameters = new Parameters();
 
-	private LinearAdditiveUtilitySpace profile; // the real profile
+	private LinearAdditive profile; // the real profile
 
 	@Before
@@ -80,6 +80,5 @@
 		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
 				StandardCharsets.UTF_8);
-		profile = (LinearAdditiveUtilitySpace) jackson.readValue(serialized,
-				Profile.class);
+		profile = (LinearAdditive) jackson.readValue(serialized, Profile.class);
 
 	}
@@ -98,5 +97,5 @@
 		Capabilities capabilities = party.getCapabilities();
 		assertFalse("party does not define protocols",
-				capabilities.getProtocols().isEmpty());
+				capabilities.getBehaviours().isEmpty());
 	}
 
@@ -167,5 +166,5 @@
 	@Test
 	public void testGetCapabilities() {
-		assertTrue(party.getCapabilities().getProtocols().contains(SAOP));
+		assertTrue(party.getCapabilities().getBehaviours().contains(SAOP));
 	}
 
Index: /exampleparties/simpleshaop/pom.xml
===================================================================
--- /exampleparties/simpleshaop/pom.xml	(revision 10)
+++ /exampleparties/simpleshaop/pom.xml	(revision 10)
@@ -0,0 +1,289 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>geniusweb.exampleparties</groupId>
+	<artifactId>simpleshaop</artifactId>
+	<version>1.0.0</version>
+	<packaging>jar</packaging>
+
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<maven.compiler.target>1.8</maven.compiler.target>
+		<maven.compiler.source>1.8</maven.compiler.source>
+		<basedir>.</basedir>
+		<passwd>${env.ARTIFACTORY_PASS}</passwd>
+		<jackson-2-version>2.9.6</jackson-2-version>
+	</properties>
+
+	<distributionManagement>
+		<repository>
+			<id>artifactory.ewi.tudelft.nl</id>
+			<url>http://artifactory.ewi.tudelft.nl/artifactory/libs-release</url>
+		</repository>
+	</distributionManagement>
+
+
+	<dependencies>
+
+
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>party</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>bidspace</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>geniusweb</groupId>
+			<artifactId>profileconnection</artifactId>
+			<version>1.1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>logging</artifactId>
+			<version>1.0.0</version>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>immutablelist</artifactId>
+			<version>1.0.1</version>
+		</dependency>
+
+		<!-- the core, which includes Streaming API, shared low-level abstractions 
+			(but NOT data-binding) -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- Just the annotations; use this dependency if you want to attach annotations 
+			to classes without connecting them to the code. -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- databinding; ObjectMapper, JsonNode and related classes are here -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- smile (binary JSON). Other artifacts in this group do other formats. -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.dataformat</groupId>
+			<artifactId>jackson-dataformat-smile</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<!-- JAX-RS provider -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.jaxrs</groupId>
+			<artifactId>jackson-jaxrs-json-provider</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<!-- Support for JAX-B annotations as additional configuration -->
+		<dependency>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-jaxb-annotations</artifactId>
+			<version>${jackson-2-version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-all</artifactId>
+			<version>1.9.5</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>tudelft.utilities</groupId>
+			<artifactId>junit</artifactId>
+			<version>1.0.1</version>
+			<scope>test</scope>
+		</dependency>
+
+	</dependencies>
+
+
+	<repositories>
+		<repository>
+			<id>artifactory.ewi.tudelft.nl</id>
+			<url>http://artifactory.ewi.tudelft.nl/artifactory/libs-release</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+
+	</repositories>
+
+	<pluginRepositories>
+		<pluginRepository>
+			<id>jcenter</id>
+			<url>http://jcenter.bintray.com</url>
+		</pluginRepository>
+	</pluginRepositories>
+
+
+	<build>
+
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.8.0</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>3.1.0</version>
+				<executions>
+					<execution>
+						<id>attach-sources</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>3.2.0</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>geniusweb.exampleparties.simpleshaop.ShaopParty</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-javadoc-plugin</artifactId>
+				<version>2.10.1</version>
+				<executions>
+					<execution>
+						<id>attach-javadocs</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+						<configuration>
+							<additionalparam>${javadoc.opts}</additionalparam>
+							<additionalparam>-Xdoclint:none</additionalparam>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.jfrog.buildinfo</groupId>
+				<artifactId>artifactory-maven-plugin</artifactId>
+				<version>2.6.1</version>
+				<executions>
+					<execution>
+						<id>build-info</id>
+						<goals>
+							<goal>publish</goal>
+						</goals>
+						<configuration>
+							<publisher>
+								<contextUrl>http://artifactory.ewi.tudelft.nl/artifactory</contextUrl>
+								<repoKey>libs-release</repoKey>
+								<username>wouter</username>
+								<password>${passwd}</password>
+							</publisher>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.4.1</version>
+				<configuration>
+					<!-- get all project dependencies -->
+					<descriptorRefs>
+						<descriptorRef>jar-with-dependencies</descriptorRef>
+					</descriptorRefs>
+					<archive>
+						<manifest>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+							<mainClass>geniusweb.exampleparties.simpleshaop.ShaopParty</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<!-- bind to the packaging phase -->
+						<phase>package</phase>
+						<goals>
+							<goal>single</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+
+
+		</plugins>
+
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>org.jfrog.buildinfo</groupId>
+										<artifactId>artifactory-maven-plugin</artifactId>
+										<versionRange>[1.0.0,)</versionRange>
+										<goals>
+											<goal>publish</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore />
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+
+
+	</build>
+</project>
Index: /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/ShaopParty.java
===================================================================
--- /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/ShaopParty.java	(revision 10)
+++ /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/ShaopParty.java	(revision 10)
@@ -0,0 +1,147 @@
+package geniusweb.exampleparties.simpleshaop;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.logging.Level;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.Action;
+import geniusweb.actions.Comparison;
+import geniusweb.actions.ElicitComparison;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.bidspace.AllPartialBidsList;
+import geniusweb.issuevalue.Bid;
+import geniusweb.party.Capabilities;
+import geniusweb.party.DefaultParty;
+import geniusweb.party.inform.ActionDone;
+import geniusweb.party.inform.Finished;
+import geniusweb.party.inform.Inform;
+import geniusweb.party.inform.Settings;
+import geniusweb.party.inform.YourTurn;
+import geniusweb.profile.PartialOrdering;
+import geniusweb.profileconnection.ProfileConnectionFactory;
+import geniusweb.profileconnection.ProfileInterface;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * A simple implementation of a SHAOP party that can handle only bilateral
+ * negotiations (1 other party). It will ignore all other parties except the one
+ * that has the turn right before us. It estimates the utilities of bids by
+ * assigning a linear increasing utility from the orderings that have been
+ * created.
+ * <p>
+ * <b>Requirement<b> the initial {@link PartialOrdering} must contain at least
+ * the bids with lowest utility and highest utility, and the proper comparison
+ * info for these two bids.
+ */
+public class ShaopParty extends DefaultParty {
+
+	private static final BigDecimal N09 = new BigDecimal("0.9");
+	private Bid lastReceivedBid = null; // we ignore all others
+	private PartyId me;
+	private final Random random = new Random();
+	protected ProfileInterface profileint;
+	private Progress progress;
+	private SimpleLinearOrdering estimatedProfile = null;
+
+	public ShaopParty() {
+	}
+
+	public ShaopParty(Reporter reporter) {
+		super(reporter); // for debugging
+	}
+
+	@Override
+	public void notifyChange(Inform info) {
+		try {
+			if (info instanceof Settings) {
+				Settings settings = (Settings) info;
+				this.profileint = ProfileConnectionFactory
+						.create(settings.getProfile().getURI(), getReporter());
+				this.me = settings.getID();
+				this.progress = settings.getProgress();
+			} else if (info instanceof ActionDone) {
+				Action otheract = ((ActionDone) info).getAction();
+				if (otheract instanceof Offer) {
+					lastReceivedBid = ((Offer) otheract).getBid();
+				} else if (otheract instanceof Comparison) {
+					estimatedProfile = estimatedProfile.with(
+							((Comparison) otheract).getBid(),
+							((Comparison) otheract).getWorse());
+					myTurn();
+				}
+			} else if (info instanceof YourTurn) {
+				myTurn();
+			} else if (info instanceof Finished) {
+				getReporter().log(Level.INFO, "Final ourcome:" + info);
+			}
+		} catch (Exception e) {
+			throw new RuntimeException("Failed to handle info", e);
+		}
+	}
+
+	@Override
+	public Capabilities getCapabilities() {
+		return new Capabilities(new HashSet<>(Arrays.asList("SHAOP")));
+	}
+
+	@Override
+	public String getDescription() {
+		return "Communicates with COB party to figure out which bids are good. Accepts bids with utility > 0.9. Offers random bids.";
+	}
+
+	/**
+	 * Called when it's (still) our turn and we should take some action. Also
+	 * Updates the progress if necessary.
+	 */
+	private void myTurn() throws IOException {
+		Action action = null;
+		if (estimatedProfile == null) {
+			estimatedProfile = new SimpleLinearOrdering(
+					profileint.getProfile());
+		}
+
+		if (lastReceivedBid != null) {
+			// then we do the action now, no need to ask user
+			if (estimatedProfile.contains(lastReceivedBid)) {
+				if (isGood(lastReceivedBid)) {
+					action = new Accept(me, lastReceivedBid);
+				}
+			} else {
+				// we did not yet assess the received bid
+				action = new ElicitComparison(me, lastReceivedBid,
+						estimatedProfile.getBids());
+			}
+			if (progress instanceof ProgressRounds) {
+				progress = ((ProgressRounds) progress).advance();
+			}
+		}
+		// Otherwise just offer a Random bid
+		// FIXME this is weird, can't we do better than random?
+		if (action == null)
+			action = randomBid();
+		getConnection().send(action);
+	}
+
+	private Offer randomBid() throws IOException {
+		AllPartialBidsList bidspace = new AllPartialBidsList(
+				profileint.getProfile().getDomain());
+		long i = random.nextInt(bidspace.size().intValue());
+		Bid bid = bidspace.get(BigInteger.valueOf(i));
+		return new Offer(me, bid);
+	}
+
+	private boolean isGood(Bid bid) {
+		if (bid == null)
+			return false;
+		return estimatedProfile.getUtility(bid).compareTo(N09) >= 0;
+	}
+
+}
Index: /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/SimpleLinearOrdering.java
===================================================================
--- /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/SimpleLinearOrdering.java	(revision 10)
+++ /exampleparties/simpleshaop/src/main/java/geniusweb/exampleparties/simpleshaop/SimpleLinearOrdering.java	(revision 10)
@@ -0,0 +1,125 @@
+package geniusweb.exampleparties.simpleshaop;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.Domain;
+import geniusweb.profile.DefaultPartialOrdering;
+import geniusweb.profile.Profile;
+import geniusweb.profile.utilityspace.UtilitySpace;
+
+/**
+ * A simple list of bids, but all bids are fully ordered (better or worse than
+ * other bids in the list).
+ */
+public class SimpleLinearOrdering implements UtilitySpace {
+
+	private final Domain domain;
+	private final List<Bid> bids; // worst bid first, best bid last.
+
+	SimpleLinearOrdering(Profile profile) {
+		this(profile.getDomain(), getSortedBids(profile));
+	}
+
+	/**
+	 * 
+	 * @param domain
+	 * @param bids   a list of bids, ordered from lowest to highest util. The
+	 *               first bid will have utility 0, the last utility 1. If only
+	 *               0 or 1 bid in the list, or if the bid is not known, it will
+	 *               have utility 0.
+	 */
+	SimpleLinearOrdering(Domain domain, List<Bid> bids) {
+		this.domain = domain;
+		this.bids = bids;
+	}
+
+	@Override
+	public String getName() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Domain getDomain() {
+		return domain;
+	}
+
+	@Override
+	public Bid getReservationBid() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public BigDecimal getUtility(Bid bid) {
+		if (bids.size() < 2 || !bids.contains(bid)) {
+			return BigDecimal.ZERO;
+		}
+		// using 8 decimals, we have to pick something here
+		return new BigDecimal(bids.indexOf(bid)).divide(
+				new BigDecimal((bids.size() - 1)), 8, RoundingMode.HALF_UP);
+	}
+
+	/**
+	 * 
+	 * @param bid
+	 * @return true iff bid is contained in this ordering
+	 */
+	public boolean contains(Bid bid) {
+		return bids.contains(bid);
+	}
+
+	/**
+	 * 
+	 * @return list of all bids in the current ordering.
+	 */
+	public List<Bid> getBids() {
+		return Collections.unmodifiableList(bids);
+	}
+
+	/**
+	 * 
+	 * @param profile
+	 * @return a list of bids in the profile sorted from low to high utility.
+	 */
+	private static List<Bid> getSortedBids(Profile profile) {
+		if (!(profile instanceof DefaultPartialOrdering)) {
+			throw new UnsupportedOperationException(
+					"Only DefaultPartialOrdering supported");
+		}
+		DefaultPartialOrdering prof = (DefaultPartialOrdering) profile;
+		List<Bid> bidslist = prof.getBids();
+		Collections.sort(bidslist, new Comparator<Bid>() {
+
+			@Override
+			public int compare(Bid b1, Bid b2) {
+				// notice, -1 if better because WORST bid first
+				return prof.isPreferredOrEqual(b1, b2) ? -1 : 1;
+			}
+
+		});
+
+		return bidslist;
+	}
+
+	/**
+	 * @param bid       a new bid to be inserted
+	 * @param worseBids all bids that are worse than this bid.
+	 * @return a SimpleLinearOrdering, updated with the given comparison. Thee
+	 *         bid will be inserted after the first bid that is not worse than
+	 *         bid.
+	 */
+	public SimpleLinearOrdering with(Bid bid, List<Bid> worseBids) {
+		int n = 0;
+		while (n < bids.size() && worseBids.contains(bids.get(n)))
+			n++;
+		LinkedList<Bid> newbids = new LinkedList<Bid>(bids);
+		newbids.add(n, bid);
+		return new SimpleLinearOrdering(domain, newbids);
+	}
+
+}
Index: /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/ShaopPartyTest.java
===================================================================
--- /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/ShaopPartyTest.java	(revision 10)
+++ /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/ShaopPartyTest.java	(revision 10)
@@ -0,0 +1,251 @@
+package geniusweb.exampleparties.simpleshaop;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.Action;
+import geniusweb.actions.ElicitComparison;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.connection.ConnectionEnd;
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.DiscreteValue;
+import geniusweb.issuevalue.Value;
+import geniusweb.party.Capabilities;
+import geniusweb.party.inform.ActionDone;
+import geniusweb.party.inform.Finished;
+import geniusweb.party.inform.Inform;
+import geniusweb.party.inform.Settings;
+import geniusweb.party.inform.YourTurn;
+import geniusweb.profile.Profile;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.references.Parameters;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+import geniusweb.references.Reference;
+import tudelft.utilities.listener.DefaultListenable;
+import tudelft.utilities.logging.Reporter;
+
+public class ShaopPartyTest {
+
+	private static final String SHAOP = "SHAOP";
+	private static final PartyId otherparty = new PartyId("other");
+	private static final String PROFILE = "src/test/resources/testprofile.json";
+	private final static ObjectMapper jackson = new ObjectMapper();
+
+	private ShaopParty party;
+	private final TestConnection connection = new TestConnection();
+	private final ProtocolRef protocol = mock(ProtocolRef.class);
+	private final ProgressRounds progress = mock(ProgressRounds.class);
+	private Settings settings;
+	private Profile profile;
+	private final Parameters parameters = new Parameters();
+
+	@Before
+	public void before() throws JsonParseException, JsonMappingException,
+			IOException, URISyntaxException {
+		party = new ShaopParty();
+		settings = new Settings(new PartyId("party1"),
+				new ProfileRef(new URI("file:" + PROFILE)), protocol, progress,
+				parameters);
+
+		String serialized = new String(Files.readAllBytes(Paths.get(PROFILE)),
+				StandardCharsets.UTF_8);
+		profile = jackson.readValue(serialized, Profile.class);
+
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void getDescriptionTest() {
+		assertNotNull(party.getDescription());
+	}
+
+	@Test
+	public void getCapabilitiesTest() {
+		Capabilities capabilities = party.getCapabilities();
+		assertFalse("party does not define protocols",
+				capabilities.getBehaviours().isEmpty());
+	}
+
+	@Test
+	public void testInformConnection() {
+		party.connect(connection);
+		// agent should not start acting just after an inform
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testInformSettings() {
+		party.connect(connection);
+		connection.notifyListeners(settings);
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testInformAndConnection() {
+		party.connect(connection);
+		party.notifyChange(settings);
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testOtherWalksAway() {
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		party.notifyChange(new ActionDone(new EndNegotiation(otherparty)));
+
+		// party should not act at this point
+		assertEquals(0, connection.getActions().size());
+	}
+
+	@Test
+	public void testAgentHasFirstTurn() {
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		party.notifyChange(new YourTurn());
+		assertEquals(1, connection.getActions().size());
+		assertTrue(connection.getActions().get(0) instanceof Offer);
+	}
+
+	@Test
+	public void testAgentElicitsComparison() {
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		Bid bid = makeBid("3", "3"); // not yet in profile
+		party.notifyChange(new ActionDone(new Offer(otherparty, bid)));
+		party.notifyChange(new YourTurn());
+		assertEquals(1, connection.getActions().size());
+		assertTrue(connection.getActions().get(0) instanceof ElicitComparison);
+
+	}
+
+	@Ignore
+	@Test
+	public void testAgentAccepts() {
+		// to make agent accept, the offered bid must score >0.9
+		// we would need to mock a lot t make that happen
+		// as we need to place >10 comparable bids for that.
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		Bid bid = makeBid("1", "1");// best pssible
+		party.notifyChange(new ActionDone(new Offer(otherparty, bid)));
+		party.notifyChange(new YourTurn());
+		assertEquals(1, connection.getActions().size());
+		assertTrue(connection.getActions().get(0) instanceof Accept);
+
+	}
+
+	@Test
+	public void testAgentLogsFinal() {
+		// this log output is optional, this is to show how to check log
+		Reporter reporter = mock(Reporter.class);
+		party = new ShaopParty(reporter);
+		party.connect(connection);
+		party.notifyChange(settings);
+		party.notifyChange(new Finished(null));
+
+		verify(reporter).log(eq(Level.INFO),
+				eq("Final ourcome:Finished[null]"));
+	}
+
+	@Test
+	public void testAgentsUpdatesProgress() {
+		party.connect(connection);
+		party.notifyChange(settings);
+
+		party.notifyChange(new ActionDone(new Offer(null, mock(Bid.class))));
+
+		party.notifyChange(new YourTurn());
+		verify(progress).advance();
+	}
+
+	@Test
+	public void testGetCapabilities() {
+		assertTrue(party.getCapabilities().getBehaviours().contains(SHAOP));
+	}
+
+	private Bid makeBid(String val1, String val2) {
+		Map<String, Value> map = new HashMap<>();
+		map.put("iss1", new DiscreteValue(val1));
+		map.put("iss2", new DiscreteValue(val2));
+		return new Bid(map);
+
+	}
+
+}
+
+/**
+ * A "real" connection object, because the party is going to subscribe etc, and
+ * without a real connection we would have to do a lot of mocks that would make
+ * the test very hard to read.
+ *
+ */
+class TestConnection extends DefaultListenable<Inform>
+		implements ConnectionEnd<Inform, Action> {
+	private List<Action> actions = new LinkedList<>();
+
+	@Override
+	public void send(Action action) throws IOException {
+		actions.add(action);
+	}
+
+	@Override
+	public Reference getReference() {
+		return null;
+	}
+
+	@Override
+	public URI getRemoteURI() {
+		return null;
+	}
+
+	@Override
+	public void close() {
+
+	}
+
+	@Override
+	public Error getError() {
+		return null;
+	}
+
+	public List<Action> getActions() {
+		return actions;
+	}
+
+}
Index: /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/SimpleLinearOrderingTest.java
===================================================================
--- /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/SimpleLinearOrderingTest.java	(revision 10)
+++ /exampleparties/simpleshaop/src/test/java/geniusweb/exampleparties/simpleshaop/SimpleLinearOrderingTest.java	(revision 10)
@@ -0,0 +1,120 @@
+package geniusweb.exampleparties.simpleshaop;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import geniusweb.issuevalue.Bid;
+import geniusweb.issuevalue.Domain;
+import geniusweb.profile.DefaultPartialOrdering;
+
+public class SimpleLinearOrderingTest {
+	private DefaultPartialOrdering realprofile = mock(
+			DefaultPartialOrdering.class);;
+	private Domain domain = mock(Domain.class);
+	private SimpleLinearOrdering estprofile;
+	private Bid bid1 = makeBid(1);
+	private Bid bid2 = makeBid(2);
+	private Bid bid3 = makeBid(3);
+	private Bid bid4 = makeBid(4);
+	private final List<Bid> emptyList = Collections.emptyList();
+
+	@Before
+	public void before() {
+		when(realprofile.getDomain()).thenReturn(domain);
+		estprofile = new SimpleLinearOrdering(realprofile);
+		when(bid1.toString()).thenReturn("bid1");
+
+	}
+
+	private static Bid makeBid(int n) {
+		Bid mockbid = mock(Bid.class);
+		when(mockbid.toString()).thenReturn("bid " + n);
+		return mockbid;
+	}
+
+	@Test
+	public void testSmoke() {
+	}
+
+	@Test
+	public void testInitialBids() {
+		assertTrue(estprofile.getBids().isEmpty());
+	}
+
+	@Test
+	public void largerInitialListTest() {
+		when(realprofile.getBids()).thenReturn(Arrays.asList(bid1, bid2, bid3));
+		// idea is that 3 > 2 > 1
+		when(realprofile.isPreferredOrEqual(bid2, bid1)).thenReturn(true);
+		when(realprofile.isPreferredOrEqual(bid1, bid2)).thenReturn(false);
+		when(realprofile.isPreferredOrEqual(bid3, bid2)).thenReturn(true);
+		when(realprofile.isPreferredOrEqual(bid2, bid3)).thenReturn(false);
+		when(realprofile.isPreferredOrEqual(bid3, bid1)).thenReturn(true);
+		when(realprofile.isPreferredOrEqual(bid1, bid3)).thenReturn(false);
+		SimpleLinearOrdering testprofile = new SimpleLinearOrdering(
+				realprofile);
+		assertEquals(Arrays.asList(bid3, bid2, bid1), testprofile.getBids());
+	}
+
+	@Test
+	public void testAddFirstBid() {
+		SimpleLinearOrdering prof1 = estprofile.with(bid1,
+				Collections.emptyList());
+
+		assertEquals(1, prof1.getBids().size());
+	}
+
+	@Test
+	public void testAddTwoBidsSecondIsWorse() {
+		SimpleLinearOrdering prof1 = estprofile.with(bid1, emptyList);
+		// new bid is worse as bid1, so list of worse bids is empty
+		SimpleLinearOrdering prof2 = prof1.with(bid2, emptyList);
+
+		assertEquals(Arrays.asList(bid2, bid1), prof2.getBids());
+	}
+
+	@Test
+	public void testAddTwoBidsSecondIsBetter() {
+		SimpleLinearOrdering prof1 = estprofile.with(bid1, emptyList);
+		SimpleLinearOrdering prof2 = prof1.with(bid2, Arrays.asList(bid1));
+
+		assertEquals(Arrays.asList(bid1, bid2), prof2.getBids());
+	}
+
+	@Test
+	public void testAddThreeBidsThirdIsMiddle() {
+		SimpleLinearOrdering prof1 = new SimpleLinearOrdering(domain,
+				Arrays.asList(bid1, bid2));
+		SimpleLinearOrdering prof2 = prof1.with(bid3, Arrays.asList(bid1));
+
+		assertEquals(Arrays.asList(bid1, bid3, bid2), prof2.getBids());
+	}
+
+	@Test
+	public void getUtilityTest3Bids() {
+		SimpleLinearOrdering prof1 = new SimpleLinearOrdering(domain,
+				Arrays.asList(bid1, bid3, bid2));
+
+		assertEquals(new BigDecimal("0.50000000"), prof1.getUtility(bid3));
+	}
+
+	@Test
+	public void getUtilityTest4Bids() {
+		// with 4 bids, we get utility 1/3 which causes deximal expansiion issue
+		SimpleLinearOrdering prof1 = new SimpleLinearOrdering(domain,
+				Arrays.asList(bid1, bid2, bid3, bid4));
+
+		assertEquals(new BigDecimal("0.33333333"), prof1.getUtility(bid2));
+	}
+
+}
Index: /exampleparties/simpleshaop/src/test/resources/testprofile.json
===================================================================
--- /exampleparties/simpleshaop/src/test/resources/testprofile.json	(revision 10)
+++ /exampleparties/simpleshaop/src/test/resources/testprofile.json	(revision 10)
@@ -0,0 +1,182 @@
+{
+	"DefaultPartialOrdering": {
+		"name": "ordering1",
+		"domain": {
+			"name": "dom1",
+			"issuesValues": {
+				"iss1": {
+					"values": [
+						"1",
+						"2",
+						"3",
+						"4"
+					]
+				},
+				"iss2": {
+					"values": [
+						"1",
+						"2",
+						"3",
+						"4"
+					]
+				}
+			}
+		},
+		"reservationBid": {
+			"issuevalues": {
+				"iss1": "3",
+				"iss2": "3"
+			}
+		},
+		"bids": [
+			{
+				"issuevalues": {
+					"iss1": "1",
+					"iss2": "1"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "2",
+					"iss2": "1"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "3",
+					"iss2": "1"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "4",
+					"iss2": "1"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "1",
+					"iss2": "2"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "2",
+					"iss2": "2"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "3",
+					"iss2": "2"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "4",
+					"iss2": "2"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "1",
+					"iss2": "3"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "2",
+					"iss2": "3"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "4",
+					"iss2": "3"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "1",
+					"iss2": "4"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "2",
+					"iss2": "4"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "3",
+					"iss2": "4"
+				}
+			},
+			{
+				"issuevalues": {
+					"iss1": "4",
+					"iss2": "4"
+				}
+			}
+		],
+		"better": [
+			[
+				0,
+				1
+			],
+			[
+				1,
+				2
+			],
+			[
+				2,
+				3
+			],
+			[
+				3,
+				4
+			],
+			[
+				4,
+				5
+			],
+			[
+				5,
+				6
+			],
+			[
+				6,
+				7
+			],
+			[
+				7,
+				8
+			],
+			[
+				8,
+				9
+			],
+			[
+				9,
+				10
+			],
+			[
+				10,
+				11
+			],
+			[
+				11,
+				12
+			],
+			[
+				12,
+				13
+			],
+			[
+				13,
+				14
+			]
+		]
+	}
+}
Index: /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/ExtendedUtilSpace.java
===================================================================
--- /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/ExtendedUtilSpace.java	(revision 9)
+++ /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/ExtendedUtilSpace.java	(revision 10)
@@ -11,5 +11,5 @@
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.Value;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import tudelft.utilities.immutablelist.ImmutableList;
 
@@ -20,5 +20,5 @@
  */
 public class ExtendedUtilSpace {
-	private LinearAdditiveUtilitySpace utilspace;
+	private LinearAdditive utilspace;
 	private BigDecimal tolerance; // utility tolerance for a bid.
 	private BidsWithUtility bidutils;
@@ -27,5 +27,5 @@
 	private BigDecimal maxUtil;
 
-	public ExtendedUtilSpace(LinearAdditiveUtilitySpace space) {
+	public ExtendedUtilSpace(LinearAdditive space) {
 		this.utilspace = space;
 		bidutils = new BidsWithUtility(utilspace);
Index: /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java
===================================================================
--- /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java	(revision 9)
+++ /exampleparties/timedependentparty/src/main/java/geniusweb/exampleparties/timedependentparty/TimeDependentParty.java	(revision 10)
@@ -4,6 +4,4 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -24,10 +22,9 @@
 import geniusweb.party.inform.YourTurn;
 import geniusweb.profile.Profile;
-import geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace;
+import geniusweb.profile.utilityspace.LinearAdditive;
 import geniusweb.profileconnection.ProfileConnectionFactory;
 import geniusweb.profileconnection.ProfileInterface;
 import geniusweb.progress.Progress;
 import geniusweb.progress.ProgressRounds;
-import geniusweb.references.ProtocolRef;
 import tudelft.utilities.immutablelist.ImmutableList;
 import tudelft.utilities.logging.Reporter;
@@ -44,5 +41,5 @@
 	private static final BigDecimal DEC100 = new BigDecimal("100");
 	private ProfileInterface profileint;
-	private LinearAdditiveUtilitySpace utilspace = null; // last received space
+	private LinearAdditive utilspace = null; // last received space
 	private PartyId me;
 	private Progress progress;
@@ -61,12 +58,5 @@
 	@Override
 	public Capabilities getCapabilities() {
-		try {
-			return new Capabilities(new HashSet<>(
-					Arrays.asList(new ProtocolRef(new URI("SAOP")))));
-		} catch (URISyntaxException e) {
-			getReporter().log(Level.SEVERE, "Failed to create capabilities URI",
-					e);
-			return null;
-		}
+		return new Capabilities(new HashSet<>(Arrays.asList("SAOP")));
 	}
 
@@ -148,8 +138,8 @@
 	}
 
-	private LinearAdditiveUtilitySpace updateUtilSpace() {
+	private LinearAdditive updateUtilSpace() throws IOException {
 		Profile newutilspace = profileint.getProfile();
 		if (!newutilspace.equals(utilspace)) {
-			utilspace = (LinearAdditiveUtilitySpace) newutilspace;
+			utilspace = (LinearAdditive) newutilspace;
 			extendedspace = new ExtendedUtilSpace(utilspace);
 		}
Index: /exampleparties/timedependentparty/src/test/java/geniusweb/exampleparties/timedependentparty/TimeDependentPartyTest.java
===================================================================
--- /exampleparties/timedependentparty/src/test/java/geniusweb/exampleparties/timedependentparty/TimeDependentPartyTest.java	(revision 9)
+++ /exampleparties/timedependentparty/src/test/java/geniusweb/exampleparties/timedependentparty/TimeDependentPartyTest.java	(revision 10)
@@ -53,5 +53,5 @@
 public class TimeDependentPartyTest {
 
-	private static final ProtocolRef SAOP = new ProtocolRef("SAOP");
+	private static final String SAOP = "SAOP";
 	private static final PartyId otherparty = new PartyId("other");
 	private static final String PROFILE = "src/test/resources/testprofile.json";
@@ -105,5 +105,5 @@
 		Capabilities capabilities = party.getCapabilities();
 		assertFalse("party does not define protocols",
-				capabilities.getProtocols().isEmpty());
+				capabilities.getBehaviours().isEmpty());
 	}
 
@@ -198,5 +198,5 @@
 	@Test
 	public void testGetCapabilities() {
-		assertTrue(party.getCapabilities().getProtocols().contains(SAOP));
+		assertTrue(party.getCapabilities().getBehaviours().contains(SAOP));
 	}
 
Index: /party/src/main/java/geniusweb/party/Capabilities.java
===================================================================
--- /party/src/main/java/geniusweb/party/Capabilities.java	(revision 9)
+++ /party/src/main/java/geniusweb/party/Capabilities.java	(revision 10)
@@ -5,7 +5,4 @@
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-
-import geniusweb.references.ProtocolRef;
-
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -19,5 +16,5 @@
 	 * List of supported protocols
 	 */
-	private final Set<ProtocolRef> protocols;
+	private final Set<String> behaviours;
 
 	/**
@@ -26,13 +23,13 @@
 	 */
 	@JsonCreator
-	public Capabilities(@JsonProperty("protocols") Set<ProtocolRef> protocols) {
-		if (protocols == null) {
-			throw new IllegalArgumentException("protocols==null");
+	public Capabilities(@JsonProperty("behaviours") Set<String> behaviours) {
+		if (behaviours == null) {
+			throw new IllegalArgumentException("behaviours==null");
 		}
-		this.protocols = protocols;
+		this.behaviours = behaviours;
 	}
 
-	public Set<ProtocolRef> getProtocols() {
-		return protocols;
+	public Set<String> getBehaviours() {
+		return behaviours;
 	}
 
@@ -42,5 +39,5 @@
 		int result = 1;
 		result = prime * result
-				+ ((protocols == null) ? 0 : protocols.hashCode());
+				+ ((behaviours == null) ? 0 : behaviours.hashCode());
 		return result;
 	}
@@ -55,8 +52,8 @@
 			return false;
 		Capabilities other = (Capabilities) obj;
-		if (protocols == null) {
-			if (other.protocols != null)
+		if (behaviours == null) {
+			if (other.behaviours != null)
 				return false;
-		} else if (!protocols.equals(other.protocols))
+		} else if (!behaviours.equals(other.behaviours))
 			return false;
 		return true;
@@ -65,5 +62,5 @@
 	@Override
 	public String toString() {
-		return "Capabilities[" + "Protocols=" + protocols + "]";
+		return "Capabilities[" + "Behaviours=" + behaviours + "]";
 	}
 
Index: /party/src/main/java/geniusweb/party/inform/Inform.java
===================================================================
--- /party/src/main/java/geniusweb/party/inform/Inform.java	(revision 9)
+++ /party/src/main/java/geniusweb/party/inform/Inform.java	(revision 10)
@@ -3,9 +3,8 @@
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 import geniusweb.party.Party;
-
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 /**
Index: /party/src/test/java/geniusweb/party/CapabilitiesTest.java
===================================================================
--- /party/src/test/java/geniusweb/party/CapabilitiesTest.java	(revision 9)
+++ /party/src/test/java/geniusweb/party/CapabilitiesTest.java	(revision 10)
@@ -4,5 +4,4 @@
 
 import java.io.IOException;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Arrays;
@@ -18,6 +17,4 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import geniusweb.party.Capabilities;
-import geniusweb.references.ProtocolRef;
 import tudelft.utilities.junit.GeneralTests;
 
@@ -26,16 +23,12 @@
 	private Capabilities capabilities1, capabilities1a, capabilities2,
 			capabilities3;
-	private String asJson = "{\"protocols\":[\"ws:localhost/protocol1\"]}";
+	private String asJson = "{\"behaviours\":[\"SAOP\"]}";
 
 	@Before
 	public void before() throws URISyntaxException {
-		capabilities1 = new Capabilities(Sets
-				.newSet(new ProtocolRef(new URI("ws:localhost/protocol1"))));
-		capabilities1a = new Capabilities(Sets
-				.newSet(new ProtocolRef(new URI("ws:localhost/protocol1"))));
-		capabilities2 = new Capabilities(Sets
-				.newSet(new ProtocolRef(new URI("ws:localhost/protocol2"))));
-		capabilities3 = new Capabilities(Sets
-				.newSet(new ProtocolRef(new URI("ws:localhost/protocol3"))));
+		capabilities1 = new Capabilities(Sets.newSet("SAOP"));
+		capabilities1a = new Capabilities(Sets.newSet("SAOP"));
+		capabilities2 = new Capabilities(Sets.newSet("SEB"));
+		capabilities3 = new Capabilities(Sets.newSet("SEB", "SAOP"));
 	}
 
@@ -48,8 +41,7 @@
 	@Override
 	public List<String> getGeneralTestStrings() {
-		return Arrays.asList(
-				"Capabilities.*Protocols=.*ProtocolRef.*protocol1.*",
-				"Capabilities.*Protocols=.*ProtocolRef.*protocol2.*",
-				"Capabilities.*Protocols=.*ProtocolRef.*protocol3.*");
+		return Arrays.asList("Capabilities.*Behaviours=.*SAOP.*",
+				"Capabilities.*Behaviours=.*SEB.*",
+				"Capabilities.*Behaviours=.*SEB.*SAOP.*");
 	}
 
Index: /profile/src/main/java/geniusweb/profile/DefaultPartialOrdering.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/DefaultPartialOrdering.java	(revision 9)
+++ /profile/src/main/java/geniusweb/profile/DefaultPartialOrdering.java	(revision 10)
@@ -26,5 +26,6 @@
  * limited anyway.
  */
-public class DefaultPartialOrdering extends DefaultProfile implements PartialOrdering {
+public class DefaultPartialOrdering extends DefaultProfile
+		implements PartialOrdering {
 
 	/**
@@ -40,10 +41,13 @@
 	 * @param reservationbid
 	 * @param bids
-	 * @param isbetterList   a list of tuples [bid1index, bid2index]. It indicates
-	 *                       that bids[bid1index] isbetterthan bids[bid2index].
+	 * @param isbetterList   a list of tuples [bid1index, bid2index]. It
+	 *                       indicates that bids[bid1index] isbetterthan
+	 *                       bids[bid2index].
 	 */
 	@JsonCreator
-	public DefaultPartialOrdering(@JsonProperty("name") String name, @JsonProperty("domain") Domain domain,
-			@JsonProperty("reservationBid") Bid reservationbid, @JsonProperty("bids") List<Bid> bids,
+	public DefaultPartialOrdering(@JsonProperty("name") String name,
+			@JsonProperty("domain") Domain domain,
+			@JsonProperty("reservationBid") Bid reservationbid,
+			@JsonProperty("bids") List<Bid> bids,
 			@JsonProperty("better") List<List<Integer>> isbetterList) {
 		this(name, domain, reservationbid, makeBidMap(bids, isbetterList));
@@ -54,8 +58,9 @@
 	 * @param domain         the {@link Domain} description
 	 * @param reservationbid the reservation {@link Bid}
-	 * @param isBetterMap    a map with keys = a better bid and value=a less good
-	 *                       bid.
+	 * @param isBetterMap    a map with keys = a better bid and value=a less
+	 *                       good bid.
 	 */
-	public DefaultPartialOrdering(String name, Domain domain, Bid reservationbid, Map<Bid, Set<Bid>> isBetterMap) {
+	public DefaultPartialOrdering(String name, Domain domain,
+			Bid reservationbid, Map<Bid, Set<Bid>> isBetterMap) {
 		super(name, domain, reservationbid);
 		this.isBetter = isBetterMap;
@@ -71,5 +76,6 @@
 	/**
 	 * 
-	 * @return a list with all the bids
+	 * @return a list with all the bids that are referred to, either as better
+	 *         or as worse than another bid
 	 */
 	@JsonGetter
@@ -97,5 +103,6 @@
 			if (isBetter.containsKey(bid)) {
 				for (Bid worsebid : isBetter.get(bid)) {
-					betterlist.add(Arrays.asList(bidslist.indexOf(bid), bidslist.indexOf(worsebid)));
+					betterlist.add(Arrays.asList(bidslist.indexOf(bid),
+							bidslist.indexOf(worsebid)));
 				}
 			}
@@ -107,13 +114,16 @@
 	@Override
 	public String toString() {
-		return "DefaultPartialOrdering[" + getValuesString() + "," + isBetter + "]";
+		return "DefaultPartialOrdering[" + getValuesString() + "," + isBetter
+				+ "]";
 	}
 
-	private static Map<Bid, Set<Bid>> makeBidMap(List<Bid> bids, List<List<Integer>> isBetterList) {
+	private static Map<Bid, Set<Bid>> makeBidMap(List<Bid> bids,
+			List<List<Integer>> isBetterList) {
 		Map<Bid, Set<Bid>> betterMap = new HashMap<>();
 
 		for (List<Integer> tuple : isBetterList) {
 			if (tuple.size() != 2) {
-				throw new IllegalArgumentException("Expected tuple but found " + tuple + "in " + isBetterList);
+				throw new IllegalArgumentException("Expected tuple but found "
+						+ tuple + "in " + isBetterList);
 			}
 			Bid betterbid = bids.get(tuple.get(0));
@@ -133,5 +143,6 @@
 		final int prime = 31;
 		int result = super.hashCode();
-		result = prime * result + ((isBetter == null) ? 0 : isBetter.hashCode());
+		result = prime * result
+				+ ((isBetter == null) ? 0 : isBetter.hashCode());
 		return result;
 	}
Index: /profile/src/main/java/geniusweb/profile/utilityspace/DiscreteValueSetUtilities.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/utilityspace/DiscreteValueSetUtilities.java	(revision 9)
+++ /profile/src/main/java/geniusweb/profile/utilityspace/DiscreteValueSetUtilities.java	(revision 10)
@@ -2,4 +2,5 @@
 
 import java.math.BigDecimal;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -68,4 +69,11 @@
 	}
 
+	/**
+	 * @return copy of the value-utility pair map.
+	 */
+	public Map<DiscreteValue, BigDecimal> getUtilities() {
+		return Collections.unmodifiableMap(valueUtilities);
+	}
+
 	@Override
 	public String isFitting(ValueSet valueset) {
Index: /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditive.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditive.java	(revision 10)
+++ /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditive.java	(revision 10)
@@ -0,0 +1,29 @@
+package geniusweb.profile.utilityspace;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+/**
+ * interface to a Utilityspace that is linear-additive.
+ *
+ */
+public interface LinearAdditive extends UtilitySpace {
+	/**
+	 * @return the map from issue names to valuesetutilities (un-weighted)
+	 */
+	Map<String, ValueSetUtilities> getUtilities();
+
+	/**
+	 * 
+	 * @return the map from issue names to weights. weights sum to 1.
+	 */
+	Map<String, BigDecimal> getWeights();
+
+	/**
+	 * 
+	 * @param issue
+	 * @return the weight of the given issue
+	 */
+	BigDecimal getWeight(String issue);
+
+}
Index: /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java	(revision 9)
+++ /profile/src/main/java/geniusweb/profile/utilityspace/LinearAdditiveUtilitySpace.java	(revision 10)
@@ -25,5 +25,5 @@
  */
 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
-public class LinearAdditiveUtilitySpace implements UtilitySpace {
+public class LinearAdditiveUtilitySpace implements LinearAdditive {
 
 	private final String name;
@@ -131,16 +131,5 @@
 	}
 
-	/**
-	 * 
-	 * @param issue the issue to get weighted util for
-	 * @param value the issue value to use (typically coming from a bid)
-	 * @return weighted util of just the issue value:
-	 *         issueUtilities[issue].utility(value) * issueWeights[issue)
-	 */
-	private BigDecimal util(String issue, Value value) {
-		return issueWeights.get(issue)
-				.multiply(issueUtilities.get(issue).getUtility(value));
-	}
-
+	@Override
 	public BigDecimal getWeight(String issue) {
 		return issueWeights.get(issue);
@@ -220,18 +209,25 @@
 	}
 
-	/**
-	 * @return the map from issue names to valuesetutilities (un-weighted)
-	 */
+	@Override
 	public Map<String, ValueSetUtilities> getUtilities() {
 		return Collections.unmodifiableMap(issueUtilities);
 	}
 
-	/**
-	 * 
-	 * @return the map from issue names to weights. weights sum to 1.
-	 */
+	@Override
 	public Map<String, BigDecimal> getWeights() {
 		return Collections.unmodifiableMap(issueWeights);
 	}
 
+	/**
+	 * 
+	 * @param issue the issue to get weighted util for
+	 * @param value the issue value to use (typically coming from a bid)
+	 * @return weighted util of just the issue value:
+	 *         issueUtilities[issue].utility(value) * issueWeights[issue)
+	 */
+	private BigDecimal util(String issue, Value value) {
+		return issueWeights.get(issue)
+				.multiply(issueUtilities.get(issue).getUtility(value));
+	}
+
 }
Index: /profile/src/main/java/geniusweb/profile/utilityspace/SumOfGroupsUtilitySpace.java
===================================================================
--- /profile/src/main/java/geniusweb/profile/utilityspace/SumOfGroupsUtilitySpace.java	(revision 9)
+++ /profile/src/main/java/geniusweb/profile/utilityspace/SumOfGroupsUtilitySpace.java	(revision 10)
@@ -84,8 +84,8 @@
 	 * grouping.
 	 * 
-	 * @param las the {@link LinearAdditiveUtilitySpace} to be converted/copied.
-	 * 
-	 */
-	public SumOfGroupsUtilitySpace(LinearAdditiveUtilitySpace las) {
+	 * @param las the {@link LinearAdditive} to be converted/copied.
+	 * 
+	 */
+	public SumOfGroupsUtilitySpace(LinearAdditive las) {
 		this(las.getDomain(), las.getName(), las2parts(las),
 				las.getReservationBid());
@@ -212,10 +212,10 @@
 	/**
 	 * 
-	 * @param las a {@link LinearAdditiveUtilitySpace}
+	 * @param las a {@link LinearAdditive}
 	 * @return a Map with partname-PartsUtilities. The partnames are identical
 	 *         to the issues in the given las.
 	 */
 	private static HashMap<String, PartsUtilities> las2parts(
-			LinearAdditiveUtilitySpace las) {
+			LinearAdditive las) {
 
 		HashMap<String, PartsUtilities> map = new HashMap<>();
Index: /profile/src/test/java/geniusweb/profile/utilityspace/NumberValueSetUtilTest.java
===================================================================
--- /profile/src/test/java/geniusweb/profile/utilityspace/NumberValueSetUtilTest.java	(revision 9)
+++ /profile/src/test/java/geniusweb/profile/utilityspace/NumberValueSetUtilTest.java	(revision 10)
@@ -3,4 +3,5 @@
 import static org.junit.Assert.assertEquals;
 
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.Arrays;
@@ -8,4 +9,9 @@
 
 import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 import geniusweb.issuevalue.DiscreteValue;
@@ -35,4 +41,7 @@
 			lowval1, low1util, highvalb, highutil);
 	private static final BigDecimal ZERO_1 = new BigDecimal("0.1");
+	private final String utilstring = "{\"numberutils\":{\"lowValue\":12.5,\"lowUtility\":0.5,\"highValue\":18.5,\"highUtility\":0.9}}";
+
+	private static final ObjectMapper jackson = new ObjectMapper();
 
 	@Override
@@ -173,3 +182,20 @@
 	}
 
+	@Test
+	public void testSerialize() throws JsonProcessingException {
+
+		String json = jackson.writeValueAsString(valueset1);
+		System.out.println(json);
+		assertEquals(utilstring, json);
+	}
+
+	@Test
+	public void testDeserialize()
+			throws JsonParseException, JsonMappingException, IOException {
+		ValueSetUtilities valueutils = jackson.readValue(utilstring,
+				ValueSetUtilities.class);
+		assertEquals(valueset1, valueutils);
+
+	}
+
 }
Index: /profileconnection/src/main/java/geniusweb/profileconnection/ProfileInterface.java
===================================================================
--- /profileconnection/src/main/java/geniusweb/profileconnection/ProfileInterface.java	(revision 9)
+++ /profileconnection/src/main/java/geniusweb/profileconnection/ProfileInterface.java	(revision 10)
@@ -1,3 +1,5 @@
 package geniusweb.profileconnection;
+
+import java.io.IOException;
 
 import geniusweb.profile.Profile;
@@ -12,7 +14,6 @@
 	 * @return the latest version of the profile. May change at any time, after
 	 *         someone updates the version on the server. Call to this may block
-	 *         for limited time if the profile is not yet available. May return
-	 *         null if the profile is not available.
-	 */
-	public Profile getProfile();
+	 *         for limited time if the profile is not yet available. 
+	 *         @throws IOException if profile can not be fetched	 */
+	public Profile getProfile() throws IOException;
 }
Index: /profileconnection/src/main/java/geniusweb/profileconnection/WebsocketProfileConnector.java
===================================================================
--- /profileconnection/src/main/java/geniusweb/profileconnection/WebsocketProfileConnector.java	(revision 9)
+++ /profileconnection/src/main/java/geniusweb/profileconnection/WebsocketProfileConnector.java	(revision 10)
@@ -69,5 +69,5 @@
 
 	@Override
-	public Profile getProfile() {
+	public Profile getProfile() throws IOException {
 		int remaining_wait = TIMEOUT_MS;
 		try {
@@ -80,4 +80,7 @@
 					e);
 		}
+		if (profile == null) {
+			throw new IOException("Failed to fetch profile");
+		}
 		return profile;
 	}
Index: /profileconnection/src/test/java/geniusweb/profileconnection/WebsocketProfileConnectorTest.java
===================================================================
--- /profileconnection/src/test/java/geniusweb/profileconnection/WebsocketProfileConnectorTest.java	(revision 9)
+++ /profileconnection/src/test/java/geniusweb/profileconnection/WebsocketProfileConnectorTest.java	(revision 10)
@@ -3,5 +3,4 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
@@ -22,5 +21,4 @@
 
 import geniusweb.profile.Profile;
-import geniusweb.profileconnection.WebsocketProfileConnector;
 import tudelft.utilities.logging.Reporter;
 
@@ -42,10 +40,9 @@
 
 	@Test
-	public void testOnOpen()
+	public void onOpenSmokeTest()
 			throws IOException, DeploymentException, URISyntaxException {
 		WebsocketProfileConnector connector = new WebsocketProfileConnector(
 				new URI("ws://someprofile"), reporter, wscontainer);
 		connector.onOpen(session);
-		assertNull(connector.getProfile());
 	}
 
@@ -93,5 +90,5 @@
 	}
 
-	@Test
+	@Test(expected = IOException.class)
 	public void testGetProfileTimeout()
 			throws DeploymentException, IOException, URISyntaxException {
@@ -116,6 +113,5 @@
 			}
 		}).start();
-		Profile profile = connector.getProfile();
-		assertNull(profile);
+		connector.getProfile();
 	}
 
Index: /protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java	(revision 10)
@@ -64,9 +64,9 @@
 			Map<PartyId, PartyWithProfile> partyprofiles, ProtocolException e) {
 		if (partyprofiles == null) {
-			partyprofiles = Collections.EMPTY_MAP;
+			partyprofiles = Collections.emptyMap();
 		}
 		if (conns == null) {
 			this.connections = new ProtocolToPartyConnections(
-					Collections.EMPTY_LIST);
+					Collections.emptyList());
 		} else {
 			this.connections = conns;
@@ -97,5 +97,4 @@
 
 	/**
-	 * @return
 	 * @return map with {@link PartyWithProfile} for the parties. May be an
 	 *         incomplete map, as more parties with their profiles may be set
Index: /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 10)
@@ -7,4 +7,5 @@
 import geniusweb.protocol.NegoSettings;
 import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.protocol.session.shaop.SHAOPSettings;
 import geniusweb.references.PartyWithProfile;
 import tudelft.utilities.logging.Reporter;
@@ -13,6 +14,6 @@
  * interface for settings for session protocols. Immutable.
  */
-@JsonSubTypes({ @JsonSubTypes.Type(value = SAOPSettings.class) })
-
+@JsonSubTypes({ @JsonSubTypes.Type(value = SAOPSettings.class),
+		@JsonSubTypes.Type(value = SHAOPSettings.class) })
 public interface SessionSettings extends NegoSettings {
 
@@ -22,22 +23,31 @@
 	 * @return list of {@link PartyWithProfile} items.
 	 */
-	public List<PartyWithProfile> getParticipants();
+	List<TeamOfPartiesAndProfiles> getTeams();
 
 	/**
-	 * Allows modification of SessionSettings to include a party. This is needed for
-	 * tournament auto-configuration of sessions.
+	 * Allows modification of SessionSettings to include a party. This is needed
+	 * for tournament auto-configuration of sessions.
 	 * 
-	 * @param partyprof the party with profile to be added
+	 * @param partyprofteam the {@link TeamOfPartiesAndProfiles} to be added
 	 * @return new modified SessionSettings object
 	 */
-	public SessionSettings with(PartyWithProfile partyprof);
+	SessionSettings with(TeamOfPartiesAndProfiles partyprofteam);
+
+	/**
+	 * 
+	 * @return all parties from all teams, as a flattened ordered list. The
+	 *         order : start with all parties from team 1, then all from team 2,
+	 *         etc. The specific order of the parties from a team depends on the
+	 *         protocol.
+	 */
+	List<PartyWithProfile> getAllParties();
 
 	/**
 	 * @param logger the logger where the protocol can log events to.
-	 * @return the an initialized and ready to use {@link SessionProtocol} that can
-	 *         handle this Negotiation.
+	 * @return the an initialized and ready to use {@link SessionProtocol} that
+	 *         can handle this Negotiation.
 	 */
 	@Override
-	public SessionProtocol getProtocol(Reporter logger);
+	SessionProtocol getProtocol(Reporter logger);
 
 }
Index: /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 10)
@@ -0,0 +1,34 @@
+package geniusweb.protocol.session;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+
+import geniusweb.protocol.session.saop.SaopPartyWithProfile;
+import geniusweb.protocol.session.shaop.ShaopTeam;
+import geniusweb.references.PartyWithProfile;
+
+/**
+ * Contains a functional team, eg in SHAOP a team would be a SHAOP together with
+ * his COB partner. In SAOP this is just a SAOP party.
+ * 
+ * This is needed to specify the general SessionSettings functionality in a
+ * sufficient general way
+ *
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+// we do not use JsonTypeInfo because we always use a specific Team within a protocol
+// and do not (de)serialize individual TeamOfPartiesAndProfiles.
+//@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
+@JsonSubTypes({ @JsonSubTypes.Type(value = SaopPartyWithProfile.class),
+		@JsonSubTypes.Type(value = ShaopTeam.class) })
+public interface TeamOfPartiesAndProfiles {
+	/**
+	 * 
+	 * @return the list of all parties involved, which is the parties in all
+	 *         teams combined.
+	 */
+	List<PartyWithProfile> getAllParties();
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java	(revision 10)
@@ -65,8 +65,8 @@
 public class SAOP extends DefaultListenable<ProtocolEvent>
 		implements SessionProtocol {
-	private static final int TIME_MARGIN = 20;// ms extra delay after deadline
-	private static final int MINDURATION = 100;
-	private static final int MIN_SLEEP_TIME = 1000;
-	private static final int MAX_SLEEP_TIME = 60000;
+	public static final int TIME_MARGIN = 20;// ms extra delay after deadline
+	public static final int MINDURATION = 100;
+	public static final int MIN_SLEEP_TIME = 1000;
+	public static final int MAX_SLEEP_TIME = 60000;
 	private static final ProtocolRef SAOP = new ProtocolRef("SAOP");
 	private final Reporter log;
@@ -154,5 +154,5 @@
 			throws InterruptedException, IOException {
 		List<PartyWithProfile> participants = state.getSettings()
-				.getParticipants();
+				.getAllParties();
 		List<Reference> parties = participants.stream()
 				.map(parti -> (parti.getParty().getPartyRef()))
@@ -319,4 +319,6 @@
 			setState(state.with(new ProtocolException(message, party, e)));
 		}
+		log.log(Level.WARNING, "SAOP protocol intercepted error due to party "
+				+ party + ":" + message, e);
 	}
 
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 10)
@@ -4,4 +4,5 @@
 import java.util.LinkedList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -11,9 +12,10 @@
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
 import geniusweb.references.PartyWithProfile;
 import tudelft.utilities.logging.Reporter;
 
 public class SAOPSettings implements SessionSettings {
-	private final List<PartyWithProfile> participants;
+	private final List<SaopPartyWithProfile> participants;
 	private final Deadline deadline;
 
@@ -28,5 +30,5 @@
 	@JsonCreator
 	public SAOPSettings(
-			@JsonProperty("participants") List<PartyWithProfile> participants,
+			@JsonProperty("participants") List<SaopPartyWithProfile> participants,
 			@JsonProperty("deadline") Deadline deadline) {
 		this.participants = participants;
@@ -51,5 +53,5 @@
 
 	@Override
-	public List<PartyWithProfile> getParticipants() {
+	public List<TeamOfPartiesAndProfiles> getTeams() {
 		return Collections.unmodifiableList(participants);
 	}
@@ -60,4 +62,11 @@
 	public Deadline getDeadline() {
 		return deadline;
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return participants.stream()
+				.map(particip -> particip.getAllParties().get(0))
+				.collect(Collectors.toList());
 	}
 
@@ -101,7 +110,11 @@
 
 	@Override
-	public SessionSettings with(PartyWithProfile party) {
-		List<PartyWithProfile> newparts = new LinkedList<>(participants);
-		newparts.add(party);
+	public SessionSettings with(TeamOfPartiesAndProfiles party) {
+		if (!(party instanceof SaopPartyWithProfile))
+			throw new IllegalArgumentException(
+					"Added party must be SaopPartyWithProfile but got "
+							+ party);
+		List<SaopPartyWithProfile> newparts = new LinkedList<>(participants);
+		newparts.add((SaopPartyWithProfile) party);
 		return new SAOPSettings(newparts, deadline);
 	}
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java	(revision 10)
@@ -16,6 +16,6 @@
 import geniusweb.progress.ProgressRounds;
 import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
-import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
 import geniusweb.protocol.session.DefaultSessionState;
 import geniusweb.protocol.session.SessionSettings;
@@ -61,24 +61,4 @@
 
 	/**
-	 * @param actor         the actor that did this action. Can be used to check
-	 *                      if action is valid. NOTICE caller has to make sure
-	 *                      the current state is not final.
-	 * @param action        the action that was proposed by actor.
-	 * @param currentTimeMs the current time in ms since 1970, see
-	 *                      {@link System#currentTimeMillis()}
-	 * @return new SessionState with the action added as last action.
-	 */
-	public SAOPState with(PartyId actor, Action action) {
-		try {
-			return with1(actor, action);
-		} catch (ProtocolException e) {
-			List<Action> newactions = new LinkedList<>(getActions());
-			newactions.add(action);
-			return new SAOPState(newactions, getConnections(), getProgress(),
-					getSettings(), getPartyProfiles(), e);
-		}
-	}
-
-	/**
 	 * 
 	 * @param connection   the new {@link ProtocolToPartyConn}
@@ -164,4 +144,59 @@
 
 	/**
+	 * @param actor         the actor that did this action. Can be used to check
+	 *                      if action is valid. NOTICE caller has to make sure
+	 *                      the current state is not final.
+	 * @param action        the action that was proposed by actor.
+	 * @param currentTimeMs the current time in ms since 1970, see
+	 *                      {@link System#currentTimeMillis()}
+	 * @return new SessionState with the action added as last action.
+	 */
+
+	public SAOPState with(PartyId actor, Action action) {
+
+		if (actor == null) { // this is a bug
+			throw new IllegalArgumentException("actor must not be null");
+		}
+		if (!actor.equals(getNextActor())) {
+			throw new IllegalArgumentException("Party does not have the turn ");
+
+		}
+		if (action == null) {
+			throw new IllegalArgumentException("action is null");
+		}
+		if (!actor.equals(action.getActor())) {
+			throw new IllegalArgumentException(
+					"act contains wrong credentials: " + action);
+		}
+
+		// check protocol is followed for specific actions
+		if (action instanceof Accept) {
+			Bid bid = getLastBid();
+			if (bid == null) {
+				throw new IllegalArgumentException(
+						"Accept without a recent offer");
+			}
+			if (!bid.equals(((Accept) action).getBid())) {
+				throw new IllegalArgumentException(
+						"Party accepts a bid differing from the last offer ="
+								+ bid + ", action=" + action + ")");
+			}
+		} else if (action instanceof Offer) {
+			// offer is always fine. We don't check if bid is actually in domain
+			// or complete etc. We would need domain for that
+		} else if (action instanceof EndNegotiation) {
+			// just act, we will get into the final state then.
+		} else {
+			throw new IllegalArgumentException(
+					"Action " + action + " is not allowed in SAOP");
+		}
+
+		List<Action> newactions = new LinkedList<>(getActions());
+		newactions.add(action);
+		return new SAOPState(newactions, getConnections(), advanceProgress(),
+				getSettings(), getPartyProfiles(), null);
+	}
+
+	/**
 	 * Check up to nparticipants-1 steps back if there was an offer.
 	 * 
@@ -182,54 +217,4 @@
 	}
 
-	private SAOPState with1(PartyId actor, Action action)
-			throws ProtocolException {
-
-		if (actor == null) { // this is a bug
-			throw new IllegalArgumentException("actor must not be null");
-		}
-		if (!actor.equals(getNextActor())) {
-			throw new ProtocolException("Party does not have the turn ",
-					actor.getName());
-
-		}
-		if (action == null) {
-			throw new ProtocolException("action is null", actor.getName());
-		}
-		if (!actor.equals(action.getActor())) {
-			throw new ProtocolException(
-					"act contains wrong credentials: " + action,
-					actor.getName());
-		}
-
-		// check protocol is followed for specific actions
-		if (action instanceof Accept) {
-			Bid bid = getLastBid();
-			if (bid == null) {
-				throw new ProtocolException("Accept without a recent offer",
-						actor.getName());
-			}
-			if (!bid.equals(((Accept) action).getBid())) {
-				throw new ProtocolException(
-						"Party accepts a bid differing from the last offer ="
-								+ bid + ", action=" + action + ")",
-						actor.getName());
-			}
-		} else if (action instanceof Offer) {
-			// offer is always fine. We don't check if bid is actually in domain
-			// or complete etc. We would need domain for that
-		} else if (action instanceof EndNegotiation) {
-			// just act, we will get into the final state then.
-		} else {
-			throw new ProtocolException(
-					"Action " + action + " is not allowed in SAOP",
-					actor.getName());
-		}
-
-		List<Action> newactions = new LinkedList<>(getActions());
-		newactions.add(action);
-		return new SAOPState(newactions, getConnections(), advanceProgress(),
-				getSettings(), getPartyProfiles(), null);
-	}
-
 	/**
 	 * 
Index: /protocol/src/main/java/geniusweb/protocol/session/saop/SaopPartyWithProfile.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/saop/SaopPartyWithProfile.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/saop/SaopPartyWithProfile.java	(revision 10)
@@ -0,0 +1,29 @@
+package geniusweb.protocol.session.saop;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+
+public class SaopPartyWithProfile extends PartyWithProfile
+		implements TeamOfPartiesAndProfiles {
+
+	@JsonCreator
+	public SaopPartyWithProfile(
+			@JsonProperty("party") PartyWithParameters party,
+			@JsonProperty("profile") ProfileRef profile) {
+		super(party, profile);
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return Arrays.asList(this);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java	(revision 10)
@@ -0,0 +1,167 @@
+package geniusweb.protocol.session.shaop;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.progress.Progress;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.references.PartyWithProfile;
+
+/**
+ * The state without all the "with" functioality to make this more readable.
+ * immutable. json-serializable.
+ */
+public abstract class BareSHAOPState implements SessionState {
+	protected final List<Action> actions;
+
+	protected final transient ProtocolToPartyConnections connections;
+
+	protected final Progress progress;
+
+	protected final SHAOPSettings settings;
+
+	protected final ProtocolException error;
+
+	protected final int actorNr;
+
+	protected final Map<PartyId, Integer> partyNumbers;
+
+	protected final Map<PartyId, Double> totalSpent;
+
+	/**
+	 * 
+	 * @param actions    value for actions done so far. null equals to empty
+	 *                   list
+	 * @param conns      the currently existing connections. Can be empty/null.
+	 *                   Each connection represents another party. Normally
+	 *                   there is exactly 1 connection for every party. The
+	 *                   protocol should check this.
+	 * @param progr      the {@link Progress} that governs this session. Can be
+	 *                   null if session did not yet start.
+	 * @param settings   the settings used for the session
+	 * @param e          the exception that occured, usually null. All errors
+	 *                   occuring due to faulty {@link Action}s translate to
+	 *                   {@link ProtocolException}s. All errors in our own code
+	 *                   are bugs (not ProtocolExceptions) and should result in
+	 *                   a throw.
+	 * @param actorNr    the actor that has the turn, index {@link #connections}
+	 * @param partyNrs   a map for each known PartyId to a number. The number is
+	 *                   the index in both {@link SHAOPSettings#getTeams()} and
+	 *                   in {@link #connections}. null is empyt map.
+	 * @param totalSpent total accumulated elicitation costs so far. only SHAOP
+	 *                   parties accumulate costs. null = empty map
+	 */
+	@JsonCreator
+	public BareSHAOPState(List<Action> actions,
+			ProtocolToPartyConnections conns, Progress progr,
+			SHAOPSettings settings, ProtocolException e, int actorNr,
+			Map<PartyId, Integer> partyNrs, Map<PartyId, Double> spent) {
+		if (conns == null) {
+			this.connections = new ProtocolToPartyConnections(
+					Collections.emptyList());
+		} else {
+			this.connections = conns;
+		}
+		if (actions == null) {
+			this.actions = new LinkedList<>();
+		} else {
+			this.actions = actions;
+		}
+		if (partyNrs == null) {
+			this.partyNumbers = Collections.emptyMap();
+		} else {
+			this.partyNumbers = partyNrs;
+		}
+		if (spent == null) {
+			this.totalSpent = Collections.emptyMap();
+		} else {
+			this.totalSpent = spent;
+		}
+
+		if (!connections.allunique()) {
+			throw new IllegalArgumentException(
+					"There can not be multiple connections for a party:"
+							+ conns);
+		}
+
+		this.progress = progr;
+		this.settings = settings;
+		this.error = e;
+		this.actorNr = actorNr;
+	}
+
+	public ProtocolToPartyConnections getConnections() {
+		return connections;
+	}
+
+	/**
+	 * 
+	 * @return party ID of current actorNr
+	 */
+	public PartyId getCurrentActor() {
+		return connections.get(actorNr).getParty();
+	}
+
+	/**
+	 * @param id the {@link PartyId}
+	 * @return the PartyWithProfile associated with that id.
+	 */
+	public PartyWithProfile getPartyProfile(PartyId id) {
+		return settings.getAllParties().get(partyNumbers.get(id));
+	}
+
+	/**
+	 * 
+	 * @return the index of each party
+	 */
+	public Map<PartyId, Integer> getPartyNumbers() {
+		return Collections.unmodifiableMap(partyNumbers);
+	}
+
+	/**
+	 * 
+	 * @return unmodifyable list of actions done so far.
+	 */
+	@Override
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	@Override
+	public Progress getProgress() {
+		return progress;
+	}
+
+	@Override
+	public SessionSettings getSettings() {
+		return settings;
+	}
+
+	@Override
+	public boolean isFinal(long currentTimeMs) {
+		return error != null
+				|| (progress != null && progress.isPastDeadline(currentTimeMs));
+	}
+
+	@Override
+	public ProtocolException getError() {
+		return error;
+	}
+
+	@Override
+	public String toString() {
+		return this.getClass().getSimpleName() + "[" + actions + ","
+				+ connections + "," + progress + "," + settings + "," + error
+				+ "]";
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 10)
@@ -0,0 +1,403 @@
+package geniusweb.protocol.session.shaop;
+
+import java.io.IOException;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.Action;
+import geniusweb.actions.Comparison;
+import geniusweb.actions.ElicitComparison;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.deadline.Deadline;
+import geniusweb.events.ProtocolEvent;
+import geniusweb.party.inform.ActionDone;
+import geniusweb.party.inform.Finished;
+import geniusweb.party.inform.Inform;
+import geniusweb.party.inform.Settings;
+import geniusweb.party.inform.YourTurn;
+import geniusweb.progress.ProgressFactory;
+import geniusweb.protocol.CurrentNegoState;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+import geniusweb.references.Reference;
+import tudelft.utilities.listener.DefaultListenable;
+import tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+/**
+ * 
+ * This is similar to SAOP but there are two types of parties: a SHAOP party and
+ * an a COB party associated with each SHAOP party.
+ * <p>
+ * Each SHAOP must receive a parameter: elicitationcost containing a Double. A
+ * party of this type can execute a RequestComparison(userid) action, which
+ * increases the total bother with the elicitationcost. The call results in the
+ * associated COB party to execute a Comparison action.
+ * <p>
+ * A SHAOP party keeps the turn until it does an action that is not a
+ * {@link ElicitComparison}. A COB party also receives all ActionDone if a party
+ * does an action. The party takes an action only after CompareWithBid is
+ * received. It reacts with a Comparison action.
+ * <p>
+ * The Comparison/ComparisonTest actions are private between the
+ * {@link ShaopTeam}. A SHAOP party can do a {@link ElicitComparison} action
+ * at any time. These actions are completely transparent to the other parties
+ * and are not dependent or influencing the normal turn taking.
+ */
+public class SHAOP extends DefaultListenable<ProtocolEvent>
+		implements SessionProtocol {
+	/**
+	 * Tech note: We can not just extend SAOP because SHAOPState and
+	 * SHAOPSettings do not extend SAOPState and SAOPSettings
+	 */
+	public static final int TIME_MARGIN = 20;// ms extra delay after deadline
+	public static final int MINDURATION = 100;
+	public static final int MIN_SLEEP_TIME = 1000;
+	public static final int MAX_SLEEP_TIME = 60000;
+	private static final ProtocolRef SHAOP = new ProtocolRef("SHAOP");
+	private final Reporter log;
+
+	private SHAOPState state = null; // mutable!
+	private volatile AtomicBoolean isFinishedInfoSent = new AtomicBoolean(
+			false);
+	private volatile Timer deadlinetimer = null;
+	private List<Class> ALLOWED_ACTIONS = Arrays.asList(Offer.class,
+			ElicitComparison.class, Accept.class, EndNegotiation.class);
+
+	/**
+	 * 
+	 * @param state normally the initial state coming from SAOPSettings
+	 */
+	public SHAOP(SHAOPState state, Reporter logger) {
+		if (state == null) {
+			throw new NullPointerException("state must be not null");
+		}
+		if (state.getSettings().getDeadline().getDuration() < MINDURATION) {
+			throw new IllegalArgumentException(
+					"Duration must be at least " + MINDURATION);
+		}
+		this.log = logger;
+		this.state = state;
+	}
+
+	@Override
+	public synchronized void start(
+			ProtocolToPartyConnFactory connectionfactory) {
+
+		try {
+			connect(connectionfactory);
+			setDeadline();
+			setupParties();
+			nextTurn();
+		} catch (Throwable e) {
+			handleError("Failed to start up session", null, e);
+		}
+	}
+
+	@Override
+	public String getDescription() {
+		return "We iterate through all PartiesTuple's, first the shaop party and then the cob party. Each gets "
+				+ "YourTurn in clockwise order, after which they can do their next action. "
+				+ "No new participants after start. End after prescribed deadline or when some bid is unanimously Accepted."
+				+ "Parties can only act on their own behalf and only when it is their turn."
+				+ "COB requests and actions are stored in the actions list but not broadcasted."
+				+ "A SHAOP party keeps the turn until it does a non-COB request (Accept, Offer, EndNegotiation).";
+
+	}
+
+	@Override
+	public void addParticipant(PartyWithProfile party)
+			throws IllegalStateException {
+		throw new IllegalStateException(
+				"Dynamic joining a negotiation is not allowed in SHAOP");
+	}
+
+	@Override
+	public SessionState getState() {
+		return state;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return SHAOP;
+	}
+
+	/*******************************************************************
+	 * private functions. Some are protected only, for testing purposes
+	 ********************************************************************/
+	/**
+	 * step 1 in protocol: connect all involved parties and start the clock.
+	 * This always "succeeds" with a valid (but possibly final) state
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @param connectionfactory the connectionfactory for making party
+	 *                          connections
+	 * 
+	 * @throws InterruptedException if the connection procedure is unterrupted
+	 * 
+	 * @throws IOException          if this fails to properly conect to the
+	 *                              parties, eg interrupted or server not
+	 *                              responding..
+	 */
+	protected synchronized void connect(
+			ProtocolToPartyConnFactory connectionfactory)
+			throws InterruptedException, IOException {
+		List<PartyWithProfile> participants = state.getSettings()
+				.getTeams().stream().map(team -> team.getAllParties())
+				.flatMap(List::stream).collect(Collectors.toList());
+		List<Reference> parties = participants.stream()
+				.map(parti -> (parti.getParty().getPartyRef()))
+				.collect(Collectors.toList());
+		List<ProtocolToPartyConn> connections = null;
+		log.log(Level.INFO, "SHAOP connect " + parties);
+		while (connections == null) {
+			try {
+				connections = connectionfactory.connect(parties);
+			} catch (NoResourcesNowException e) {
+				long waitms = e.getLater().getTime()
+						- System.currentTimeMillis();
+				log.log(Level.INFO,
+						"No resources available to run session, waiting"
+								+ waitms);
+				Thread.sleep(Math.min(MAX_SLEEP_TIME,
+						Math.max(MIN_SLEEP_TIME, waitms)));
+			}
+		}
+		for (int i = 0; i < participants.size(); i++) {
+			setState(this.state.with(connections.get(i)));
+		}
+	}
+
+	/**
+	 * Set state to proper deadline. Starts the timer tasks. This tasks triggers
+	 * a call to handleError when the session times out.
+	 */
+	private synchronized void setDeadline() {
+		long now = System.currentTimeMillis();
+		Deadline deadline = state.getSettings().getDeadline();
+		setState(state.with(ProgressFactory.create(deadline, now)));
+		deadlinetimer = new Timer();
+		TimerTask task = new TimerTask() {
+			@Override
+			public void run() {
+				if (!state.isFinal(System.currentTimeMillis())) {
+					log.log(Level.SEVERE,
+							"BUG. Deadline timer has triggered but state is not final");
+				}
+				log.log(Level.WARNING,
+						"SHAOP deadline reached. Terminating session.");
+				finish();
+			}
+		};
+// set timer TIME_MARGIN after real deadline to ensure we're not too
+// early
+		deadlinetimer.schedule(task, deadline.getDuration() + TIME_MARGIN);
+		log.log(Level.INFO, "SHAOP deadline set to "
+				+ new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
+						.format(new Date(System.currentTimeMillis()
+								+ deadline.getDuration() + TIME_MARGIN)));
+	}
+
+	/**
+	 * step 2 in protocol: listen to connections and send settings to the
+	 * parties.
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @throws ProtocolException if a party does not follow the protocol
+	 */
+	protected synchronized void setupParties() throws ProtocolException {
+		for (ProtocolToPartyConn conn : state.connections) {
+			conn.addListener(action -> actionRequest(conn, action));
+		}
+
+		for (ProtocolToPartyConn connection : state.connections) {
+			try {
+				sendSettings(connection);
+			} catch (IOException e) {
+				throw new ProtocolException("Failed to initialize",
+						connection.getParty().getName(), e);
+			}
+		}
+
+	}
+
+	/**
+	 * Inform a party about its settings
+	 * 
+	 * @param connection
+	 * @throws IOException if party got disconnected
+	 */
+	private synchronized void sendSettings(ProtocolToPartyConn connection)
+			throws IOException {
+		PartyId partyid = connection.getParty();
+		PartyWithProfile settings = state.getPartyProfile(partyid);
+		ProfileRef profile = settings.getProfile();
+		Parameters params = settings.getParty().getParameters();
+		connection.send(new Settings(connection.getParty(), profile, getRef(),
+				state.getProgress(), params));
+	}
+
+	/**
+	 * This is called when one of the party connections does an action.
+	 * Synchronized so that we always handle only 1 action at a time. This is
+	 * also called when a connection closes down, but then with a null action.
+	 * 
+	 * @param partyconn the connection on which the action came in
+	 * @param action    the {@link Action} taken by some party
+	 */
+	protected synchronized void actionRequest(
+			final ProtocolToPartyConn partyconn, final Action action) {
+		try {
+			if (partyconn.getError() != null)
+				throw partyconn.getError();
+			actionRequest1(partyconn, action);
+		} catch (Throwable e) {
+			handleError("failed to handle action " + action,
+					partyconn.getParty().getName(), e);
+		}
+	}
+
+	protected synchronized void actionRequest1(
+			final ProtocolToPartyConn partyconn, final Action action)
+			throws ProtocolException, IOException {
+		PartyId partyid = partyconn.getParty();
+		if (state.isFinal(System.currentTimeMillis())) {
+			return;
+		}
+
+		if (action == null)
+			throw new ProtocolException("Party sent a null action",
+					partyid.getName());
+
+		// check if action allowed
+		if (state.isShaopParty(partyid)) {
+			if (!ALLOWED_ACTIONS.contains(action.getClass())) {
+				throw new ProtocolException(
+						"Illegal action for SHAOP Party:" + action,
+						partyid.getName());
+			}
+		} else {
+			if (!(action instanceof Comparison))
+				throw new ProtocolException(
+						"Illegal action for COB Party:" + action,
+						partyid.getName());
+		}
+
+		// broadcast, except if this is about eliciting comparison.
+		if (action instanceof ElicitComparison
+				|| action instanceof Comparison) {
+			// send actionDone only to the partner
+			setState(state.with(partyconn.getParty(), action));
+			state.connections.get(state.getPartner(partyid))
+					.send(new ActionDone(action));
+		} else {
+			if (!partyconn.getParty().equals(state.getCurrentActor()))
+				throw new ProtocolException(
+						"Party acts without having the turn",
+						partyid.getName());
+			setState(state.with(partyconn.getParty(), action));
+			state.connections.broadcast(new ActionDone(action));
+			if (!state.isFinal(System.currentTimeMillis()))
+				nextTurn();
+		}
+
+	}
+
+	/**
+	 * Signal the current participant it's his turn
+	 * 
+	 * @throws IOException
+	 */
+	private synchronized void nextTurn() {
+		PartyId party = state.getCurrentActor();
+		try {
+			state.getConnections().get(party).send(new YourTurn());
+		} catch (IOException e) {
+			handleError("failed to send YourTurn", party.getName(), e);
+		}
+	}
+
+	/**
+	 * Update state to include the given error and finishes up the session.
+	 * 
+	 * @param message The message to attach to the error
+	 * @param party   the party where the error occured
+	 * @param e       the exception that occured.
+	 */
+	private synchronized void handleError(final String message,
+			final String party, final Throwable e) {
+		log.log(Level.WARNING, "SHAOP protocol intercepted error due to party "
+				+ party + ": " + message, e);
+		if (e instanceof ProtocolException) {
+			setState(state.with((ProtocolException) e));
+		} else {
+			setState(state.with(new ProtocolException(message, party, e)));
+		}
+	}
+
+	/**
+	 * Sets the new state. If the new state is final, the finish-up procedure is
+	 * executed.
+	 * 
+	 * @param newstate the new state.
+	 */
+	private synchronized void setState(SHAOPState newstate) {
+		long now = System.currentTimeMillis();
+		if (state.isFinal(now)) {
+			finish();
+			return;
+		}
+		this.state = newstate;
+		if (newstate.isFinal(now)) {
+			finish();
+		}
+	}
+
+	/**
+	 * Called when we reach final state. Cancels deadline timer. Send finished
+	 * info to all parties, notify current nego state as final and set
+	 * {@link #isFinishedInfoSent}. Double calls are automatically ignored.
+	 */
+	private synchronized void finish() {
+		if (deadlinetimer != null) {
+			deadlinetimer.cancel();
+			deadlinetimer = null;
+		}
+		if (!isFinishedInfoSent.compareAndSet(false, true))
+			return;
+		Inform finished = new Finished(state.getAgreement());
+		state.connections.stream().forEach(conn -> sendFinish(conn, finished));
+		notifyListeners(new CurrentNegoState(state));
+	}
+
+	private void sendFinish(ProtocolToPartyConn connection, Inform finished) {
+		try {
+			connection.send(finished);
+			connection.close();
+		} catch (Exception e) {
+			log.log(Level.INFO, "Failed to send Finished to " + connection, e);
+		}
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPSettings.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPSettings.java	(revision 10)
@@ -0,0 +1,138 @@
+package geniusweb.protocol.session.shaop;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithProfile;
+import tudelft.utilities.logging.Reporter;
+
+public class SHAOPSettings implements SessionSettings {
+	private final List<ShaopTeam> participants;
+	private final Deadline deadline;
+	private final transient List<PartyWithProfile> allParties = new LinkedList<>();
+
+	/**
+	 * @param teams    the list of {@link ShaopTeam}s containing the shaop
+	 *                 parties and their partner cob There must be at least 2 to
+	 *                 run the SHAOP protocol. But SHAOP can be initialized with
+	 *                 less, for use in TournamentSettings.
+	 * @param deadline the deadline settings for the session
+	 */
+	@JsonCreator
+	public SHAOPSettings(@JsonProperty("participants") List<ShaopTeam> teams,
+			@JsonProperty("deadline") Deadline deadline) {
+		if (teams == null)
+			throw new IllegalArgumentException("participants must not be null");
+		if (deadline == null)
+			throw new IllegalArgumentException("deadline must not be null");
+		this.participants = teams;
+		this.deadline = deadline;
+		// notice, order here is crucial because of way #isShaopParty works.
+		for (ShaopTeam tuple : teams) {
+			allParties.add(tuple.getShaop());
+			allParties.add(tuple.getCob());
+		}
+	}
+
+	@Override
+	public Double getMaxRunTime() {
+		return deadline.getDuration() / 1000d;
+	}
+
+	@Override
+	public SessionProtocol getProtocol(Reporter logger) {
+		return new SHAOP(new SHAOPState(this), logger);
+	}
+
+	@Override
+	public List<TeamOfPartiesAndProfiles> getTeams() {
+		// cast needed...
+		return new ArrayList<TeamOfPartiesAndProfiles>(participants);
+	}
+
+	/**
+	 * 
+	 * @param n an index number into the {@link #getTeams()} list.
+	 * @return true iff that participant is a SHAOP party.
+	 */
+	public boolean isShaopParty(int n) {
+		return (n & 1) == 0;
+	}
+
+	/**
+	 * @return the deadline for this negotiation
+	 */
+	public Deadline getDeadline() {
+		return deadline;
+	}
+
+	/**
+	 * 
+	 * @return list of all parties, the even parties (0=first element in list)
+	 *         being the SHAOP party and the next in the list the partner COB
+	 *         party.
+	 */
+	public List<PartyWithProfile> getAllParties() {
+		return participants.stream().map(team -> team.getAllParties())
+				.flatMap(Collection::stream).collect(Collectors.toList());
+	}
+
+	@Override
+	public SessionSettings with(TeamOfPartiesAndProfiles party) {
+		if (!(party instanceof ShaopTeam))
+			throw new IllegalArgumentException(
+					"Added party must be ShaopTeam but got " + party);
+		List<ShaopTeam> newparts = new LinkedList<>(participants);
+		newparts.add((ShaopTeam) party);
+		return new SHAOPSettings(newparts, deadline);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((deadline == null) ? 0 : deadline.hashCode());
+		result = prime * result
+				+ ((participants == null) ? 0 : participants.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SHAOPSettings other = (SHAOPSettings) obj;
+		if (deadline == null) {
+			if (other.deadline != null)
+				return false;
+		} else if (!deadline.equals(other.deadline))
+			return false;
+		if (participants == null) {
+			if (other.participants != null)
+				return false;
+		} else if (!participants.equals(other.participants))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "SHAOPSettings" + participants + "," + deadline + "]";
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java	(revision 10)
@@ -0,0 +1,329 @@
+package geniusweb.protocol.session.shaop;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.Action;
+import geniusweb.actions.Comparison;
+import geniusweb.actions.ElicitComparison;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.references.PartyWithProfile;
+
+/**
+ * Immutable.
+ */
+public class SHAOPState extends BareSHAOPState {
+
+	private static final double DEFAULT_ELICITATATION_COST = 0.1d;
+
+	/**
+	 * see {@link BareSHAOPState}.
+	 */
+	@JsonCreator
+	public SHAOPState(@JsonProperty("actions") List<Action> actions,
+			@JsonProperty("connections") ProtocolToPartyConnections conns,
+			@JsonProperty("progress") Progress progress,
+			@JsonProperty("settings") SHAOPSettings settings,
+			@JsonProperty("error") ProtocolException e,
+			@JsonProperty("actorNr") int actorNr,
+			@JsonProperty("partyNumbers") Map<PartyId, Integer> partytNumbers,
+			@JsonProperty("totalSpent") Map<PartyId, Double> spent) {
+		super(actions, conns, progress, settings, e, actorNr, partytNumbers,
+				spent);
+	}
+
+	/**
+	 * Creates the initial state from the given settings
+	 * 
+	 * @param settings the {@link SHAOPSettings}
+	 */
+	public SHAOPState(SHAOPSettings settings) {
+		this(Collections.emptyList(), null, null, settings, null, 0, null,
+				null);
+	}
+
+	/**
+	 * 
+	 * @param id the {@link PartyId}
+	 * @return the {@link PartyWithProfile} for that party
+	 */
+	public PartyWithProfile getSettings(PartyId id) {
+		return settings.getAllParties().get(partyNumbers.get(id));
+	}
+
+	/**
+	 * 
+	 * @param party
+	 * @return true iff party is a SHAOP party.
+	 */
+	public boolean isShaopParty(PartyId party) {
+		return settings.isShaopParty(partyNumbers.get(party));
+	}
+
+	/**
+	 * 
+	 * @param party a Party Id
+	 * @return the PartyId of the party
+	 */
+	public PartyId getPartner(PartyId party) {
+		return connections.get(partyNumbers.get(party) ^ 1).getParty();
+	}
+
+	@Override
+	public boolean isFinal(long currentTimeMs) {
+		List<Action> acts = getActions();
+		return super.isFinal(currentTimeMs) || getAgreement() != null
+				|| (!acts.isEmpty()
+						&& acts.get(acts.size() - 1) instanceof EndNegotiation);
+	}
+
+	@Override
+	public SHAOPSettings getSettings() {
+		return settings;
+	}
+
+	/**
+	 * search back through the actions for an offer. We can not smarly limit the
+	 * search size because there can be an unlimited number of cob Comparison
+	 * actions anywhere in the list.
+	 * 
+	 * @return Bid from the most recent offer, or null if no such offer
+	 */
+	private Bid getLastBid() {
+		for (int n = actions.size() - 1; n >= 0; n--) {
+			Action action = actions.get(n);
+			if (action instanceof Offer) {
+				return ((Offer) action).getBid();
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 
+	 * @param connection the new {@link ProtocolToPartyConn} to a party
+	 * @return new SessionState with the new connection added. This call ignores
+	 *         the progress (does not check isFinal) because SAOP uses this
+	 *         during the setup where the deadline is not yet relevant. The
+	 *         connection should be informed about the partyprofile but the
+	 *         state currently indicate if that already happened or not.
+	 *         IMPORTANT. The connections MUST be made in the same order as
+	 *         {@link SHAOPSettings#getTeams()}.
+	 */
+	public SHAOPState with(ProtocolToPartyConn connection) {
+		// Only called from the SHAOP initialization phase.
+		ProtocolToPartyConnections newconns = connections.with(connection);
+		Map<PartyId, Integer> newNumbers = new HashMap<>(partyNumbers);
+		newNumbers.put(connection.getParty(), connections.size());
+		return new SHAOPState(actions, newconns, progress, settings, null,
+				actorNr, newNumbers, totalSpent);
+	}
+
+	/**
+	 * 
+	 * @param e             the error that occured
+	 * @param currentTimeMs the current time in ms since 1970, see
+	 *                      {@link System#currentTimeMillis()}
+	 * @return a new state with the error set.
+	 */
+	public SHAOPState with(ProtocolException e) {
+		return new SHAOPState(actions, connections, progress, settings, e,
+				actorNr, partyNumbers, totalSpent);
+	}
+
+	/**
+	 * Sets the progress for this session. call is ignored if progress already
+	 * set.
+	 * 
+	 * @param newprogress the new progress
+	 * @return new SAOPState with the progress set
+	 */
+	public SHAOPState with(Progress newprogress) {
+		if (newprogress == null) {
+			throw new IllegalArgumentException("newprogress must be not null");
+		}
+		if (progress != null) {
+			return this;
+		}
+		return new SHAOPState(actions, connections, newprogress, settings,
+				error, actorNr, partyNumbers, totalSpent);
+	}
+
+	@Override
+	public Bid getAgreement() {
+		List<Action> acts = getActions();
+		if (acts.isEmpty())
+			return null;
+
+		int requiredaccepts = getSettings().getTeams().size() - 1;
+
+		// check that there are requiredaccepts and find the offer before that
+		for (int n = acts.size() - 1; n >= 0; n--) {
+			Action act = acts.get(n);
+			if (act instanceof Comparison || act instanceof ElicitComparison)
+				continue;
+			if (requiredaccepts == 0 && act instanceof Offer)
+				return ((Offer) act).getBid();
+			if (!(act instanceof Accept))
+				return null;
+			requiredaccepts--;
+		}
+		return null;
+	}
+
+	/**
+	 * @param actor         the actor that did this action. Can be used to check
+	 *                      if action is valid. NOTICE caller has to make sure
+	 *                      the current state is not final.
+	 * @param action        the action that was proposed by actor.
+	 * @param currentTimeMs the current time in ms since 1970, see
+	 *                      {@link System#currentTimeMillis()}
+	 * @return new SessionState with the action added as last action.
+	 */
+
+	public SHAOPState with(PartyId actor, Action action)
+			throws IllegalArgumentException {
+
+		if (actor == null) { // this is a bug
+			throw new IllegalArgumentException("actor must not be null");
+		}
+		if (action == null) {
+			throw new IllegalArgumentException("action is null");
+		}
+		if (!actor.equals(action.getActor())) {
+			throw new IllegalArgumentException(
+					"act contains wrong credentials: " + action);
+		}
+
+		if (action instanceof ElicitComparison) {
+			return withShaopInternalAction(actor, (ElicitComparison) action);
+		}
+		if (action instanceof Comparison) {
+			return withCobInternalAction(actor, (Comparison) action);
+		}
+
+		return withShaopRealAction(actor, action);
+
+	}
+
+	private SHAOPState withCobInternalAction(PartyId actor, Comparison action)
+			throws IllegalArgumentException {
+		if (isShaopParty(actor))
+			throw new IllegalArgumentException(
+					"Illegal action, actor is not a cob party:" + action);
+		return withAction(action);
+	}
+
+	/**
+	 * 
+	 * @param actor  actor, supposedly SHAOP, did Elicit action
+	 * @param action an elicit action
+	 * @return new state
+	 * @throws ProtocolException
+	 */
+	private SHAOPState withShaopInternalAction(PartyId actor,
+			ElicitComparison action) throws IllegalArgumentException {
+		if (!isShaopParty(actor))
+			throw new IllegalArgumentException(
+					"Illegal action, actor is not a shaop party:" + action);
+		return withAction(action);
+	}
+
+	/**
+	 * update action, actorNr, and progress
+	 * 
+	 * @param action the action that was done. Elicit actions do not update
+	 *               progress
+	 * @return
+	 */
+	private SHAOPState withAction(Action action) {
+		Progress newprogress = progress;
+		Map<PartyId, Double> newSpent = new HashMap<>(totalSpent);
+		PartyId partyid = action.getActor();
+
+		List<Action> newactions = new LinkedList<>(getActions());
+		newactions.add(action);
+		int newActor = actorNr;
+		if (action instanceof ElicitComparison) {
+			Object cost = getPartyProfile(partyid).getParty().getParameters()
+					.get("elicitationcost");
+			if (!(cost instanceof Double)) {
+				cost = DEFAULT_ELICITATATION_COST;
+			}
+			Double oldSpent = totalSpent.get(partyid);
+			if (oldSpent == null) {
+				oldSpent = 0d;
+			}
+			newSpent.put(partyid, oldSpent + (Double) cost);
+
+		} else if (action instanceof Comparison) {
+			// nothing, turn remains with current SHAOP party
+		} else {
+			// shaop party does real action, move to next SHAOP party.
+			newActor = (actorNr + 2) % settings.getTeams().size();
+			if (newprogress instanceof ProgressRounds) {
+				newprogress = ((ProgressRounds) newprogress).advance();
+			}
+		}
+		return new SHAOPState(newactions, connections, newprogress, settings,
+				null, newActor, partyNumbers, newSpent);
+
+	}
+
+	/**
+	 * party that did a real action
+	 * 
+	 * @param actor
+	 * @param action
+	 * @return new state.
+	 * @throws ProtocolException
+	 */
+	private SHAOPState withShaopRealAction(PartyId actor, Action action)
+			throws IllegalArgumentException {
+		if (!isShaopParty(actor))
+			throw new IllegalArgumentException(
+					"Illegal action for COB party" + action);
+		// real action only allowed if party has the turn.
+		if (!actor.equals(connections.get(actorNr).getParty())) {
+			throw new IllegalArgumentException("Party does not have the turn ");
+		}
+		// check protocol is followed for specific actions
+		if (action instanceof Accept) {
+			Bid bid = getLastBid();
+			if (bid == null) {
+				throw new IllegalArgumentException(
+						"Accept without a recent offer");
+			}
+			if (!bid.equals(((Accept) action).getBid())) {
+				throw new IllegalArgumentException(
+						"Party accepts a bid differing from the last offer ="
+								+ bid + ", action=" + action + ")");
+			}
+		} else if (action instanceof Offer) {
+			// offer is always fine. We don't check if bid is actually in domain
+			// or complete etc. We would need domain for that
+		} else if (action instanceof EndNegotiation) {
+			// just act, we will get into the final state then.
+		} else {
+			throw new IllegalArgumentException(
+					"Action " + action + " is not allowed in SHAOP");
+		}
+		return withAction(action);
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/session/shaop/ShaopTeam.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/session/shaop/ShaopTeam.java	(revision 10)
+++ /protocol/src/main/java/geniusweb/protocol/session/shaop/ShaopTeam.java	(revision 10)
@@ -0,0 +1,93 @@
+package geniusweb.protocol.session.shaop;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Comparison;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithProfile;
+
+/**
+ * A tuple of a SHOAP party and a COB party. Part of the settings.
+ *
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class ShaopTeam implements TeamOfPartiesAndProfiles {
+	private final PartyWithProfile shaop;
+	private final PartyWithProfile cob;
+
+	/**
+	 * @param shaop {@link PartyWithProfile} participants supporting SHAOP
+	 *              behaviour. SHAOP behaviour has parameters:
+	 *              <ul>
+	 *              <li>elicitationcost: the value must be a double number
+	 *              indicating the costs of each RequestComparison action.
+	 *              <li>credit: the credit the party has to start with. The sum
+	 *              of the elicitationcost should stay below this.
+	 *              </ul>
+	 * @param cob   {@link PartyWithProfile} party, must support the COB
+	 *              behaviour. This is the cob partie that the shaop requests
+	 *              for {@link Comparison} will be forwarded to
+	 */
+	@JsonCreator
+	public ShaopTeam(@JsonProperty("shoap") PartyWithProfile shaop,
+			@JsonProperty("cob") PartyWithProfile cob) {
+		this.shaop = shaop;
+		this.cob = cob;
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return Arrays.asList(shaop, cob);
+	}
+
+	@Override
+	public String toString() {
+		return "Parties[" + shaop + "," + cob + "]";
+	}
+
+	public PartyWithProfile getShaop() {
+		return shaop;
+	}
+
+	public PartyWithProfile getCob() {
+		return cob;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((cob == null) ? 0 : cob.hashCode());
+		result = prime * result + ((shaop == null) ? 0 : shaop.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		ShaopTeam other = (ShaopTeam) obj;
+		if (cob == null) {
+			if (other.cob != null)
+				return false;
+		} else if (!cob.equals(other.cob))
+			return false;
+		if (shaop == null) {
+			if (other.shaop != null)
+				return false;
+		} else if (!shaop.equals(other.shaop))
+			return false;
+		return true;
+	}
+
+}
Index: /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocol.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocol.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocol.java	(revision 10)
@@ -81,6 +81,8 @@
 			long now = System.currentTimeMillis();
 			if (sessionstate.isFinal(now)) {
+				// List<TeamOfPartiesAndProfiles> participants =
+				// sessionstate.getSettings().getParticipants()
 				SessionResult result = new SessionResult(
-						sessionstate.getSettings().getParticipants(),
+						sessionstate.getSettings().getAllParties(),
 						sessionstate.getAgreement(), sessionstate.getError());
 				state = state.with(result);
Index: /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 10)
@@ -1,4 +1,6 @@
 package geniusweb.protocol.tournament.allpermutations;
 
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Collections;
 import java.util.List;
@@ -7,7 +9,14 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import geniusweb.profile.Profile;
 import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.protocol.session.saop.SaopPartyWithProfile;
+import geniusweb.protocol.session.shaop.SHAOPSettings;
+import geniusweb.protocol.session.shaop.ShaopTeam;
 import geniusweb.protocol.tournament.TournamentProtocol;
 import geniusweb.protocol.tournament.TournamentSettings;
+import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
 import geniusweb.references.PartyWithParameters;
@@ -30,10 +39,32 @@
  * Takes a number of parties and profiles and makes all permutations of them
  * with the given number of parties and profiles. All profiles must be in the
- * same domain. All sessions are based on the given sessionsettings (can be any
- * session protocol). The parties for each tournament session are added to this
- * basic sessionsettings.
- *
+ * same domain.
+ * <p>
+ * All sessions are based on the given {@link SessionSettings} (can be
+ * {@link SAOPSettings} or {@link SHAOPSettings}). The parties for each
+ * tournament session are added to this basic sessionsettings. sessionsettings
+ * contains basically all the normal run-sessionsettings. This is used as a
+ * "template" for all sessions of the tournament. You can put any use any
+ * session setting here, and each session will be run according to the protocol
+ * you select here.
+ * 
+ * The participants list usually is empty. The AllPermutationsProtocol adds the
+ * the required parties to this list. So if you provide a non-empty list here,
+ * then these parties would be present in every session in the tournament.
+ * <p>
+ * If the tournament is used with a SHAOP sessionsettings, a number of rules are
+ * used to generate the COB party from the settings.
+ * 
+ * The COB party is assumed to be the comparebids-1.0.0 party on the same server
+ * as the party you provide. So if you provide http://server/../party1-1.2.3
+ * then the cob party will be assumed to be at
+ * http://server/../comparebids-1.0.0. The provided parties that you provide are
+ * the SHAOP parties (you can also use SAOP parties). The provided profile is
+ * also given to the SHAOP party. Be aware that you typically need a parameter
+ * like partial=10. The COB party gets the profile as specified, but without the
+ * query part. The COB and SHAOP party are thus a fixed pair.
  */
 public class AllPermutationsSettings implements TournamentSettings {
+
 	private final List<PartyWithParameters> parties;
 	private final List<ProfileRef> profiles;
@@ -49,5 +80,6 @@
 	 *                          we use PermutationsWithoutReturn to create the
 	 *                          parties.
-	 * @param profiles
+	 * @param profiles          list of available {@link Profile}s to be
+	 *                          permutated over the parties
 	 * @param partiesPerSession number of parties per session, must be at least
 	 *                          2.
@@ -97,5 +129,5 @@
 	 */
 	public ImmutableList<SessionSettings> permutations() {
-		ImmutableList<ImmutableList<PartyWithProfile>> partylistlist;
+		ImmutableList<ImmutableList<TeamOfPartiesAndProfiles>> partylistlist;
 		ImmutableList<PartyWithParameters> partieslist = new FixedList<PartyWithParameters>(
 				parties);
@@ -166,7 +198,7 @@
 	 */
 	private SessionSettings createSetting(
-			ImmutableList<PartyWithProfile> partyproflist) {
+			ImmutableList<TeamOfPartiesAndProfiles> partyproflist) {
 		SessionSettings settings = sessionsettings;
-		for (PartyWithProfile partyprof : partyproflist) {
+		for (TeamOfPartiesAndProfiles partyprof : partyproflist) {
 			settings = settings.with(partyprof);
 		}
@@ -181,9 +213,9 @@
 	 * @param drawPartyWithPutback if parties can be drawn multiple times
 	 * @return list of sessionsets where each sessionset contains settings for a
-	 *         session: a list with {@link PartyWithProfile}s. The sessionsets
-	 *         are made by making all combinations of parties and profiles.
-	 *         Profiles are drawn with replace.
-	 */
-	private ImmutableList<ImmutableList<PartyWithProfile>> getParticipants(
+	 *         session: a list with {@link TeamOfPartiesAndProfiles}s. The
+	 *         sessionsets are made by making all combinations of parties and
+	 *         profiles. Profiles are drawn with replace.
+	 */
+	private ImmutableList<ImmutableList<TeamOfPartiesAndProfiles>> getParticipants(
 			ImmutableList<PartyWithParameters> parties,
 			ImmutableList<ProfileRef> profiles, int n,
@@ -205,6 +237,6 @@
 				partiesPermutations, profilesPermutations);
 
-		return new MapList<Tuple<ImmutableList<PartyWithParameters>, ImmutableList<ProfileRef>>, ImmutableList<PartyWithProfile>>(
-				tuple -> partyprofilelist(tuple), tuples);
+		return new MapList<Tuple<ImmutableList<PartyWithParameters>, ImmutableList<ProfileRef>>, ImmutableList<TeamOfPartiesAndProfiles>>(
+				tuple -> teamlist(tuple), tuples);
 	}
 
@@ -213,20 +245,60 @@
 	 * @param tuple a tuple with (1) ImmutableList<PartyRef> (2)
 	 *              ImmutableList<ProfileRef>
-	 * @return list of PartyWithProfile, each picked from the two lists in
-	 *         order.
-	 */
-	private ImmutableList<PartyWithProfile> partyprofilelist(
+	 * @return list of TeamOfPartiesAndProfiles, each picked from the two lists
+	 *         in order.
+	 */
+	private ImmutableList<TeamOfPartiesAndProfiles> teamlist(
 			Tuple<ImmutableList<PartyWithParameters>, ImmutableList<ProfileRef>> tuple) {
-		Function2<PartyWithParameters, ProfileRef, PartyWithProfile> //
-		makeparty = new Function2<PartyWithParameters, ProfileRef, PartyWithProfile>() {
+		Function2<PartyWithParameters, ProfileRef, TeamOfPartiesAndProfiles> //
+		makeparty = new Function2<PartyWithParameters, ProfileRef, TeamOfPartiesAndProfiles>() {
 			@Override
-			public PartyWithProfile apply(PartyWithParameters party,
+			public TeamOfPartiesAndProfiles apply(PartyWithParameters party,
 					ProfileRef profile) {
-				return new PartyWithProfile(party, profile);
+				if (sessionsettings instanceof SAOPSettings)
+					return new SaopPartyWithProfile(party, profile);
+				else if (sessionsettings instanceof SHAOPSettings) {
+					return new ShaopTeam(new PartyWithProfile(party, profile),
+							makeCob(party.getPartyRef(), profile));
+				} else
+					throw new IllegalArgumentException(
+							"Unknown/unsupported protocol "
+									+ sessionsettings.getClass());
 			}
+
 		};
-		return new MapThreadList<PartyWithProfile, PartyWithParameters, ProfileRef>(
+		return new MapThreadList<TeamOfPartiesAndProfiles, PartyWithParameters, ProfileRef>(
 				makeparty, tuple.get1(), tuple.get2());
 	}
 
+	/**
+	 * Extract the raw URL, without the part after the question mark
+	 * 
+	 * @param party   a party reference (for another party)
+	 * @param profile a profile that may have a filter like "?partial=XX"
+	 * @return profile without the filter and with a cob party on the same
+	 *         machine. IT IS ASSUMED that there will the default
+	 *         "comparebids-1.0.0" party on the same machine as where the other
+	 *         party is
+	 */
+	protected static PartyWithProfile makeCob(PartyRef party,
+			ProfileRef profile) {
+		try {
+			URI profileuri = profile.getURI();
+			URI partyuri = party.getURI();
+			ProfileRef cobprof = new ProfileRef(profileuri.getScheme() + "://"
+					+ profileuri.getAuthority() + profileuri.getPath());
+			String partypath = partyuri.getPath();
+			partypath = partypath.substring(0, partypath.lastIndexOf('/'));
+			PartyWithParameters cobparty = new PartyWithParameters(new PartyRef(
+					partyuri.getScheme() + "://" + partyuri.getAuthority()
+							+ partypath + "/comparebids-1.0.0"),
+					new Parameters());
+			return new PartyWithProfile(cobparty, cobprof);
+
+		} catch (URISyntaxException e) {
+			throw new IllegalArgumentException(
+					"Failed making cob party with profile " + profile, e);
+		}
+	}
+
 }
Index: /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java
===================================================================
--- /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java	(revision 9)
+++ /protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java	(revision 10)
@@ -55,5 +55,5 @@
 	 */
 	public AllPermutationsState with(SessionResult result) {
-		if (!getNextSettings().getParticipants()
+		if (!getNextSettings().getAllParties()
 				.equals(result.getParticipants())) {
 			throw new IllegalArgumentException("Inconsistent session result");
Index: /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 9)
+++ /protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 10)
@@ -29,4 +29,5 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
 import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -39,4 +40,5 @@
 	private final ObjectMapper jackson = new ObjectMapper();
 	private DefaultSessionState1 state1, state1a, stateA, stateE, stateF;
+	// The 1 is because we are testing an instance, not the abstract class
 	private String state1string = "{\"DefaultSessionState1\":{\"actions\":[],\"progress\":{\"time\":{\"duration\":1000,\"start\":1000}},\"settings\":{\"SAOPSettings\":{\"participants\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":1000}}}},\"error\":null,\"partyprofiles\":{\"party1\":{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}}}}";
 	private ProgressTime progr;
@@ -58,7 +60,7 @@
 				new PartyRef("party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("profile2");
-		List<PartyWithProfile> participants = Arrays.asList(
-				new PartyWithProfile(party1ref, profile1),
-				new PartyWithProfile(party2ref, profile2));
+		List<SaopPartyWithProfile> participants = Arrays.asList(
+				new SaopPartyWithProfile(party1ref, profile1),
+				new SaopPartyWithProfile(party2ref, profile2));
 		SessionSettings settings = new SAOPSettings(participants, deadline);
 		ProtocolException e = new ProtocolException("test protocol error",
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 9)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 10)
@@ -23,5 +23,4 @@
 import geniusweb.references.PartyRef;
 import geniusweb.references.PartyWithParameters;
-import geniusweb.references.PartyWithProfile;
 import geniusweb.references.ProfileRef;
 import tudelft.utilities.junit.GeneralTests;
@@ -30,10 +29,10 @@
 public class SAOPSettingsTest extends GeneralTests<SAOPSettings> {
 
-	private PartyWithProfile partyprof1 = mock(PartyWithProfile.class);
-	private PartyWithProfile partyprof2 = mock(PartyWithProfile.class);
-	private PartyWithProfile partyprof3 = mock(PartyWithProfile.class);
-	private List<PartyWithProfile> participants2 = Arrays.asList(partyprof1,
+	private SaopPartyWithProfile partyprof1 = mock(SaopPartyWithProfile.class);
+	private SaopPartyWithProfile partyprof2 = mock(SaopPartyWithProfile.class);
+	private SaopPartyWithProfile partyprof3 = mock(SaopPartyWithProfile.class);
+	private List<SaopPartyWithProfile> participants2 = Arrays.asList(partyprof1,
 			partyprof2);
-	private List<PartyWithProfile> participants3 = Arrays.asList(partyprof1,
+	private List<SaopPartyWithProfile> participants3 = Arrays.asList(partyprof1,
 			partyprof2, partyprof3);
 
@@ -67,9 +66,9 @@
 				new PartyRef("http://party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("http://profile2");
-		PartyWithProfile partywithprof1 = new PartyWithProfile(party1,
+		SaopPartyWithProfile partywithprof1 = new SaopPartyWithProfile(party1,
 				profile1);
-		PartyWithProfile partywithprof2 = new PartyWithProfile(party2,
+		SaopPartyWithProfile partywithprof2 = new SaopPartyWithProfile(party2,
 				profile2);
-		List<PartyWithProfile> participants = Arrays.asList(partywithprof1,
+		List<SaopPartyWithProfile> participants = Arrays.asList(partywithprof1,
 				partywithprof2);
 
@@ -141,5 +140,5 @@
 		SAOPSettings saop = new SAOPSettings(participants2, deadline);
 		SessionSettings saop2 = saop.with(partyprof3);
-		assertEquals(3, saop2.getParticipants().size());
+		assertEquals(3, saop2.getTeams().size());
 	}
 
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java	(revision 9)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java	(revision 10)
@@ -27,6 +27,6 @@
 import geniusweb.progress.ProgressTime;
 import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
-import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
 import geniusweb.protocol.session.SessionSettings;
 import tudelft.utilities.junit.GeneralTests;
@@ -220,22 +220,17 @@
 	}
 
-	@Test
+	@Test(expected = IllegalArgumentException.class)
 	public void withWrongActionTest() {
 		// wrong because action has actor null which does not match party1
 		SAOPState state = state1.with(party1, action);
-		assertEquals(1, state.getActions().size());
-		assertEquals(action, state.getActions().get(0));
-		assertTrue(state.isFinal(NOW));
-		assertEquals(
-				"geniusweb.protocol.ProtocolException: party1:act contains wrong credentials: act1",
-				state.getError().toString());
-	}
-
-	@Test
+//		assertEquals(1, state.getActions().size());
+//		assertEquals(action, state.getActions().get(0));
+//		assertTrue(state.isFinal(NOW));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
 	public void withActionTest() {
 		when(action.getActor()).thenReturn(party1);
-		SAOPState state = state1.with(party1, action);
-		assertEquals(1, state.getActions().size());
-		assertEquals(action, state.getActions().get(0));
+		state1.with(party1, action);
 	}
 
@@ -265,15 +260,11 @@
 	}
 
-	@Test
+	@Test(expected = IllegalArgumentException.class)
 	public void RefuseImmediateAccept() {
 		Accept nullaccept = new Accept(party1, null);
 		SAOPState state = state1.with(party1, nullaccept);
-		assertTrue(state.isFinal(NOW));
-		assertEquals(
-				"geniusweb.protocol.ProtocolException: party1:Accept without a recent offer",
-				state.getError().toString());
-	}
-
-	@Test
+	}
+
+	@Test(expected = IllegalArgumentException.class)
 	public void RefuseNotMyTurn() {
 		List<Action> actions = Arrays.asList(offer1);
@@ -281,12 +272,12 @@
 				settings, null, null);
 
-		state = state.with(party1, offer1);
-		assertTrue(state.isFinal(NOW));
-		assertEquals(
-				"geniusweb.protocol.ProtocolException: party1:Party does not have the turn ",
-				state.getError().toString());
-	}
-
-	@Test
+		state.with(party1, offer1);
+//		assertTrue(state.isFinal(NOW));
+//		assertEquals(
+//				"geniusweb.protocol.ProtocolException: party1:Party does not have the turn ",
+//				state.getError().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
 	public void RefuseNullAccept() {
 		List<Action> actions = Arrays.asList(offer1);
@@ -297,9 +288,7 @@
 		state = state.with(party2, nullaccept);
 		assertTrue(state.isFinal(NOW));
-		assertTrue(state.getError().toString().matches(
-				".*party2.*Party accepts a bid differing from the last offer.*"));
-	}
-
-	@Test
+	}
+
+	@Test(expected = IllegalArgumentException.class)
 	public void RefuseNullAction() {
 		List<Action> actions = Arrays.asList(offer1);
@@ -308,7 +297,4 @@
 
 		state = state.with(party2, null);
-		assertEquals(
-				"geniusweb.protocol.ProtocolException: party2:action is null",
-				state.getError().toString());
 	}
 
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 9)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 10)
@@ -20,4 +20,5 @@
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.junit.Test;
@@ -39,7 +40,8 @@
 import geniusweb.progress.Progress;
 import geniusweb.protocol.ProtocolException;
-import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -73,6 +75,6 @@
 	private final PartyRef party2ref = mock(PartyRef.class);
 	private final SAOPSettings settings = mock(SAOPSettings.class);
-	private final PartyWithProfile party1 = mock(PartyWithProfile.class);
-	private final PartyWithProfile party2 = mock(PartyWithProfile.class);
+	private final SaopPartyWithProfile team1 = mock(SaopPartyWithProfile.class);
+	private final SaopPartyWithProfile team2 = mock(SaopPartyWithProfile.class);
 	private SAOP saop;
 	private ProtocolToPartyConnFactory factory;
@@ -80,5 +82,5 @@
 	private ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
 			conn2 = mock(ProtocolToPartyConn.class);
-	private Map<PartyId, PartyWithProfile> partyprofiles;
+	private Map<PartyId, SaopPartyWithProfile> partyprofiles;
 	private Progress progress = mock(Progress.class);
 	private ProfileRef profile1;
@@ -101,17 +103,20 @@
 			throws URISyntaxException, IOException, NoResourcesNowException {
 		SAOP = new ProtocolRef("SAOP");
-		when(party1.getParty()).thenReturn(partywithparam1);
-		when(party2.getParty()).thenReturn(partywithparam2);
+		when(team1.getParty()).thenReturn(partywithparam1);
+		when(team2.getParty()).thenReturn(partywithparam2);
+		when(team1.getAllParties()).thenReturn(Arrays.asList(team1));
+		when(team2.getAllParties()).thenReturn(Arrays.asList(team2));
 
 		partyprofiles = new HashMap<>();
-		partyprofiles.put(PARTY1ID, party1);
-		partyprofiles.put(PARTY2ID, party2);
+		partyprofiles.put(PARTY1ID, team1);
+		partyprofiles.put(PARTY2ID, team2);
 
 		when(deadlinetime.getDuration()).thenReturn(1000l);
 
-		List<PartyWithProfile> participants = new ArrayList<>();
-		participants.add(party1);
-		participants.add(party2);
-		when(settings.getParticipants()).thenReturn(participants);
+		List<TeamOfPartiesAndProfiles> teams = new ArrayList<>();
+		teams.add(team1);
+		teams.add(team2);
+		when(settings.getTeams()).thenReturn(teams);
+		when(settings.getAllParties()).thenReturn(Arrays.asList(team1, team2));
 		when(settings.getDeadline()).thenReturn(deadlinetime);
 
@@ -126,7 +131,7 @@
 
 		profile1 = mock(ProfileRef.class);
-		when(party1.getProfile()).thenReturn(profile1);
+		when(team1.getProfile()).thenReturn(profile1);
 		profile2 = mock(ProfileRef.class);
-		when(party2.getProfile()).thenReturn(profile2);
+		when(team2.getProfile()).thenReturn(profile2);
 
 		mockState(connectedstate, "connected state");
@@ -157,5 +162,7 @@
 		when(state.getSettings()).thenReturn(settings);
 		when(state.getConnections()).thenReturn(connectionswithparties);
-		when(state.getPartyProfiles()).thenReturn(partyprofiles);
+		when(state.getPartyProfiles())
+				.thenReturn(partyprofiles.entrySet().stream().collect(
+						Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
 		when(state.getProgress()).thenReturn(progress);
 		when(state.with(any(ProtocolToPartyConn.class),
Index: /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 10)
+++ /protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 10)
@@ -0,0 +1,110 @@
+package geniusweb.protocol.session.saop;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.ProfileRef;
+import tudelft.utilities.junit.GeneralTests;
+
+public class SaopPartyWithProfileTest
+		extends GeneralTests<SaopPartyWithProfile> {
+
+	private PartyRef party1, party1a, party2, party3;
+	private ProfileRef profile1, profile2;
+	private SaopPartyWithProfile partyprof1, partyprof1a, partyprof2,
+			partyprof3, partyprof4;
+	private final String serialized = "{\"party\":{\"partyref\":\"ws:party1\",\"parameters\":{}},\"profile\":\"ws:profile1\"}";
+
+	@Before
+	public void before() throws URISyntaxException {
+		party1 = new PartyRef(new URI("ws:party1"));
+		party1a = new PartyRef(new URI("ws:party1"));
+		party2 = new PartyRef(new URI("ws:party2"));
+		party3 = new PartyRef(new URI("http:party3"));
+
+		profile1 = new ProfileRef(new URI("ws:profile1"));
+		profile2 = new ProfileRef(new URI("ws:profile2"));
+
+		Parameters settings1 = new Parameters();
+		Parameters settings2 = new Parameters();
+		settings2.put("a", 1);
+
+		PartyWithParameters party1withparams1 = new PartyWithParameters(party1,
+				settings1);
+		PartyWithParameters party1withparams2 = new PartyWithParameters(party1,
+				settings2);
+		PartyWithParameters party2withparams1 = new PartyWithParameters(party2,
+				settings1);
+
+		partyprof1 = new SaopPartyWithProfile(party1withparams1, profile1);
+		partyprof1a = new SaopPartyWithProfile(party1withparams1, profile1);
+		partyprof2 = new SaopPartyWithProfile(party2withparams1, profile1);
+		partyprof3 = new SaopPartyWithProfile(party1withparams1, profile2);
+		partyprof4 = new SaopPartyWithProfile(party1withparams2, profile1);
+
+	}
+
+	@Override
+	public List<List<SaopPartyWithProfile>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(partyprof1, partyprof1a),
+				Arrays.asList(partyprof2), Arrays.asList(partyprof3),
+				Arrays.asList(partyprof4));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"PartyWithProfile.PartyRef.ws:party1.,ProfileRef.ws:profile1.*",
+				"PartyWithProfile.PartyRef.ws:party2.,ProfileRef.ws:profile1.*",
+				"PartyWithProfile.PartyRef.ws:party1.,ProfileRef.ws:profile2.*",
+				"PartyWithProfile.PartyRef.ws:party1.\\{a=1\\},ProfileRef.ws:profile1.*");
+	}
+
+	@Test
+	public void smokeTest() throws URISyntaxException {
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void nullTest() throws URISyntaxException {
+		new SaopPartyWithProfile((PartyWithParameters) null, (ProfileRef) null);
+	}
+
+	@Test
+	public void testSerialize() throws JsonProcessingException {
+		ObjectMapper jackson = new ObjectMapper();
+
+		String json = jackson.writeValueAsString(partyprof1);
+		System.out.println(json);
+		assertEquals(serialized, json);
+	}
+
+	@Test
+	public void testDeserialize()
+			throws JsonParseException, JsonMappingException, IOException {
+		ObjectMapper jackson = new ObjectMapper();
+		// always used as part of bigger class, do not include class name
+		// and therfore can not serialize as TeamOfPartiesAndProfiles
+		TeamOfPartiesAndProfiles p = jackson.readValue(serialized,
+				SaopPartyWithProfile.class);
+		System.out.println(p);
+		assertEquals(partyprof1, p);
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPSettingsTest.java	(revision 10)
+++ /protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPSettingsTest.java	(revision 10)
@@ -0,0 +1,116 @@
+package geniusweb.protocol.session.shaop;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class SHAOPSettingsTest extends GeneralTests<SHAOPSettings> {
+
+	private final ObjectMapper jackson = new ObjectMapper();
+	private Parameters params = new Parameters();
+	private final String serialized = "{\"SHAOPSettings\":{\"participants\":[{\"cob\":{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile1\"},\"shaop\":{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"}},{\"cob\":{\"party\":{\"partyref\":\"party4\",\"parameters\":{}},\"profile\":\"profile1\"},\"shaop\":{\"party\":{\"partyref\":\"party3\",\"parameters\":{}},\"profile\":\"profile1\"}}],\"deadline\":{\"deadlinetime\":{\"durationms\":1000}}}}";
+	private SHAOPSettings settings1, settings1a, settings2, settings3;
+	private ShaopTeam pair1, pair2, pair3;
+
+	@Before
+	public void before() throws URISyntaxException {
+		ProfileRef profile1 = new ProfileRef("profile1");
+		PartyWithParameters party1 = new PartyWithParameters(
+				new PartyRef("party1"), params);
+		PartyWithParameters party2 = new PartyWithParameters(
+				new PartyRef("party2"), params);
+		PartyWithParameters party3 = new PartyWithParameters(
+				new PartyRef("party3"), params);
+		PartyWithParameters party4 = new PartyWithParameters(
+				new PartyRef("party4"), params);
+		PartyWithParameters party5 = new PartyWithParameters(
+				new PartyRef("party5"), params);
+		PartyWithParameters party6 = new PartyWithParameters(
+				new PartyRef("party6"), params);
+
+		PartyWithProfile partyprof1 = new PartyWithProfile(party1, profile1);
+		PartyWithProfile partyprof2 = new PartyWithProfile(party2, profile1);
+		PartyWithProfile partyprof3 = new PartyWithProfile(party3, profile1);
+		PartyWithProfile partyprof4 = new PartyWithProfile(party4, profile1);
+		PartyWithProfile partyprof5 = new PartyWithProfile(party5, profile1);
+		PartyWithProfile partyprof6 = new PartyWithProfile(party6, profile1);
+
+		DeadlineTime deadline = new DeadlineTime(1000);
+		DeadlineTime deadline2 = new DeadlineTime(2000);
+		pair1 = new ShaopTeam(partyprof1, partyprof2);
+		pair2 = new ShaopTeam(partyprof3, partyprof4);
+		pair3 = new ShaopTeam(partyprof5, partyprof6);
+
+		settings1 = new SHAOPSettings(Arrays.asList(pair1, pair2), deadline);
+		settings1a = new SHAOPSettings(Arrays.asList(pair1, pair2), deadline);
+		settings2 = new SHAOPSettings(Arrays.asList(pair1, pair3), deadline);
+		settings3 = new SHAOPSettings(Arrays.asList(pair1, pair2), deadline2);
+	}
+
+	@Override
+	public List<List<SHAOPSettings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings1, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"SHAOPSettings.*Parties.*PartyWithProfile.*party1.*party2.*Parties.*party3.*party4.*DeadlineTime.*1000.*",
+				"SHAOPSettings.*Parties.*PartyWithProfile.*party1.*party2.*Parties.*party5.*party6.*DeadlineTime.*1000.*",
+				"SHAOPSettings.*Parties.*PartyWithProfile.*party1.*party2.*Parties.*party3.*party4.*DeadlineTime.*2000.*");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("SHAOP", settings1.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void constructorNoDeadlineTest() {
+		new SHAOPSettings(Arrays.asList(pair1, pair2), null);
+	}
+
+	@Test
+	public void testMaxRuntime() {
+		assertEquals((Double) 1.000, settings1.getMaxRunTime());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		SessionSettings obj = jackson.readValue(serialized,
+				SessionSettings.class);
+		System.out.println(obj);
+		assertEquals(settings1, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(settings1);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java	(revision 10)
+++ /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java	(revision 10)
@@ -0,0 +1,179 @@
+package geniusweb.protocol.tournament.allpermutations;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import geniusweb.events.ProtocolEvent;
+import geniusweb.issuevalue.Bid;
+import geniusweb.protocol.CurrentNegoState;
+import geniusweb.protocol.NegoState;
+import geniusweb.protocol.SessionResult;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProtocolRef;
+import tudelft.utilities.listener.Listener;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class AllPermutationsProtocolTest {
+
+	private final AllPermutationsState state = mock(AllPermutationsState.class),
+			newstate = mock(AllPermutationsState.class);
+	private final ReportToLogger log = new ReportToLogger("test");
+	private final AllPermutationsProtocol app = new AllPermutationsProtocol(
+			state, log);
+	private final ProtocolToPartyConnFactory factory = mock(
+			ProtocolToPartyConnFactory.class);
+	private final SessionSettings settings = mock(SessionSettings.class);
+	private final SessionState finalsessionstate = mock(SessionState.class);
+	private Bid agreement = mock(Bid.class);
+	private final long NOW = 1000;
+
+	@Before
+	public void before() {
+		when(state.getSize()).thenReturn(BigInteger.ZERO);
+		when(finalsessionstate.isFinal(anyLong())).thenReturn(true);
+		when(finalsessionstate.getSettings()).thenReturn(settings);
+		when(finalsessionstate.getAgreement()).thenReturn(agreement);
+		when(state.with(any())).thenReturn(newstate);
+	}
+
+	@Test
+	public void smokeTest() {
+	}
+
+	@Test
+	public void startTestNoSessions() {
+		AllPermutationsProtocol app = new AllPermutationsProtocol(state, log);
+		app.start(factory);
+
+	}
+
+	@Test
+	public void getDescrTest() {
+		assertNotNull(app.getDescription());
+	}
+
+	@Test
+	public void getStateTest() {
+		assertEquals(state, app.getState());
+	}
+
+	@Test
+	public void getRefTest() {
+		assertEquals("APP", app.getRef().getURI().toString());
+	}
+
+	@Test
+	public void runOneSessionTest() throws InterruptedException {
+		MockSessionProtocol sessionprotocol = startSession();
+		sessionprotocol.getListener();
+	}
+
+	@Test
+	public void runOneSessionAndUnhandledEvent() throws InterruptedException {
+		MockSessionProtocol sessionprotocol = startSession();
+		sessionprotocol.getListener().notifyChange(mock(ProtocolEvent.class));
+		verify(state, times(0)).with(any());
+	}
+
+	/**
+	 * Check that a started session correctly handles a session event.
+	 */
+	@Test
+	public void CurrentNegoStateEventTest() throws InterruptedException {
+		MockSessionProtocol sessionprotocol = startSession();
+
+		CurrentNegoState evt = mock(CurrentNegoState.class);
+		when(evt.getState()).thenReturn(finalsessionstate);
+		sessionprotocol.getListener().notifyChange(evt);
+
+		ArgumentCaptor<SessionResult> argument = ArgumentCaptor
+				.forClass(SessionResult.class);
+		verify(state, times(1)).with(argument.capture());
+		assertEquals(agreement, argument.getValue().getAgreement());
+		assertNull(argument.getValue().getError());
+	}
+
+	private MockSessionProtocol startSession() throws InterruptedException {
+		SessionSettings setting = mock(SessionSettings.class);
+		MockSessionProtocol sessionprotocol = new MockSessionProtocol();
+		when(setting.getProtocol(log)).thenReturn(sessionprotocol);
+		when(state.getNextSettings()).thenReturn(setting);
+
+		app.start(factory);
+		Thread.sleep(500); // allow app to start session
+		// this call should return fine, and have listener registered to our
+		// protocol. app now waits for a notification
+		return sessionprotocol;
+	}
+
+}
+
+/**
+ * 
+ * The protocol needs to record the listners, we need a listener to trigger
+ * further protocol actions. Mockito can't handle this, we mneed our own mock...
+ *
+ */
+class MockSessionProtocol implements SessionProtocol {
+	private final List<Listener<ProtocolEvent>> listeners = new LinkedList<>();
+
+	@Override
+	public void start(ProtocolToPartyConnFactory connectionfactory) {
+
+	}
+
+	@Override
+	public String getDescription() {
+		return null;
+	}
+
+	@Override
+	public NegoState getState() {
+		return null;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return null;
+	}
+
+	@Override
+	public void addParticipant(PartyWithProfile party) {
+	}
+
+	@Override
+	public void addListener(Listener<ProtocolEvent> l) {
+		listeners.add(l);
+	}
+
+	@Override
+	public void removeListener(Listener<ProtocolEvent> l) {
+	}
+
+	public Listener<ProtocolEvent> getListener() {
+		if (listeners.size() != 1) {
+			throw new IllegalStateException("Not having exactly 1 listener");
+		}
+		return listeners.get(0);
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettingsTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettingsTest.java	(revision 10)
+++ /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettingsTest.java	(revision 10)
@@ -0,0 +1,197 @@
+package geniusweb.protocol.tournament.allpermutations;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.NegoSettings;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.protocol.tournament.TournamentSettings;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class AllPermutationsSettingsTest
+		extends GeneralTests<AllPermutationsSettings> {
+
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private final String serialized = "{\"AllPermutationsSettings\":{\"parties\":[{\"partyref\":\"party1\",\"parameters\":{}},{\"partyref\":\"party2\",\"parameters\":{}}],\"reuseParties\":false,\"profiles\":[\"profile1\",\"profile2\",\"profile3\"],\"partiesPerSession\":2,\"sessionsettings\":{\"SAOPSettings\":{\"participants\":[],\"deadline\":{\"deadlinetime\":{\"durationms\":10000}}}}}}";
+	private AllPermutationsSettings settings, settings1a, settings2, settings3,
+			settings4;
+
+	private SAOPSettings saopsettings;
+
+	private ProfileRef profile1, profile2, profile3;
+
+	private List<PartyWithParameters> parties;
+
+	@Before
+	public void before() throws URISyntaxException {
+		PartyRef partyref1 = new PartyRef("party1");
+		PartyRef partyref2 = new PartyRef("party2");
+		PartyRef partyref3 = new PartyRef("party3");
+
+		Parameters params1 = new Parameters();
+		Parameters params2 = new Parameters();
+		Parameters params3 = new Parameters();
+
+		PartyWithParameters partywithparams1 = new PartyWithParameters(
+				partyref1, params1);
+		PartyWithParameters partywithparams2 = new PartyWithParameters(
+				partyref2, params2);
+		PartyWithParameters partywithparams3 = new PartyWithParameters(
+				partyref3, params3);
+
+		parties = Arrays.asList(partywithparams1, partywithparams2);
+		DeadlineTime deadline = new DeadlineTime(10000);
+
+		saopsettings = new SAOPSettings(Collections.emptyList(), deadline);
+
+		profile1 = new ProfileRef("profile1");
+		profile2 = new ProfileRef("profile2");
+		profile3 = new ProfileRef("profile3");
+
+		List<ProfileRef> profiles = Arrays.asList(profile1, profile2, profile3);
+
+		settings = new AllPermutationsSettings(parties, false, profiles, 2,
+				saopsettings);
+
+		settings1a = new AllPermutationsSettings(parties, false, profiles, 2,
+				saopsettings);
+
+		settings2 = new AllPermutationsSettings(Arrays.asList(partywithparams1,
+				partywithparams2, partywithparams3), false, profiles, 2,
+				saopsettings);
+
+		settings3 = new AllPermutationsSettings(parties, true, profiles, 2,
+				saopsettings);
+
+		settings4 = new AllPermutationsSettings(parties, false,
+				Arrays.asList(profile1, profile2, profile3, profile1), 2,
+				saopsettings);
+
+	}
+
+	@Override
+	public List<List<AllPermutationsSettings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3),
+				Arrays.asList(settings4));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"AllPermutationsSettings.*party1.*party2.*,false,.*profile1.*profile2.*profile3.*,2,SAOPSettings.*",
+				"AllPermutationsSettings.*party1.*party2.*party3.*,false,.*profile1.*profile2.*profile3.*,2,SAOPSettings.*",
+				"AllPermutationsSettings.*party1.*party2.*,true,.*profile1.*profile2.*profile3.*,2,SAOPSettings.*",
+				"AllPermutationsSettings.*party1.*party2.*,false,.*profile1.*profile2.*profile3.*profile1.*,2,SAOPSettings.*");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("APP", settings.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		TournamentSettings obj = (TournamentSettings) jackson
+				.readValue(serialized, NegoSettings.class);
+		System.out.println(obj);
+		assertEquals(settings, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(settings);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNullParties() {
+		new AllPermutationsSettings(null, true,
+				Arrays.asList(profile1, profile2, profile3, profile1), 2,
+				saopsettings);
+
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNoParties() {
+		new AllPermutationsSettings(Collections.emptyList(), true,
+				Arrays.asList(profile1, profile2, profile3, profile1), 2,
+				saopsettings);
+
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNullProfiles() {
+		new AllPermutationsSettings(parties, true, null, 2, saopsettings);
+
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testInsufficientProfiles() {
+		new AllPermutationsSettings(parties, true,
+				Arrays.asList(profile1, profile2, profile3, profile1), 4,
+				saopsettings);
+
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testNullSessionSettings() {
+		new AllPermutationsSettings(parties, true,
+				Arrays.asList(profile1, profile2, profile3, profile1), 4, null);
+
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testOnePartyPerSession() {
+		new AllPermutationsSettings(parties, true,
+				Arrays.asList(profile1, profile2, profile3, profile1), 1,
+				saopsettings);
+
+	}
+
+	@Test
+	public void getMaxRuntimeTest() {
+		assertEquals(BigInteger.valueOf(6), settings.permutations().size());
+		assertEquals((Double) (6d * 12), settings.getMaxRunTime());
+	}
+
+	@Test
+	public void makeCobTest() throws URISyntaxException {
+		// Check that the cob party gets profile without the query part
+		String profilebase = "ws://1.2.3.4:8080/profilesserver-a.b.c/websocket/get/someprofile";
+		String partybase = "http://131.180.202.213:8080/partiesserver/run/";
+		String query = "?a=2&partial=4";
+		PartyWithProfile partyprofile = AllPermutationsSettings.makeCob(
+				new PartyRef(partybase + "simpleshaop-1.0.0"),
+				new ProfileRef(profilebase + query));
+		assertEquals(profilebase,
+				partyprofile.getProfile().getURI().toString());
+		assertEquals(partybase + "comparebids-1.0.0",
+				partyprofile.getParty().getPartyRef().getURI().toString());
+	}
+
+}
Index: /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java
===================================================================
--- /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java	(revision 10)
+++ /protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java	(revision 10)
@@ -0,0 +1,95 @@
+package geniusweb.protocol.tournament.allpermutations;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import geniusweb.protocol.SessionResult;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.references.PartyWithProfile;
+import tudelft.utilities.immutablelist.ImmutableList;
+
+public class AllPermutationsStateTest {
+
+	private final AllPermutationsSettings toursettings = mock(
+			AllPermutationsSettings.class);;
+	@SuppressWarnings("unchecked")
+	private final List<SessionResult> results = mock(List.class);
+	@SuppressWarnings("unchecked")
+	private final ImmutableList<SessionSettings> settings = mock(
+			ImmutableList.class);
+	private final AllPermutationsState state = new AllPermutationsState(
+			toursettings, settings, results);
+	private final SessionSettings session7settings = mock(
+			SessionSettings.class);
+	private final long NOW = 1000;
+
+	@SuppressWarnings("unchecked")
+	private final List<PartyWithProfile> participants = mock(List.class);
+
+	@Test
+	public void getResultsTest() {
+		assertEquals(results, state.getResults());
+	}
+
+	@Test
+	public void isFinalTestFalse() {
+		when(settings.size()).thenReturn(BigInteger.TEN);
+		when(results.size()).thenReturn(11);
+		assertFalse(state.isFinal(NOW));
+	}
+
+	@Test
+	public void isFinalTestTrue() {
+		when(settings.size()).thenReturn(BigInteger.TEN);
+		when(results.size()).thenReturn(10);
+		assertTrue(state.isFinal(NOW));
+	}
+
+	@Test
+	public void getNextSetting() {
+		prepareNextSettings();
+		assertEquals(session7settings, state.getNextSettings());
+	}
+
+	private void prepareNextSettings() {
+		when(results.size()).thenReturn(7); // result 0..6
+		when(settings.size()).thenReturn(BigInteger.TEN);
+		// when(session7settings.getTeams()).thenReturn(participants);
+		when(session7settings.getAllParties()).thenReturn(participants);
+		when(settings.get(BigInteger.valueOf(7))).thenReturn(session7settings);
+
+	}
+
+	@Test
+	public void withTest() {
+		prepareNextSettings();
+		ArrayList<SessionResult> results1 = new ArrayList<>();
+		for (int n = 0; n < 7; n++) {
+			results1.add(mock(SessionResult.class));
+		}
+		// use state with "real" results... to allow array copy in with
+		AllPermutationsState state = new AllPermutationsState(toursettings,
+				settings, results1);
+
+		when(settings.size()).thenReturn(BigInteger.TEN);
+		when(settings.get(BigInteger.valueOf(7))).thenReturn(session7settings);
+
+		SessionResult result = mock(SessionResult.class);
+		when(result.getParticipants()).thenReturn(participants);
+		AllPermutationsState state1 = state.with(result);
+
+		assertEquals(null, state1.getError());
+		assertEquals(8, state1.getResults().size());
+		assertEquals(toursettings, state1.getSettings());
+
+	}
+}
Index: /references/src/main/java/geniusweb/references/Parameters.java
===================================================================
--- /references/src/main/java/geniusweb/references/Parameters.java	(revision 9)
+++ /references/src/main/java/geniusweb/references/Parameters.java	(revision 10)
@@ -11,3 +11,20 @@
 public class Parameters extends HashMap<String, Object> {
 
+	/**
+	 * Getter with type-check
+	 * 
+	 * @param <T>       the expected type
+	 * @param paramname the parameter name that must be in the map
+	 * @param classType the expected value type for this parameter
+	 * @return object of requested type.
+	 * @throws IllegalArgumentException if the object is not available.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> T get(String paramname, Class<T> classType) {
+		if (!containsKey(paramname)
+				|| !(get(paramname).getClass().isInstance(classType)))
+			throw new IllegalArgumentException(" Missing contain a parameter "
+					+ paramname + " with a " + classType.getName() + " value");
+		return (T) get(paramname);
+	}
 }
Index: /references/src/main/java/geniusweb/references/PartyWithProfile.java
===================================================================
--- /references/src/main/java/geniusweb/references/PartyWithProfile.java	(revision 9)
+++ /references/src/main/java/geniusweb/references/PartyWithProfile.java	(revision 10)
@@ -22,4 +22,8 @@
 	}
 
+	/**
+	 * 
+	 * @return the {@link PartyWithParameters}. never null.
+	 */
 	public PartyWithParameters getParty() {
 		return party;
@@ -27,4 +31,7 @@
 	}
 
+	/**
+	 * @return the profile setting for this party. Never null.
+	 */
 	public ProfileRef getProfile() {
 		return profile;
Index: /simplerunner/pom.xml
===================================================================
--- /simplerunner/pom.xml	(revision 9)
+++ /simplerunner/pom.xml	(revision 10)
@@ -42,4 +42,16 @@
 			<artifactId>randomparty</artifactId>
 			<version>1.1.0</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>geniusweb.exampleparties</groupId>
+			<artifactId>comparebids</artifactId>
+			<version>1.0.0</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>geniusweb.exampleparties</groupId>
+			<artifactId>simpleshaop</artifactId>
+			<version>1.0.0</version>
 			<scope>test</scope>
 		</dependency>
Index: /simplerunner/src/main/java/geniusweb/simplerunner/NegoRunner.java
===================================================================
--- /simplerunner/src/main/java/geniusweb/simplerunner/NegoRunner.java	(revision 9)
+++ /simplerunner/src/main/java/geniusweb/simplerunner/NegoRunner.java	(revision 10)
@@ -14,5 +14,4 @@
 import geniusweb.protocol.NegoSettings;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
-import geniusweb.protocol.session.SessionSettings;
 import tudelft.utilities.logging.ReportToLogger;
 import tudelft.utilities.logging.Reporter;
@@ -72,6 +71,6 @@
 		String serialized = new String(Files.readAllBytes(Paths.get(args[0])),
 				StandardCharsets.UTF_8);
-		SessionSettings settings = jackson.readValue(serialized,
-				SessionSettings.class);
+		NegoSettings settings = jackson.readValue(serialized,
+				NegoSettings.class);
 
 		NegoRunner runner = new NegoRunner(settings,
Index: /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerE2ETest.java
===================================================================
--- /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerE2ETest.java	(revision 9)
+++ /simplerunner/src/test/java/geniusweb/simplerunner/SessionRunnerE2ETest.java	(revision 10)
@@ -16,5 +16,5 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.NegoSettings;
 import tudelft.utilities.logging.ReportToLogger;
 import tudelft.utilities.logging.Reporter;
@@ -34,6 +34,8 @@
 	@Parameters
 	public static Collection<Object[]> data() {
-		return Arrays.asList(
-				new Object[][] { { "src/test/resources/settings.json" }, { "src/test/resources/settings2.json" } });
+		return Arrays
+				.asList(new Object[][] { { "src/test/resources/settings.json" },
+						{ "src/test/resources/settings2.json" },
+						{ "src/test/resources/shaoptoursettings.json" } });
 	}
 
@@ -44,8 +46,12 @@
 	@Before
 	public void before() throws IOException {
-		String serialized = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
-		SessionSettings settings = jackson.readValue(serialized, SessionSettings.class);
+		System.out.println("Running from file " + filename);
+		String serialized = new String(Files.readAllBytes(Paths.get(filename)),
+				StandardCharsets.UTF_8);
+		NegoSettings settings = jackson.readValue(serialized,
+				NegoSettings.class);
 
-		runner = new NegoRunner(settings, new ClassPathConnectionFactory(), logger);
+		runner = new NegoRunner(settings, new ClassPathConnectionFactory(),
+				logger);
 
 	}
Index: /simplerunner/src/test/resources/shaoptoursettings.json
===================================================================
--- /simplerunner/src/test/resources/shaoptoursettings.json	(revision 10)
+++ /simplerunner/src/test/resources/shaoptoursettings.json	(revision 10)
@@ -0,0 +1,34 @@
+{
+	"AllPermutationsSettings": {
+		"parties": [
+			{
+				"partyref": "classpath:geniusweb.exampleparties.simpleshaop.ShaopParty",
+				"parameters": {
+				}
+			},
+			{
+				"partyref": "classpath:geniusweb.exampleparties.randomparty.RandomParty",
+				"parameters": {
+				}
+			}
+		],
+		"reuseParties": false,
+		"profiles": [
+			"file:src/test/resources/jobs/jobs1partial.json?partial=10",
+			"file:src/test/resources/jobs/jobs2.json?partial=15"
+		],
+		"partiesPerSession": 2,
+		"sessionsettings": {
+			"SHAOPSettings": {
+				"participants": [
+				],
+				"deadline": {
+					"deadlinerounds": {
+						"rounds": 10,
+						"durationms": 10000
+					}
+				}
+			}
+		}
+	}
+}
