{"id":5593,"date":"2018-12-02T14:00:25","date_gmt":"2018-12-02T14:00:25","guid":{"rendered":"http:\/\/writeasync.net\/?p=5593"},"modified":"2018-11-25T00:45:38","modified_gmt":"2018-11-25T00:45:38","slug":"a-simple-message-bus-java-edition","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=5593","title":{"rendered":"A simple message bus: Java edition"},"content":{"rendered":"<p>In the <a href=\"writeasync.net\/?p=5586\">previous post<\/a>, we looked at competing message bus implementations in C# and C++. How about we give Java a try now?<\/p>\n<p>Converting to Java syntax and style conventions, the <code>SendOneSubscriber<\/code> test should look like this:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example;\r\n\r\nimport java.util.function.*;\r\nimport java.util.*;\r\nimport org.junit.Test;\r\n\r\nimport static org.hamcrest.Matchers.*;\r\nimport static org.hamcrest.MatcherAssert.assertThat;\r\n\r\npublic class MessageBusTest {\r\n    \/\/ . . .\r\n\r\n    @Test\r\n    public void testSendOneSubscriber() {\r\n        MessageBus bus = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;MyMessage&gt; subscriber = m -&gt; received.add(m.getText());\r\n\r\n        bus.subscribe(subscriber);\r\n        bus.send(new MyMessage(&quot;hello&quot;));\r\n\r\n        assertThat(received, contains(&quot;hello&quot;));\r\n    }\r\n\r\n    \/\/ . . .\r\n}\r\n<\/pre>\n<p>Alas, there is a big problem here. Let&#8217;s first review what the API for <code>MessageBus<\/code> should look like given the test above:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example;\r\n\r\nimport java.util.function.*;\r\n\r\npublic class MessageBus {\r\n    public MessageBus() {\r\n        \/* . . . *\/\r\n    }\r\n\r\n    public &lt;M&gt; void subscribe(Consumer&lt;M&gt; subscriber) {\r\n        \/* . . . *\/\r\n    }\r\n\r\n    public &lt;M&gt; void send(M message) {\r\n        \/* . . . *\/\r\n    }\r\n}\r\n<\/pre>\n<p>Ideally, inside those generic methods we would retrieve the type information about <code>M<\/code> and use this as a lookup key for a <code>HashMap<\/code>. Unfortunately, the implementation of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Generics_in_Java\">Java generics<\/a> is based on <a href=\"https:\/\/en.wikipedia.org\/wiki\/Type_erasure\">type erasure<\/a>. By definition, we have no runtime information available to us about what type <code>M<\/code> actually was in the source code. Techniques such as <a href=\"https:\/\/stackoverflow.com\/questions\/8655921\/use-of-typeliteral-in-java\">TypeLiteral in various Java libraries<\/a> exist to solve related type erasure problems but cannot seem to help us in this instance.<\/p>\n<p>As yet, I have not found a way to make this type of generically typed API work as is. We will simply have to alter the API and force the caller to pass a <a href=\"http:\/\/gafter.blogspot.com\/2006\/12\/super-type-tokens.html\">type token<\/a> as an additional parameter:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example;\r\n\r\nimport java.util.function.*;\r\n\r\npublic class MessageBus {\r\n    public MessageBus() {\r\n        \/* . . . *\/\r\n    }\r\n\r\n    public &lt;M&gt; void subscribe(Consumer&lt;M&gt; subscriber, Class&lt;M&gt; type) {\r\n        \/* . . . *\/\r\n    }\r\n\r\n    public &lt;M&gt; void send(M message, Class&lt;M&gt; type) {\r\n        \/* . . . *\/\r\n    }\r\n}\r\n<\/pre>\n<p>The test would change as follows:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n    \/* . . . *\/\r\n\r\n    @Test\r\n    public void testSendOneSubscriber() {\r\n        MessageBus bus = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;MyMessage&gt; subscriber = m -&gt; received.add(m.getText());\r\n\r\n        bus.subscribe(subscriber, MyMessage.class);\r\n        bus.send(new MyMessage(&quot;hello&quot;), MyMessage.class);\r\n\r\n        assertThat(received, contains(&quot;hello&quot;));\r\n    }\r\n\r\n    \/* . . . *\/\r\n<\/pre>\n<p>It&#8217;s a bit ugly but should be type safe. For example, it is not possible to compile this code:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n        Consumer&lt;String&gt; subscriber = m -&gt; \/* . . . *\/;\r\n\r\n        \/\/ ERROR: The method subscribe(M, Class&lt;M&gt;) in the type MessageBus is not\r\n        \/\/ applicable for the arguments (Consumer&lt;String&gt;, Class&lt;Integer&gt;)\r\n        bus.subscribe(subscriber, Integer.class);\r\n<\/pre>\n<p>Here is the entire test suite translated to Java, using the convenient <a href=\"http:\/\/hamcrest.org\/JavaHamcrest\/tutorial\">Hamcrest matcher objects<\/a> for fluent syntax:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example;\r\n\r\nimport java.util.function.*;\r\nimport java.util.*;\r\nimport org.junit.Test;\r\n\r\nimport static org.hamcrest.Matchers.*;\r\nimport static org.hamcrest.MatcherAssert.assertThat;\r\n\r\npublic class MessageBusTest {\r\n    @Test\r\n    public void testSendZeroSubscribers() {\r\n        MessageBus bus = new MessageBus();\r\n\r\n        boolean threwException = false;\r\n        try {\r\n            bus.send(new MyMessage(&quot;hello&quot;), MyMessage.class);\r\n        } catch (Exception e) {\r\n            threwException = true;\r\n        }\r\n\r\n        assertThat(threwException, is(false));\r\n    }\r\n\r\n    @Test\r\n    public void testSendOneSubscriber() {\r\n        MessageBus bus = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;MyMessage&gt; subscriber = m -&gt; received.add(m.getText());\r\n\r\n        bus.subscribe(subscriber, MyMessage.class);\r\n        bus.send(new MyMessage(&quot;hello&quot;), MyMessage.class);\r\n\r\n        assertThat(received, contains(&quot;hello&quot;));\r\n    }\r\n\r\n    @Test\r\n    public void testSendTwoSubscribers() {\r\n        MessageBus bus = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;MyMessage&gt; subscriber1 = m -&gt; received.add(m.getText() + &quot;1&quot;);\r\n        Consumer&lt;MyMessage&gt; subscriber2 = m -&gt; received.add(m.getText() + &quot;2&quot;);\r\n\r\n        bus.subscribe(subscriber1, MyMessage.class);\r\n        bus.subscribe(subscriber2, MyMessage.class);\r\n        bus.send(new MyMessage(&quot;hello&quot;), MyMessage.class);\r\n\r\n        assertThat(received, contains(&quot;hello1&quot;, &quot;hello2&quot;));\r\n    }\r\n\r\n    @Test\r\n    public void testSendTwoOneSubscriberEach() {\r\n        MessageBus bus = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;MyMessage&gt; subscriber1 = m -&gt; received.add(m.getText() + &quot;1&quot;);\r\n        Consumer&lt;MyOtherMessage&gt; subscriber2 = m -&gt; received.add(m.getText() + &quot;2&quot;);\r\n\r\n        bus.subscribe(subscriber1, MyMessage.class);\r\n        bus.subscribe(subscriber2, MyOtherMessage.class);\r\n        bus.send(new MyMessage(&quot;one-hello&quot;), MyMessage.class);\r\n        bus.send(new MyOtherMessage(&quot;two-hello&quot;), MyOtherMessage.class);\r\n\r\n        assertThat(received, contains(&quot;one-hello1&quot;, &quot;two-hello2&quot;));\r\n    }\r\n\r\n    @Test\r\n    public void testSendTwoSimpleTypesOneSubscriberEach() {\r\n        MessageBus bus = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;String&gt; subscriber1 = m -&gt; received.add(&quot;S=&quot; + m);\r\n        Consumer&lt;Integer&gt; subscriber2 = m -&gt; received.add(&quot;N=&quot; + m);\r\n\r\n        bus.subscribe(subscriber1, String.class);\r\n        bus.subscribe(subscriber2, Integer.class);\r\n        bus.send(&quot;xyz&quot;, String.class);\r\n        bus.send(123, Integer.class);\r\n\r\n        assertThat(received, contains(&quot;S=xyz&quot;, &quot;N=123&quot;));\r\n    }\r\n\r\n    @Test\r\n    public void testSendTwoInstancesThreeSubscribersEachBeforeAndAfter() {\r\n        MessageBus bus1 = new MessageBus();\r\n        MessageBus bus2 = new MessageBus();\r\n        ArrayList&lt;String&gt; received = new ArrayList&lt;String&gt;();\r\n        Consumer&lt;String&gt; subscriber1 = m -&gt; received.add(&quot;S1=&quot; + m);\r\n        Consumer&lt;String&gt; subscriber2 = m -&gt; received.add(&quot;S2=&quot; + m);\r\n        Consumer&lt;String&gt; subscriber3 = m -&gt; received.add(&quot;S3=&quot; + m);\r\n        Consumer&lt;String&gt; subscriber4 = m -&gt; received.add(&quot;S4=&quot; + m);\r\n        Consumer&lt;Integer&gt; subscriber5 = m -&gt; received.add(&quot;S5=&quot; + m);\r\n        Consumer&lt;Integer&gt; subscriber6 = m -&gt; received.add(&quot;S6=&quot; + m);\r\n\r\n        bus1.subscribe(subscriber1, String.class);\r\n        bus2.subscribe(subscriber2, String.class);\r\n        bus1.send(&quot;aaa&quot;, String.class);\r\n        bus2.send(&quot;bbb&quot;, String.class);\r\n        bus1.subscribe(subscriber3, String.class);\r\n        bus2.subscribe(subscriber4, String.class);\r\n        bus1.send(&quot;ccc&quot;, String.class);\r\n        bus2.send(&quot;ddd&quot;, String.class);\r\n        bus1.subscribe(subscriber5, Integer.class);\r\n        bus2.subscribe(subscriber6, Integer.class);\r\n        bus1.send(1, Integer.class);\r\n        bus2.send(2, Integer.class);\r\n\r\n        assertThat(received, contains(&quot;S1=aaa&quot;, &quot;S2=bbb&quot;, &quot;S1=ccc&quot;, &quot;S3=ccc&quot;, &quot;S2=ddd&quot;, &quot;S4=ddd&quot;, &quot;S5=1&quot;, &quot;S6=2&quot;));\r\n    }\r\n\r\n    private class MyMessage {\r\n        private final String text;\r\n\r\n        public MyMessage(String text) {\r\n            this.text = text;\r\n        }\r\n\r\n        public String getText() {\r\n            return this.text;\r\n        }\r\n    }\r\n\r\n    private class MyOtherMessage extends MyMessage {\r\n        public MyOtherMessage(String text) {\r\n            super(text);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>These tests are somewhat closer to the C# implementation than the C++, in large part due to <a href=\"http:\/\/hamcrest.org\/\">Hamcrest<\/a>. There doesn&#8217;t seem to be a nice <a href=\"https:\/\/stackoverflow.com\/questions\/27724660\/how-to-use-hamcrest-in-java-to-test-for-a-exception\">fluent way to assert for exceptions<\/a>, though. FYI, since <a href=\"https:\/\/github.com\/junit-team\/junit4\/blob\/master\/doc\/ReleaseNotes4.11.md#matchers-upgrade-to-hamcrest-13\">JUnit 4.12 includes an older version of Hamcrest<\/a>, I updated my <a href=\"https:\/\/docs.gradle.org\/3.5\/userguide\/java_library_plugin.html\"><code>build.gradle<\/code> file for the <code>java-library<\/code> plugin<\/a> to include Hamcrest 2.0 like so:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndependencies {\r\n    \/\/ . . .\r\n    \/\/ Use JUnit test framework\r\n    testImplementation 'junit:junit:4.12'\r\n    \r\n    \/\/ Use Hamcrest 2.0\r\n    testImplementation('org.hamcrest:java-hamcrest:2.0.0.0')\r\n}\r\n<\/pre>\n<p>Finally, let&#8217;s look at the implementation code:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example;\r\n\r\nimport java.util.*;\r\nimport java.util.function.*;\r\n\r\npublic class MessageBus {\r\n    private final HashMap&lt;Class&lt;?&gt;, ArrayList&lt;Consumer&lt;Object&gt;&gt;&gt; map;\r\n\r\n    public MessageBus() {\r\n        this.map = new HashMap&lt;Class&lt;?&gt;, ArrayList&lt;Consumer&lt;Object&gt;&gt;&gt;();\r\n    }\r\n\r\n    public &lt;M&gt; void subscribe(Consumer&lt;M&gt; subscriber, Class&lt;M&gt; type) {\r\n        ArrayList&lt;Consumer&lt;Object&gt;&gt; list = this.map.get(type);\r\n        if (list == null) {\r\n            list = new ArrayList&lt;Consumer&lt;Object&gt;&gt;();\r\n            this.map.put(type, list);\r\n        }\r\n\r\n        @SuppressWarnings(&quot;unchecked&quot;)\r\n        Consumer&lt;Object&gt; f = m -&gt; subscriber.accept((M) m);\r\n        list.add(f);\r\n    }\r\n\r\n    public &lt;M&gt; void send(M message, Class&lt;M&gt; type) {\r\n        ArrayList&lt;Consumer&lt;Object&gt;&gt; list = this.map.get(type);\r\n        if (list != null) {\r\n            for (Consumer&lt;Object&gt; f : list) {\r\n                f.accept(message);\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Like C++, Java has no multicast delegates, so we&#8217;re back to the &#8220;list of functions&#8221; solution. Other than that, the code is roughly as concise as the C# version. Note the use of the <a href=\"https:\/\/docs.oracle.com\/javase\/tutorial\/java\/generics\/unboundedWildcards.html\">unbounded wildcard type<\/a> <code>Class&lt;?&gt;<\/code> which is roughly equivalent to <code>System.Type<\/code> in the C# solution, and the need to suppress <a href=\"http:\/\/www.angelikalanger.com\/GenericsFAQ\/FAQSections\/TechnicalDetails.html#FAQ001\">the &#8220;unchecked&#8221; warning<\/a> due to the <code>Object<\/code> to <code>M<\/code> cast (we know it is safe here, but due to type erasure, the runtime does not).<\/p>\n<p>Of course, the exercise wouldn&#8217;t be complete without the ported benchmark. Here it is:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example;\r\n\r\nimport java.util.function.*;\r\n\r\npublic class Program {\r\n    public static void main(String&#x5B;] args) {\r\n        benchmark(1);\r\n        benchmark(16);\r\n        benchmark(256);\r\n        benchmark(4096);\r\n        benchmark(65536);\r\n        benchmark(262144);\r\n        benchmark(524288);\r\n        benchmark(524288);\r\n        benchmark(524288);\r\n        benchmark(524288);\r\n        benchmark(524288);\r\n    }\r\n\r\n    private static void benchmark(int iterations) {\r\n        LongWrapper count = new LongWrapper();\r\n        Consumer&lt;String&gt; subscriber = m -&gt; count.increment(m.length());\r\n        MessageBus bus = new MessageBus();\r\n\r\n        bus.subscribe(subscriber, String.class);\r\n\r\n        Consumer&lt;Integer&gt; func = n -&gt; {\r\n            for (int i = 0; i &lt; n; ++i) {\r\n                for (char c = 'A'; c &lt;= 'Z'; ++c) {\r\n                    String msg = new String(c + &quot;&quot;);\r\n                    bus.send(msg, String.class);\r\n                }\r\n            }\r\n        };\r\n\r\n        long start = System.nanoTime();\r\n\r\n        func.accept(iterations);\r\n\r\n        long end = System.nanoTime();\r\n        double nsecPerOp = 1.0 * (end - start) \/ iterations;\r\n\r\n        System.out.printf(&quot;Average operation time: %f ns (var count = %d)\\n&quot;, nsecPerOp, count.get());\r\n    }\r\n\r\n    private static class LongWrapper {\r\n        private long value;\r\n\r\n        public LongWrapper() {\r\n            this.value = 0L;\r\n        }\r\n\r\n        public void increment(long inc) {\r\n            this.value += inc;\r\n        }\r\n\r\n        public long get() {\r\n            return this.value;\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Remember, Java <a href=\"https:\/\/stackoverflow.com\/questions\/430346\/why-doesnt-java-support-unsigned-ints\">doesn&#8217;t have unsigned types<\/a> and <a href=\"https:\/\/stackoverflow.com\/questions\/25055392\/lambdas-local-variables-need-final-instance-variables-dont\">local variables cannot be mutated within a lambda expression<\/a> (necessitating the <code>LongWrapper<\/code> workaround that is <a href=\"https:\/\/stackoverflow.com\/questions\/5438307\/detailed-explanation-of-variable-capture-in-closures\">otherwise done &#8220;for free&#8221; in the .NET case<\/a>). The results:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nAverage operation time: 176769.000000 ns (var count = 26)\r\nAverage operation time: 62644.437500 ns (var count = 416)\r\nAverage operation time: 20396.523438 ns (var count = 6656)\r\nAverage operation time: 8717.085205 ns (var count = 106496)\r\nAverage operation time: 1592.115631 ns (var count = 1703936)\r\nAverage operation time: 1398.114811 ns (var count = 6815744)\r\nAverage operation time: 1432.140196 ns (var count = 13631488)\r\nAverage operation time: 852.286091 ns (var count = 13631488)\r\nAverage operation time: 816.319372 ns (var count = 13631488)\r\nAverage operation time: 835.554661 ns (var count = 13631488)\r\nAverage operation time: 869.618492 ns (var count = 13631488)\r\n<\/pre>\n<p>What we traded in API convenience seems to have paid back in runtime performance. The Java implementation which stabilizes at ~850 ns obviously won&#8217;t beat C++ but is apparently faster than the C# implementation by almost 25%.<\/p>\n<p>Who says <a href=\"https:\/\/stackoverflow.com\/questions\/2163411\/is-java-really-slow\">Java is slow<\/a>?!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post, we looked at competing message bus implementations in C# and C++. How about we give Java a try now? Converting to Java syntax and style conventions, the SendOneSubscriber test should look like this: package com.example; import&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[91,104],"tags":[],"class_list":["post-5593","post","type-post","status-publish","format-standard","hentry","category-design","category-performance"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5593","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5593"}],"version-history":[{"count":3,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5593\/revisions"}],"predecessor-version":[{"id":5596,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5593\/revisions\/5596"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5593"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5593"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5593"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}