{"id":12831,"date":"2013-05-16T16:00:46","date_gmt":"2013-05-16T13:00:46","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=12831"},"modified":"2013-05-16T09:06:11","modified_gmt":"2013-05-16T06:06:11","slug":"asynchronous-cdi-events","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html","title":{"rendered":"Asynchronous CDI Events"},"content":{"rendered":"<p>Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer (so a method with parameter annotated <code>@Observes<\/code>) would be invoked multiple times at the same time for different event instances. In other words, after producing few events, is it possible that the following method will be processed by more than one thread at the same time:<br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n&nbsp;<\/p>\n<pre class=\" brush:java\">public void observe(@Observes MyEvent myEvent) { ... }<\/pre>\n<p>After thinking about it I\u2019ve decided to run a few tests and describe results in this post.<\/p>\n<p>First results: it occurred that <strong>CDI Events are fired in synchronous mode<\/strong> which was a bit surprise for me. Why?<\/p>\n<p>Up to this time, I\u2019ve seen it this way: CDI observers allows me to very cleanly separate event producer from event consumer, so I don\u2019t have any hard-coded registering of listeners, maintaining a list of listeners and manually informing them. The CDI container does everything for me.<\/p>\n<p>Therefore, if we have cleanly separated producers from consumers I thought that there exists some kind of event bus running in specialized, thread executors pool which is responsible for mediation between registered events and invoked observers methods. I guess I based this assumption on other event\/listeners solutions like <a href=\"http:\/\/code.google.com\/p\/guava-libraries\/wiki\/EventBusExplained\">Google Guava EventBus<\/a>. They give you a chance to define if you want to use synchronous (default, <a href=\"http:\/\/docs.guava-libraries.googlecode.com\/git\/javadoc\/com\/google\/common\/eventbus\/EventBus.html\">EventBus<\/a>) or asynchronous event dispatchers (<a href=\"http:\/\/docs.guava-libraries.googlecode.com\/git\/javadoc\/com\/google\/common\/eventbus\/AsyncEventBus.html\">AsyncEventBus<\/a>.)<\/p>\n<p>Moreover, if EJBs are both: producer and consumer, I assume it would have the same features as asynchronous EJB calls when it comes to transactions. The only possible JTA transaction attribute for asynchronous event observer would be: <code>REQUIRED<\/code>, <code>REQUIRES_NEW<\/code> or <code>NOT_SUPPORTED<\/code>.<\/p>\n<p>Now that\u2019s all how I <strong>expected<\/strong> it to work which seems to be <strong>quite different from the current status<\/strong>. The real life shows that CDI events are synchronous.<\/p>\n<p>There is an <a href=\"https:\/\/issues.jboss.org\/browse\/CDI-31\">issue for making asynchronous events available in CDI 1.1<\/a> but I\u2019m not sure what is the current status of this feature and didn\u2019t find a word about it in CDI 1.1 (part of Java EE 7).<\/p>\n<p>Let\u2019s see how we can deal with it on our own.<\/p>\n<h4>Table of contents<\/h4>\n<ol>\n<li><a title=\"Jump to Default Synchronous Events\" href=\"#toc-default-synchronous-events\">Default Synchronous Events<\/a><\/li>\n<li><a title=\"Jump to Solution 1 \u2013 CDI Producer and Singleton EJB as Receiver\" href=\"#toc-solution-1-cdi-producer-and-singleton-ejb-as-receiver\">Solution 1 \u2013 CDI Producer and Singleton EJB as Receiver<\/a><\/li>\n<li><a title=\"Jump to Solution 2 \u2013 Use Singleton EJB as Receiver With Read Lock\" href=\"#toc-solution-2-use-singleton-ejb-as-receiver-with-read-lock\">Solution 2 \u2013 Use Singleton EJB as Receiver With Read Lock<\/a><\/li>\n<li><a title=\"Jump to Solution 3 \u2013 EJB Producer and CDI Consumer\" href=\"#toc-solution-3-ejb-producer-and-cdi-consumer\">Solution 3 \u2013 EJB Producer and CDI Consumer<\/a><\/li>\n<li><a title=\"Jump to Solution 4 \u2013 EJB Producer and EJB Consumer\" href=\"#toc-solution-4-ejb-producer-and-ejb-consumer\">Solution 4 \u2013 EJB Producer and EJB Consumer<\/a><\/li>\n<li><a title=\"Jump to Solution 4 vs Solution 2\" href=\"#toc-solution-4-vs-solution-2\">Solution 4 vs Solution 2<\/a><\/li>\n<li><a title=\"Jump to Solution 5 \u2013 EJB Producer and CDI Consumer II\" href=\"#toc-solution-5-ejb-producer-and-cdi-consumer-ii\">Solution 5 \u2013 EJB Producer and CDI Consumer II<\/a><\/li>\n<li><a title=\"Jump to Solution 6 \u2013 CDI With JMS\" href=\"#toc-solution-6-cdi-with-jms\">Solution 6 \u2013 CDI With JMS<\/a><\/li>\n<li><a title=\"Jump to Conclusion\" href=\"#toc-conclusion\">Conclusion<\/a><\/li>\n<\/ol>\n<h2>Default Synchronous Events<\/h2>\n<p>Let\u2019s start with basic example showing the problem. Take a look at the code \u2013 first, the CDI Bean producer:<\/p>\n<pre class=\" brush:java\">@Path(\"\/produce\")\r\npublic class EventGenerator {\r\n\r\n    @Inject\r\n    private Logger logger;\r\n\r\n    @Inject\r\n    private Event&lt;MyEvent&gt; events;\r\n\r\n    @Path(\"\/cdiBean\/{eventsNum}\")\r\n    @GET\r\n    public String generateEvents(@PathParam(\"eventsNum\") int numberOfEventsToGenerate) {\r\n        for (int i = 0; i &lt; numberOfEventsToGenerate; i++) {\r\n            MyEvent event = new MyEvent(i);\r\n\r\n            logger.info(\"Generating Event: \" + event);\r\n            events.fire(event);\r\n        }\r\n        return \"Finished. Generated \" + numberOfEventsToGenerate + \" events.\";\r\n    }\r\n}<\/pre>\n<p><code>MyEvent<\/code> is just some event object which is not really important here. It stores event sequence number that we pass while instantiation.<\/p>\n<p>Consumer is a pretty simple CDI Bean:<\/p>\n<pre class=\" brush:java\">public class EventConsumer {\r\n\r\n    @Inject\r\n    private Logger logger;\r\n\r\n    public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException {\r\n        logger.info(\"Receiving event: \" + myEvent);\r\n\r\n        TimeUnit.MILLISECONDS.sleep(500);\r\n    }\r\n}<\/pre>\n<p>Note that I\u2019ve inserted a thread sleep to simulate some long-running event receiver process.<\/p>\n<p>Now, let\u2019s run this example by invoking a REST command this <code>EventProducer<\/code> is exposing. The result (running <strong>JBoss EAP 6.1 Alpha<\/strong>) would be similar to this:<\/p>\n<blockquote>\n<p>14:15:59,196 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=0<\/strong>]<br \/>\n14:15:59,197 [com.piotrnowicki.EventConsumer] (http-\/127.0.0.1:8080-1) Receiving event: MyEvent[<strong>seqNo=0<\/strong>]<br \/>\n14:15:59,697 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=1<\/strong>]<br \/>\n14:15:59,698 [com.piotrnowicki.EventConsumer] (http-\/127.0.0.1:8080-1) Receiving event: MyEvent[<strong>seqNo=1<\/strong>]<br \/>\n14:16:00,199 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=2<\/strong>]<br \/>\n14:16:00,200 [com.piotrnowicki.EventConsumer] (http-\/127.0.0.1:8080-1) Receiving event: MyEvent[<strong>seqNo=2<\/strong>]<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<\/blockquote>\n<p>It shows the synchronous nature of CDI events \u2013 event producing and consuming takes place in the same thread and one after another.<\/p>\n<p>So, how to achieve asynchronous events with CDI?<\/p>\n<h2>Solution 1 \u2013 CDI Producer and Singleton EJB as Receiver<\/h2>\n<p>Producer stays at it was \u2013 a pure CDI bean:<\/p>\n<pre class=\" brush:java\">@Path(\"\/produce\") public class EventGenerator {\r\n\r\n    @Path(\"\/cdiBean\/{eventsNum}\")\r\n    @GET\r\n    public String generateEvents(@PathParam(\"eventsNum\") int numberOfEventsToGenerate) { ... }\r\n}<\/pre>\n<p>Now if you turn your receiver into <strong>@Singleton<\/strong> EJB and mark observes method as <strong>@Asynchronous<\/strong> like this:<\/p>\n<pre class=\" brush:java\">@Singleton\r\npublic class EventConsumer {\r\n\r\n    @Asynchronous\r\n    public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException { ...  }\r\n}<\/pre>\n<p>You\u2019ll get the following results:<\/p>\n<blockquote>\n<p>14:21:19,341 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]<br \/>\n14:21:19,343 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]<br \/>\n14:21:19,343 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]<br \/>\n14:21:<strong>19,347<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 2) Receiving event: MyEvent[seqNo=1]<br \/>\n14:21:<strong>19,848<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 1) Receiving event: MyEvent[seqNo=0]<br \/>\n14:21:<strong>20,350<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 3) Receiving event: MyEvent[seqNo=2]<\/p>\n<\/blockquote>\n<p>Events are produced one after another and in separate threads Singleton EJB is serving them one after another (take a look at time of event processing.) That\u2019s because of implicit write-lock for every business methods of Singleton EJB. So this is:<\/p>\n<p>Asynchronous: <strong>yes<\/strong><br \/>\nThread-safe observer method: <strong>yes<\/strong><\/p>\n<h2>Solution 2 \u2013 Use Singleton EJB as Receiver With Read Lock<\/h2>\n<p>This approach is very similar to Solution 1, however, it gives you a much higher throughput because all events processing takes place in parallel.<\/p>\n<p>Our producer stays the same \u2013 it\u2019s a CDI bean:<\/p>\n<pre class=\" brush:java\">@Path(\"\/produce\")\r\npublic class EventGenerator {\r\n\r\n    @Path(\"\/cdiBean\/{eventsNum}\")\r\n    @GET\r\n    public String generateEvents(@PathParam(\"eventsNum\") int numberOfEventsToGenerate) { ... }\r\n}<\/pre>\n<p>Our consumer has <code>@Lock(READ)<\/code> added to its observes method; this makes the magic of being able to serve multiple events at the same time:<\/p>\n<pre class=\" brush:java\">@Singleton\r\npublic class EventConsumer {\r\n\r\n    @Asynchronous\r\n    @Lock(LockType.READ)\r\n    public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException { ... }\r\n}<\/pre>\n<p>This is what you can get as a result:<\/p>\n<blockquote>\n<p>14:24:44,202 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]<br \/>\n14:24:44,204 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]<br \/>\n14:24:44,205 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]<br \/>\n14:24:<strong>44,207<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 4) Receiving event: MyEvent[seqNo=0]<br \/>\n14:24:<strong>44,207<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 6) Receiving event: MyEvent[seqNo=2]<br \/>\n14:24:<strong>44,207<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 5) Receiving event: MyEvent[seqNo=1]<\/p>\n<\/blockquote>\n<p>Different threads serving events at the same time are giving you a bigger throughput. So this is:<\/p>\n<p>Asynchronous: <strong>yes<\/strong><br \/>\nThread-safe observer method: <strong>no<\/strong><\/p>\n<h2>Solution 3 \u2013 EJB Producer and CDI Consumer<\/h2>\n<p>CDI allows you to observe events during specific stages of transaction. You specify it using <code>@Observes(during=TransactionPhase...)<\/code>. In our case, we would like the CDI to stack all those events and invoke our observer only after the transaction ends. To do so we need only to add the above attribute to our CDI Bean observer:<\/p>\n<pre class=\" brush:java\">public class EventConsumer { \r\n   public void consumeEvent(@Observes(during = TransactionPhase.AFTER_COMPLETION) MyEvent myEvent) { ... } \r\n}<\/pre>\n<p>Now we just need to make sure we have a running transaction in <code>EventGenerator<\/code> method. We can do it quickly by transforming our CDI Bean into <code>@Stateless<\/code> EJB and using its implicit <code>REQUIRED<\/code> TransactionAttribute like this:<\/p>\n<pre class=\" brush:java\">@Stateless\r\n@Path(\"\/produce\")\r\npublic class EventGenerator {\r\n\r\n    @Path(\"\/cdiBean\/{eventsNum}\")\r\n    @GET\r\n    public String generateEvents(@PathParam(\"eventsNum\") int numberOfEventsToGenerate) { ... }\r\n}<\/pre>\n<p>This is the result we might end with:<\/p>\n<blockquote>\n<p>14:39:06,776 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]<br \/>\n14:39:06,776 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]<br \/>\n14:39:06,776 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]<br \/>\n14:39:<strong>06,778<\/strong> [com.piotrnowicki.EventConsumer] (http-\/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=2]<br \/>\n14:39:<strong>07,279<\/strong> [com.piotrnowicki.EventConsumer] (http-\/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=1]<br \/>\n14:39:<strong>07,780<\/strong> [com.piotrnowicki.EventConsumer] (http-\/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=0]<\/p>\n<\/blockquote>\n<p>EJB <code>EventGenerator<\/code> starts a transaction and CDI bean observer will be invoked in serialized way only after the transaction completes.<\/p>\n<p>Asynchronous: <strong>yes<\/strong><br \/>\nThread-safe observer method: <strong>yes<\/strong><\/p>\n<h2>Solution 4 \u2013 EJB Producer and EJB Consumer<\/h2>\n<p>This is very similar to Solution 3. Our generator stays the same (Stateless EJB):<\/p>\n<pre class=\" brush:java\">@Stateless\r\n@Path(\"\/produce\")\r\npublic class EventGenerator {\r\n\r\n    @Path(\"\/cdiBean\/{eventsNum}\")\r\n    @GET\r\n    public String generateEvents(@PathParam(\"eventsNum\") int numberOfEventsToGenerate) { ... }\r\n}<\/pre>\n<p>And changes are made to <code>EventConsumer<\/code> which is right now:<\/p>\n<pre class=\" brush:java\">@Singleton\r\npublic class EventConsumer {\r\n\r\n    @Asynchronous\r\n    @Lock(LockType.READ)\r\n    public void consumeEvent(@Observes(during = TransactionPhase.AFTER_COMPLETION) MyEvent myEvent) throws InterruptedException { ...  }\r\n}<\/pre>\n<p>The result might be as follows:<\/p>\n<blockquote>\n<p>14:44:09,363 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]<br \/>\n14:44:09,464 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]<br \/>\n14:44:09,564 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]<br \/>\n14:44:<strong>09,670<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 8) Receiving event: MyEvent[seqNo=2]<br \/>\n14:44:<strong>09,670<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 2) Receiving event: MyEvent[seqNo=1]<br \/>\n14:44:<strong>09,670<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 1) Receiving event: MyEvent[seqNo=0]<\/p>\n<\/blockquote>\n<p>We\u2019ve used two features here \u2013 one is that the event consumer method is asynchronous and the second one is that the consumer will not be notified before the producer transaction completes. This gives us:<\/p>\n<p>Asynchronous: <strong>yes<\/strong><br \/>\nThread-safe observer method: <strong>no<\/strong><\/p>\n<h2>Solution 4 vs Solution 2<\/h2>\n<p>Those two solutions seems to be the same. They only differ with consumer\u2019s annotation: <code>@Observes<\/code> vs <code>@Observes(during = TransactionPhase.AFTER_COMPLETION)<\/code>. Moreover they act the same for our test case: <strong>they are asynchronous<\/strong> and <strong>multiple threads can be processing event receivers at the same time<\/strong>. However, there is one big difference between them.<\/p>\n<p>In our test case we fired events one after another. Imagine that there is some other operations between events firing. In such case:<\/p>\n<ul>\n<li>Solution 2 (<code>@Observes<\/code>) will start processing events <strong>just after the first one will be fired<\/strong>,<\/li>\n<li>Solution 4 (<code>@Observes(during = TransactionPhase.AFTER_COMPLETION)<\/code>) will start processing <strong>just after transaction completes<\/strong>, so when all events will be fired.<\/li>\n<\/ul>\n<p>This shows possible result of such situation:<\/p>\n<p>Solution 2 (<code>@Observes<\/code>)<\/p>\n<blockquote>\n<p>15:01:34,318 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=0<\/strong>]<br \/>\n15:01:34,320 [com.piotrnowicki.EventConsumer] (EJB default \u2013 3) Receiving event: MyEvent[<strong>seqNo=0<\/strong>]<br \/>\n15:01:34,419 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=1<\/strong>]<br \/>\n15:01:34,420 [com.piotrnowicki.EventConsumer] (EJB default \u2013 6) Receiving event: MyEvent[<strong>seqNo=1<\/strong>]<br \/>\n15:01:34,520 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=2<\/strong>]<br \/>\n15:01:34,521 [com.piotrnowicki.EventConsumer] (EJB default \u2013 9) Receiving event: MyEvent[<strong>seqNo=2<\/strong>]<\/p>\n<\/blockquote>\n<p>Solution 4 (<code>@Observes(during = TransactionPhase.AFTER_COMPLETION)<\/code>)<\/p>\n<blockquote>\n<p>15:00:41,126 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=0<\/strong>]<br \/>\n15:00:41,226 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=1<\/strong>]<br \/>\n15:00:41,326 [com.piotrnowicki.EventGenerator] (http-\/127.0.0.1:8080-1) Generating Event: MyEvent[<strong>seqNo=2<\/strong>]<br \/>\n15:00:41,432 [com.piotrnowicki.EventConsumer] (EJB default \u2013 10) Receiving event: MyEvent[<strong>seqNo=2<\/strong>]<br \/>\n15:00:41,432 [com.piotrnowicki.EventConsumer] (EJB default \u2013 4) Receiving event: MyEvent[<strong>seqNo=1<\/strong>]<br \/>\n15:00:41,432 [com.piotrnowicki.EventConsumer] (EJB default \u2013 5) Receiving event: MyEvent[<strong>seqNo=0<\/strong>]<\/p>\n<\/blockquote>\n<h2>Solution 5 \u2013 EJB Producer and CDI Consumer II<\/h2>\n<p>Up to this point we\u2019ve tried to make our receiver asynchronous. There is also the opposite way \u2013 we can <strong>make the event producer asynchronous<\/strong>. We can achieve it by marking our producer as <code>@Stateless<\/code> and invoking its own asynchronous method that will just fire an event:<\/p>\n<pre class=\" brush:java\">@Stateless\r\n@Path(\"\/produce\")\r\npublic class EventGenerator {\r\n    \/\/ ...\r\n\r\n    @Resource\r\n    private SessionContext sctx;\r\n\r\n    @Path(\"\/cdiBean\/{eventsNum}\")\r\n    @GET\r\n    public String generateEvents(@PathParam(\"eventsNum\") int numberOfEventsToGenerate) {\r\n        for (int i = 0; i &lt; numberOfEventsToGenerate; i++) {\r\n            sctx.getBusinessObject(EventGenerator.class).fireEvent(new MyEvent(i));\r\n        }\r\n\r\n        return \"Finished. Generated \" + numberOfEventsToGenerate + \" events.\";\r\n    }\r\n\r\n    @Asynchronous\r\n    public void fireEvent(final MyEvent event) {\r\n        events.fire(event);\r\n    }\r\n}<\/pre>\n<p>Take a closer look at EJB auto-reference using <code>SessionContext<\/code>. It is required in this case as we want the container to dispatch our method call and add the asynchronous nature of it. We don\u2019t want to make it a local call so we refuse to use implicit <code>this<\/code> object.<br \/>\nEvent consumer, on the other hand, is plain CDI bean:<\/p>\n<pre class=\" brush:java\">public class EventConsumer {\r\n    public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException { ... }\r\n}<\/pre>\n<p>The result might be as follows:<\/p>\n<blockquote>\n<p>00:40:32,820 [com.piotrnowicki.EventGenerator] (EJB default \u2013 2) Generating Event: MyEvent[seqNo=1]<br \/>\n00:40:32,820 [com.piotrnowicki.EventGenerator] (EJB default \u2013 3) Generating Event: MyEvent[seqNo=2]<br \/>\n00:40:32,820 [com.piotrnowicki.EventGenerator] (EJB default \u2013 1) Generating Event: MyEvent[seqNo=0]<br \/>\n00:40:<strong>32,821<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 1) Receiving event: MyEvent[seqNo=0]<br \/>\n00:40:<strong>32,821<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 2) Receiving event: MyEvent[seqNo=1]<br \/>\n00:40:<strong>32,821<\/strong> [com.piotrnowicki.EventConsumer] (EJB default \u2013 3) Receiving event: MyEvent[seqNo=2]<\/p>\n<\/blockquote>\n<p>Asynchronous: <strong>yes<\/strong><br \/>\nThread-safe observer method: <strong>no<\/strong><\/p>\n<h2>Solution 6 \u2013 CDI With JMS<\/h2>\n<p>This is a solution presented by <a href=\"https:\/\/weblogs.java.net\/blog\/jjviana\">Juliano Viana<\/a> on <a href=\"https:\/\/weblogs.java.net\/blog\/jjviana\/archive\/2010\/04\/13\/decoupling-event-producers-and-consumers-jee6-using-cdi-and-jms\">his blog<\/a>. It uses JMS as the event bus. CDI event is produced, then fetched by some class that is responsible for putting this event into JMS topic \/ queue. MDB that fetches messages from topic \/ queue is producing an event that invokes the real receiver. This not only gives you the asynchronous delivery of events but also adds transaction nature to it. E.g. if event receiver is not able to process the message \u2013 it can rollback the transaction and the queue will make sure the message will be re-delivered (perhaps next time your event processor will be able to serve this event?)<\/p>\n<h2>Conclusion<\/h2>\n<p>CDI 1.0 doesn\u2019t support asynchronous events generation. It also doesn\u2019t seem that CDI 1.1 will have such support.<\/p>\n<p>This, however, doesn\u2019t mean you cannot achieve asynchronous processing. There are already existing solutions either basing on EJB\u2019s 3.1 or existing CDI observer attributes. You should also be able to write a <a href=\"https:\/\/weblogs.java.net\/blog\/jjviana\/archive\/2010\/04\/13\/decoupling-event-producers-and-consumers-jee6-using-cdi-and-jms#comment-826731\">portable CDI extension<\/a> that adds this functionality to your code.<br \/>\n&nbsp;<\/p>\n<div style=\"border: 1px solid #D8D8D8; background: #FAFAFA; width: 100%; padding-left: 5px;\"><b><i>Reference: <\/i><\/b><a href=\"http:\/\/piotrnowicki.com\/2013\/05\/asynchronous-cdi-events\/\">Asynchronous CDI Events<\/a> from our <a href=\"http:\/\/www.javacodegeeks.com\/jcg\">JCG partner<\/a> Piotr Nowicki at the <a href=\"http:\/\/piotrnowicki.com\/blog\/\">Piotr Nowicki&#8217;s Blog<\/a> blog.<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer (so a method with parameter annotated @Observes) would be invoked multiple times at the same time for different event instances. In other words, after producing few &hellip;<\/p>\n","protected":false},"author":380,"featured_media":148,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[290,289],"class_list":["post-12831","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-cdi","tag-java-ee6"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Asynchronous CDI Events<\/title>\n<meta name=\"description\" content=\"Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Asynchronous CDI Events\" \/>\n<meta property=\"og:description\" content=\"Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:author\" content=\"http:\/\/www.facebook.com\/piotr.nowicki\" \/>\n<meta property=\"article:published_time\" content=\"2013-05-16T13:00:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/java-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Piotr Nowicki\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/p_nowicki\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Piotr Nowicki\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html\"},\"author\":{\"name\":\"Piotr Nowicki\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/3a59f170e0f91da4c90191637f0fa45e\"},\"headline\":\"Asynchronous CDI Events\",\"datePublished\":\"2013-05-16T13:00:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html\"},\"wordCount\":1775,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/java-logo.jpg\",\"keywords\":[\"CDI\",\"Java EE6\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html\",\"name\":\"Asynchronous CDI Events\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/java-logo.jpg\",\"datePublished\":\"2013-05-16T13:00:46+00:00\",\"description\":\"Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/java-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/java-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/05\\\/asynchronous-cdi-events.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Asynchronous CDI Events\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/3a59f170e0f91da4c90191637f0fa45e\",\"name\":\"Piotr Nowicki\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2912400b087180e77e92900db0df9b1330610bec9bbce89f1eb36914193c8479?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2912400b087180e77e92900db0df9b1330610bec9bbce89f1eb36914193c8479?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2912400b087180e77e92900db0df9b1330610bec9bbce89f1eb36914193c8479?s=96&d=mm&r=g\",\"caption\":\"Piotr Nowicki\"},\"description\":\"Piotr is a Java fascinate since his computer science studies (2007). He's currently working as a senior Java EE developer in utilities and industry sector. He's mostly interested in designing and development of web applications using Java EE technology stack.\",\"sameAs\":[\"http:\\\/\\\/piotrnowicki.com\\\/blog\\\/\",\"http:\\\/\\\/www.facebook.com\\\/piotr.nowicki\",\"http:\\\/\\\/www.linkedin.com\\\/pub\\\/piotr-nowicki\\\/52\\\/951\\\/8b5\",\"https:\\\/\\\/x.com\\\/https:\\\/\\\/twitter.com\\\/p_nowicki\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/piotr-nowicki\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Asynchronous CDI Events","description":"Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html","og_locale":"en_US","og_type":"article","og_title":"Asynchronous CDI Events","og_description":"Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer","og_url":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_author":"http:\/\/www.facebook.com\/piotr.nowicki","article_published_time":"2013-05-16T13:00:46+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/java-logo.jpg","type":"image\/jpeg"}],"author":"Piotr Nowicki","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/p_nowicki","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Piotr Nowicki","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html"},"author":{"name":"Piotr Nowicki","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/3a59f170e0f91da4c90191637f0fa45e"},"headline":"Asynchronous CDI Events","datePublished":"2013-05-16T13:00:46+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html"},"wordCount":1775,"commentCount":3,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/java-logo.jpg","keywords":["CDI","Java EE6"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html","url":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html","name":"Asynchronous CDI Events","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/java-logo.jpg","datePublished":"2013-05-16T13:00:46+00:00","description":"Few days ago, during our regular code review, one of my colleagues raised a question what would happen \u2014 and if it\u2019s even possible \u2014 when a CDI Observer","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/java-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/java-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2013\/05\/asynchronous-cdi-events.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Asynchronous CDI Events"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/3a59f170e0f91da4c90191637f0fa45e","name":"Piotr Nowicki","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/2912400b087180e77e92900db0df9b1330610bec9bbce89f1eb36914193c8479?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/2912400b087180e77e92900db0df9b1330610bec9bbce89f1eb36914193c8479?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2912400b087180e77e92900db0df9b1330610bec9bbce89f1eb36914193c8479?s=96&d=mm&r=g","caption":"Piotr Nowicki"},"description":"Piotr is a Java fascinate since his computer science studies (2007). He's currently working as a senior Java EE developer in utilities and industry sector. He's mostly interested in designing and development of web applications using Java EE technology stack.","sameAs":["http:\/\/piotrnowicki.com\/blog\/","http:\/\/www.facebook.com\/piotr.nowicki","http:\/\/www.linkedin.com\/pub\/piotr-nowicki\/52\/951\/8b5","https:\/\/x.com\/https:\/\/twitter.com\/p_nowicki"],"url":"https:\/\/www.javacodegeeks.com\/author\/piotr-nowicki"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/12831","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/380"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=12831"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/12831\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/148"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=12831"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=12831"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=12831"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}