Skip to content

Commit

Permalink
Add more documentation on topics and serdes
Browse files Browse the repository at this point in the history
  • Loading branch information
Superioz committed Feb 4, 2021
1 parent 4e6e373 commit 9e62ca2
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
package dev.volix.rewinside.odyssey.hagrid.serdes;

/**
* A serdes is a serialization and deserialization instance.
*
* @author Tobias Büser
*/
public interface HagridSerdes<T> {

/**
* @return The class of the type this serdes is handling.
*/
Class<T> getType();

/**
* Serializes the payload to a byte array, so that the bytes
* can be send across the network and later on be put together again.
*
* @param payload The payload to serialize.
*
* @return The byte array
*/
byte[] serialize(T payload);

/**
* Deserializes the given {@code data} back to the type class.
*
* @param typeUrl The Java url of the class i.e. {@link Class#getTypeName()}
* @param data The data to be deserialized
*
* @return The instance deserialized out of the {@code data}. Can be null
*/
T deserialize(String typeUrl, byte[] data);

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ public Message deserialize(final String typeUrl, final byte[] data) {

return defaultInstance.getParserForType().parseFrom(data);
} catch (final InvalidProtocolBufferException | ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.volix.rewinside.odyssey.hagrid.serdes;

import java.nio.charset.StandardCharsets;

/**
* @author Tobias Büser
*/
public class StringHagridSerdes implements HagridSerdes<String> {

@Override
public Class<String> getType() {
return String.class;
}

@Override
public byte[] serialize(final String payload) {
return payload.getBytes(StandardCharsets.UTF_8);
}

@Override
public String deserialize(final String typeUrl, final byte[] data) {
return new String(data, StandardCharsets.UTF_8);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,50 @@
import java.util.regex.Pattern;

/**
* Generally speaking a topic is the main part of a pub/sub mechanism. It defines a channel
* where packets can be sent into and <b>multiple</b> clients can listen to
* these packets at the same time.
* <p>
* For Hagrid a topic describes a pattern of multiple subtopics that this topic
* unifies and a {@link HagridSerdes} that handles the serialization and deserialization
* of existing payloads inside the topics.
* <p>
* For the {@link #pattern} we use a regex {@link Pattern} that allows Hagrid to use
* the mechanism explained above.
* <p>
* For example: a topic pattern {@code volix-rewinside-*} contains all topics
* that matches this pattern, e.g. {@code volix-rewinside-odyssey} and
* {@code volix-rewinside-worlds}.
*
* @author Tobias Büser
*/
public class HagridTopic<T> implements Comparable<HagridTopic<?>> {

/**
* The topic pattern used to match subtopics to a topic.
* <p>
* For example: {@code volix-rewinside-odyssey-party-*}
* Important to note is that the aserisk '*' is not allowed
* to be the prefix of the pattern. It has to be in the middle
* or at the end.
*/
private final String pattern;

/**
* The serdes handling the deserializing and serializing
* of the packets in the topic.
*/
private final HagridSerdes<T> serdes;

/**
* Some options regarding the topic.
*/
private final TopicProperties properties;

/**
* The generated regex pattern, so that we don't have to
* generate it on demand but cache it here.
*/
private final Pattern regexPattern;

public HagridTopic(final String pattern, final HagridSerdes<T> serdes, final TopicProperties properties) {
Expand Down Expand Up @@ -43,6 +78,16 @@ public TopicProperties getProperties() {
return this.properties;
}

/**
* Returns given topic pattern as a regex so that the aserisk operator
* '*' is applied correctly.
* Can then be matched against a string to check if another topic
* is a subtopic of given one.
*
* @param topicPattern The pattern.
*
* @return The regex pattern.
*/
public static Pattern getTopicAsRegex(final String topicPattern) {
String regex = topicPattern.replaceAll("-\\*", "-\\\\w+");
if (!regex.endsWith("*")) {
Expand All @@ -54,6 +99,7 @@ public static Pattern getTopicAsRegex(final String topicPattern) {
@Override
public int compareTo(final HagridTopic<?> o) {
if (this.pattern.equalsIgnoreCase(o.pattern)) return 0;
// compares for which is a subtopic of which.

final String t1 = this.pattern.replaceAll("\\*", "any");
final String t2 = o.pattern.replaceAll("\\*", "any");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,25 @@
import java.util.Map;

/**
* The topic group is a wrapper for a sorted list of topics to
* better determine which registered topic fits best for a
* given topic name.
* <p>
* For example if I have a topic {@code volix-party} and I want to know
* which of the registered topics (e.g. {@code volix-*}, {@code volix-party-*}, ..)
* fits best, we can easily use {@link #getMostFitting(String)}.
*
* @author Tobias Büser
*/
public class HagridTopicGroup {

/**
* As every topic begins with a fixed set of alphanumerical characters
* we can determine a prefix from that, so that we can map
* this group to a key.
* <p>
* e.g.: For {@code volix-rewinside-odyssey} the prefix would be {@code volix}.
*/
private final String prefix;

private final Map<String, HagridTopic<?>> topics = new HashMap<>();
Expand All @@ -23,10 +38,23 @@ public HagridTopicGroup(final HagridTopic<?> topic) {
this.add(topic);
}

/**
* @param pattern The topic pattern.
*
* @return A registered topic only, if the given pattern is exactly like
* the pattern the topic got registered with.
*/
public HagridTopic<?> getTopicExactly(final String pattern) {
return this.topics.get(pattern);
}

/**
* @param topicPattern The topic pattern to check
*
* @return A registered topic that matches given pattern the most. This means
* that we do not choose the most abstract, but the less abstract one which
* still matches the topic.
*/
public HagridTopic<?> getMostFitting(final String topicPattern) {
if (this.topics.containsKey(topicPattern)) {
return this.topics.get(topicPattern);
Expand All @@ -49,6 +77,13 @@ public HagridTopic<?> getMostFitting(final String topicPattern) {
return null;
}

/**
* Adds a new topic to the map and sorts an internal list
* with {@link HagridTopic#compareTo(HagridTopic)}.
* If a topic like this already exists, it will get overriden.
*
* @param topic The topic to register
*/
public void add(final HagridTopic<?> topic) {
final boolean contains = this.topics.containsKey(topic.getPattern());
this.topics.put(topic.getPattern(), topic);
Expand All @@ -59,6 +94,12 @@ public void add(final HagridTopic<?> topic) {
}
}

/**
* Removes given topic and sorts the internal list if a topic
* to unregister has been found.
*
* @param topicPattern The pattern of the topic to unregister.
*/
public void remove(final String topicPattern) {
final HagridTopic<?> topic = this.topics.remove(topicPattern);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import dev.volix.rewinside.odyssey.hagrid.serdes.HagridSerdes;

/**
* Represents a registry for {@link HagridTopic}.
*
* @author Tobias Büser
*/
public interface HagridTopicRegistry {
Expand All @@ -13,6 +15,14 @@ default boolean hasTopicGroup(final String prefix) {
return this.getTopicGroup(prefix) != null;
}

/**
* Gets a topic with given pattern.
* If no topic could be found, an error will be thrown.
*
* @param pattern The pattern to search for
*
* @return The topic, will not be null
*/
<T> HagridTopic<T> getTopic(String pattern);

default <T> HagridTopic<T> getTopicOrNull(final String pattern) {
Expand All @@ -27,6 +37,15 @@ default boolean hasTopic(final String pattern) {
return this.getTopic(pattern) != null;
}

/**
* Registers a topic. If it already exists, it will replace the old one.
*
* @param pattern The pattern of the topic
* @param serdes The serdes
* @param properties Some other options regarding the topic
*
* @see HagridTopicGroup#add(HagridTopic)
*/
void registerTopic(String pattern, HagridSerdes<?> serdes, TopicProperties properties);

default void registerTopic(final String pattern, final HagridSerdes<?> serdes) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package dev.volix.rewinside.odyssey.hagrid.topic;

/**
* Some properties to closer define what happens when
* registering and running the topic.
*
* @author Tobias Büser
*/
public class TopicProperties {

/**
* Will be used to determine if the topic should be
* in a seperate thread, so that this topic
* can be consumed in parallel.
* <p>
* It is incumbent upon the implementation to ensure that
* or to throw an error.
*/
private final boolean shouldRunInParallel;

private TopicProperties(final boolean shouldRunInParallel) {
Expand Down

0 comments on commit 9e62ca2

Please sign in to comment.