// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/Commitment.java
// public class Commitment {
// 
//     public enum Decision {
//         AGREE_AND_NOTIFY,  // Query and Subscribe
//         AGREE_SILENTLY,    // Query only
//         REFUSE             // Query and Subscribe
//     }
// 
//     private final Decision decision;
//     private final ErrorExplanation explanation;
// 
//     public Commitment(final Decision decision,
//                       final ErrorExplanation explanation) {
//         this.decision = decision;
//         this.explanation = explanation;
//     }
// 
//     public Decision getDecision() {
//         return decision;
//     }
// 
//     public ErrorExplanation getExplanation() {
//         return explanation;
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/LocalFailure.java
// public class LocalFailure extends Exception {
//     public LocalFailure(final Throwable cause) {
//         super(cause);
//     }
// 
//     public LocalFailure(final String message) {
//         super(message);
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/Role.java
// public abstract class Role {
//     protected final RDFAgent agent;
// 
//     public Role(final RDFAgent agent) {
//         this.agent = agent;
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/model/AgentId.java
// public class AgentId {
//     private final String name;
//     private final IRI iri;
//     private final IRI[] transportAddresses;
// 
//     public AgentId(final String name,
//                    final String... transportAddresses) {
//         this.name = name;
// 
//         this.iri = iriFromName(name);
// 
//         this.transportAddresses = new IRI[transportAddresses.length];
//         for (int i = 0; i < transportAddresses.length; i++) {
//             this.transportAddresses[i] = SimpleValueFactory.getInstance().createIRI(transportAddresses[i]);
//         }
//     }
// 
//     public AgentId(final IRI iri,
//                    final IRI[] transportAddresses) {
//         this.iri = iri;
//         this.name = nameFromIri(iri);
//         this.transportAddresses = transportAddresses;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public IRI getIri() {
//         return iri;
//     }
// 
//     public IRI[] getTransportAddresses() {
//         return transportAddresses;
//     }
// 
//     private static IRI iriFromName(String name) {
//         // attempt to make the name into an IRI if it isn't one already.
//         // It is not always possible to pass a valid IRI as an agent identifier.
//         String iriStr = name;
//         if (!iriStr.startsWith("http://") && !iriStr.startsWith("urn:")) {
//             iriStr = "urn:" + iriStr;
//         }
// 
//         return SimpleValueFactory.getInstance().createIRI(iriStr);
//     }
// 
//     private static String nameFromIri(final IRI iri) {
//         String s = iri.stringValue();
//         return s.startsWith("http://") ? s.substring(7) : s.startsWith("urn:") ? s.substring(4) : s;
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/model/RDFAgent.java
// public abstract class RDFAgent {
//     protected final AgentId identity;
// 
//     public RDFAgent(final RDFAgentsPlatform platform,
//                     final AgentId id) throws RDFAgentException {
// 
//         if (0 == id.getTransportAddresses().length) {
//             throw new IllegalArgumentException("at least one transport address must be specified");
//         }
// 
//         identity = id;
//     }
// 
//     public AgentId getIdentity() {
//         return identity;
//     }
// 
//     public abstract void setQueryProvider(QueryProvider<Value, Dataset> queryProvider);
// 
//     public abstract void setPubsubProvider(PubsubProvider<Value, Dataset> pubsubProvider);
// 
//     public static class RDFAgentException extends Exception {
//         public RDFAgentException(final Throwable cause) {
//             super(cause);
//         }
//     }
// 
//     // TODO: remove/shutdown method
// }


import net.fortytwo.rdfagents.messaging.Commitment;
import net.fortytwo.rdfagents.messaging.LocalFailure;
import net.fortytwo.rdfagents.messaging.Role;
import net.fortytwo.rdfagents.model.AgentId;
import net.fortytwo.rdfagents.model.RDFAgent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
                synchronized (mutex) {
                    Set<String> ids = idsByTopic.get(topic);
                    if (null == ids) {
                        ids = new HashSet<>();
                        idsByTopic.put(topic, ids);
                    }
                    ids.add(conversationId);

                    Subscription s = new Subscription(initiator, topic, handler);
                    subscriptionsById.put(conversationId, s);
                }

                return c;
            case AGREE_SILENTLY:
                throw new LocalFailure("agreeing to a subscription without confirmation is not supported");
            case REFUSE:
                return c;
            default:
                throw new LocalFailure("unexpected decision: " + c.getDecision());
        }
    }

    /**
     * Renders a decision with respect to a subscription request.
     *
     * @param topic     the topic of the desired stream
     * @param initiator the requester of the subscription
     * @return the commitment of this server towards handling the subscription
     */
    protected abstract Commitment considerSubscriptionRequestInternal(T topic,
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/Commitment.java
// public class Commitment {
// 
//     public enum Decision {
//         AGREE_AND_NOTIFY,  // Query and Subscribe
//         AGREE_SILENTLY,    // Query only
//         REFUSE             // Query and Subscribe
//     }
// 
//     private final Decision decision;
//     private final ErrorExplanation explanation;
// 
//     public Commitment(final Decision decision,
//                       final ErrorExplanation explanation) {
//         this.decision = decision;
//         this.explanation = explanation;
//     }
// 
//     public Decision getDecision() {
//         return decision;
//     }
// 
//     public ErrorExplanation getExplanation() {
//         return explanation;
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/LocalFailure.java
// public class LocalFailure extends Exception {
//     public LocalFailure(final Throwable cause) {
//         super(cause);
//     }
// 
//     public LocalFailure(final String message) {
//         super(message);
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/Role.java
// public abstract class Role {
//     protected final RDFAgent agent;
// 
//     public Role(final RDFAgent agent) {
//         this.agent = agent;
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/model/AgentId.java
// public class AgentId {
//     private final String name;
//     private final IRI iri;
//     private final IRI[] transportAddresses;
// 
//     public AgentId(final String name,
//                    final String... transportAddresses) {
//         this.name = name;
// 
//         this.iri = iriFromName(name);
// 
//         this.transportAddresses = new IRI[transportAddresses.length];
//         for (int i = 0; i < transportAddresses.length; i++) {
//             this.transportAddresses[i] = SimpleValueFactory.getInstance().createIRI(transportAddresses[i]);
//         }
//     }
// 
//     public AgentId(final IRI iri,
//                    final IRI[] transportAddresses) {
//         this.iri = iri;
//         this.name = nameFromIri(iri);
//         this.transportAddresses = transportAddresses;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public IRI getIri() {
//         return iri;
//     }
// 
//     public IRI[] getTransportAddresses() {
//         return transportAddresses;
//     }
// 
//     private static IRI iriFromName(String name) {
//         // attempt to make the name into an IRI if it isn't one already.
//         // It is not always possible to pass a valid IRI as an agent identifier.
//         String iriStr = name;
//         if (!iriStr.startsWith("http://") && !iriStr.startsWith("urn:")) {
//             iriStr = "urn:" + iriStr;
//         }
// 
//         return SimpleValueFactory.getInstance().createIRI(iriStr);
//     }
// 
//     private static String nameFromIri(final IRI iri) {
//         String s = iri.stringValue();
//         return s.startsWith("http://") ? s.substring(7) : s.startsWith("urn:") ? s.substring(4) : s;
//     }
// }
// 
// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/model/RDFAgent.java
// public abstract class RDFAgent {
//     protected final AgentId identity;
// 
//     public RDFAgent(final RDFAgentsPlatform platform,
//                     final AgentId id) throws RDFAgentException {
// 
//         if (0 == id.getTransportAddresses().length) {
//             throw new IllegalArgumentException("at least one transport address must be specified");
//         }
// 
//         identity = id;
//     }
// 
//     public AgentId getIdentity() {
//         return identity;
//     }
// 
//     public abstract void setQueryProvider(QueryProvider<Value, Dataset> queryProvider);
// 
//     public abstract void setPubsubProvider(PubsubProvider<Value, Dataset> pubsubProvider);
// 
//     public static class RDFAgentException extends Exception {
//         public RDFAgentException(final Throwable cause) {
//             super(cause);
//         }
//     }
// 
//     // TODO: remove/shutdown method
// }


// Path: rdfagents-core/src/main/java/net/fortytwo/rdfagents/messaging/subscribe/PubsubProvider.java
import net.fortytwo.rdfagents.messaging.Commitment;
import net.fortytwo.rdfagents.messaging.LocalFailure;
import net.fortytwo.rdfagents.messaging.Role;
import net.fortytwo.rdfagents.model.AgentId;
import net.fortytwo.rdfagents.model.RDFAgent;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

                synchronized (mutex) {
                    Set<String> ids = idsByTopic.get(topic);
                    if (null == ids) {
                        ids = new HashSet<>();
                        idsByTopic.put(topic, ids);
                    }
                    ids.add(conversationId);

                    Subscription s = new Subscription(initiator, topic, handler);
                    subscriptionsById.put(conversationId, s);
                }

                return c;
            case AGREE_SILENTLY:
                throw new LocalFailure("agreeing to a subscription without confirmation is not supported");
            case REFUSE:
                return c;
            default:
                throw new LocalFailure("unexpected decision: " + c.getDecision());
        }
    }

    /**
     * Renders a decision with respect to a subscription request.
     *
     * @param topic     the topic of the desired stream
     * @param initiator the requester of the subscription
     * @return the commitment of this server towards handling the subscription
     */
    protected abstract Commitment considerSubscriptionRequestInternal(T topic,
                                                                      AgentId initiator);