{"id":706,"date":"2009-12-22T08:45:20","date_gmt":"2009-12-22T13:45:20","guid":{"rendered":"http:\/\/www.andygibson.net\/blog\/?p=706"},"modified":"2010-07-28T01:10:43","modified_gmt":"2010-07-28T06:10:43","slug":"getting-started-with-cdi-part-2-injection","status":"publish","type":"post","link":"http:\/\/www.andygibson.net\/old-blog\/tutorial\/getting-started-with-cdi-part-2-injection\/","title":{"rendered":"Getting Started with CDI part 2 &#8211; Injection"},"content":{"rendered":"<p>In <a href=\"http:\/\/www.andygibson.net\/blog\/index.php\/tutorial\/getting-started-with-jsf-2-0-and-cdi-in-jee-6-part-1\/\"> part 1<\/a>, we looked at creating a JEE 6 application with Netbeans using JSF and CDI running on Glassfish. Now we&#8217;ll take a closer look at using CDI for managing dependencies in a Java EE 6 environment.<br \/>\n<!--more--><br \/>\nRead <a href=\"http:\/\/www.andygibson.net\/blog\/tutorial\/getting-started-with-jsf-2-0-and-cdi-in-jee-6-part-1\/\">Part 1<\/a><br \/>\nRead <a href=\"http:\/\/www.andygibson.net\/blog\/tutorial\/getting-started-with-cdi-part-2-injection\/\">Part 2<\/a><br \/>\nRead <a href=\"http:\/\/www.andygibson.net\/blog\/tutorial\/getting-started-with-jsf-2-0-and-cdi-part-3\/\">Part 3<\/a><\/p>\n<h1>Last Time<\/h1>\n<p>Last time we looked at setting up the development environment and creating our EE6 application in Netbeans and deploying it to Glassfish v3. This time, we&#8217;ll skip straight to writing code which can be put into either a new Netbeans project using the previous tutorial, or you can use the existing project and just add the new code.<\/p>\n<h1>The &#8216;I&#8217; in CDI<\/h1>\n<p>CDI is an API for injecting contexts and dependencies  which is the part we&#8217;ll turn our attention to now. In Seam and Spring dependencies worked mostly by naming beans and binding them to their injection points by their name. So far we have only referenced a managed bean by name from the JSF page when we defined the name for the bean using the <code>@Named<\/code> annotation. The primary role of the <code>@Named<\/code> annotation is to define the bean for the purpose of resolving EL statements within the application, usually through the JSF EL resolvers. Injection <i>could<\/i> be performed by using names, but this was not how injection in CDI was meant to work since CDI gives us a much richer way to express injection points and the beans to be injected into them.<\/p>\n<p>Let&#8217;s look at an example which is somewhat contrived. We have a dao that returns a list of objects that need validating, and for invalid ones, we take a certain action.  Here&#8217;s the definition for the item.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage eedemo;\r\n\r\npublic class Item {\r\n\r\n    private int value;\r\n    private int limit;\r\n\r\n    @Override\r\n    public String toString() {\r\n        return super.toString() + String.format(&quot; &#x5B;Value=%d, Limit=%d]&quot;, value,limit);\r\n    }\r\n\r\n    \/*\r\n    getters and setters omitted\r\n    *\/\r\n}\r\n<\/pre>\n<p>The <code>ItemDao<\/code> interface defines how we get the list of item objects. In this test application we anticipate using multiple implementations so we will code to interfaces. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic interface ItemDao {\r\n\r\n    List&lt;Item&gt; fetchItems();\r\n\r\n}\r\n<\/pre>\n<p>The <code>ItemProcessor<\/code> is our main class that we will inject our beans into and execute the process from. For now, we will start with the Dao and look at how we will inject it into our processor bean.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport java.util.List;\r\nimport javax.inject.Named;\r\nimport javax.enterprise.context.RequestScoped;\r\n\r\n@Named(&quot;itemProcessor&quot;)\r\n@RequestScoped\r\npublic class ItemProcessor {\r\n\r\n    private ItemDao itemDao;\r\n\r\n    public void execute() {\r\n      List&lt;Item&gt;  items = itemDao.fetchItems();\r\n      for (Item item : items) {\r\n          System.out.println(&quot;Found item &quot;+item);\r\n      }\r\n    }\r\n}\r\n<\/pre>\n<p>We&#8217;ll start with a simple Dao that just creates a list of items and returns a fixed list of items.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class DefaultItemDao implements ItemDao {\r\n\r\n    public List&lt;Item&gt; fetchItems() {\r\n        List&lt;Item&gt; results = new ArrayList&lt;Item&gt;();\r\n        results.add(new Item(34, 7));\r\n        results.add(new Item(4, 37));\r\n        results.add(new Item(24, 19));\r\n        results.add(new Item(89, 32));\r\n        return results;\r\n    }\r\n}\r\n<\/pre>\n<p>In order to inject the <code>DefaultItemDao<\/code> into our <code>ItemProcessor<\/code> we add the <code>javax.inject.Inject<\/code> annotation to the <code>ItemDao<\/code> field to indicate that this field is an injection point.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;itemProcessor&quot;)\r\n@RequestScoped\r\npublic class ItemProcessor {\r\n\r\n    @Inject\r\n    private ItemDao itemDao;\r\n\r\n    ...\r\n}\r\n<\/pre>\n<p>Finally, we need some way to call the <code>execute()<\/code< method on the <code>ItemProcessor<\/code>. We can run this in a SE environment, but for now we&#8217;ll keep it in a JSF page. We&#8217;ll add a new page with a button to call the execute method called <code>process.xhtml<\/code>.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;?xml version='1.0' encoding='UTF-8' ?&gt;\r\n&lt;!DOCTYPE html PUBLIC &quot;-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN&quot; &quot;http:\/\/www.w3.org\/TR\/xhtml1\/DTD\/xhtml1-transitional.dtd&quot;&gt;\r\n&lt;html xmlns=&quot;http:\/\/www.w3.org\/1999\/xhtml&quot;\r\n      xmlns:h=&quot;http:\/\/java.sun.com\/jsf\/html&quot;&gt;\r\n    &lt;h:head&gt;\r\n        &lt;title&gt;Item Processor&lt;\/title&gt;\r\n    &lt;\/h:head&gt;\r\n    &lt;h:body&gt;\r\n        &lt;h:form&gt;\r\n        &lt;h:commandButton action=&quot;#{itemProcessor.execute}&quot; value=&quot;Execute&quot;\/&gt;&lt;br\/&gt;\r\n        &lt;\/h:form&gt;\r\n    &lt;\/h:body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>If you open up the page at <a href=\"http:\/\/localhost:8080\/ee6demo\/faces\/process.xhtml\">http:\/\/localhost:8080\/ee6demo\/faces\/process.xhtml<\/a> you will see just a button that when clicked lists the items from our default Dao implementation in the console.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nINFO: Found item eedemo.Item@23cbc6 &#x5B;Value=34, Limit=7]\r\nINFO: Found item eedemo.Item@a40279 &#x5B;Value=4, Limit=37]\r\nINFO: Found item eedemo.Item@19e6292 &#x5B;Value=24, Limit=19]\r\nINFO: Found item eedemo.Item@1597bc4 &#x5B;Value=89, Limit=32]\r\n<\/pre>\n<p>We created a class which implements the <code>ItemDao<\/code> interface and when the application was deployed our managed beans in the module were processed by the CDI implementation (again because of the <code>beans.xml<\/code> file in the module. Our <code>Inject<\/code> annotation specifies that we want to inject a managed bean into that field and the only thing we know about the bean to inject is that it must implement <code>ItemDao<\/code> or some subtype of that interface. In this case, the <code>DefaultItemDao<\/code> class fits the bill perfectly.<\/p>\n<p>Let&#8217;s say we add another Dao class to our application which also implements the <code>ItemDao<\/code> interface, now the choice isn&#8217;t so clear as to which bean we want to inject.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class AnotherItemDao implements ItemDao {\r\n\r\n    public List&lt;Item&gt; fetchItems() {\r\n        List&lt;Item&gt; results = new ArrayList&lt;Item&gt;();\r\n        results.add(new Item(99, 9));\r\n        return results;\r\n    }\r\n\r\n}\r\n<\/pre>\n<p>We now have two classes the implement this interface and predictably, Weld gives us an ambiguous dependency error meaning that it cannot determine what bean to use for that injection point. Most, if not all of the errors that can occur with regards to CDI injection in Weld are reported at deployment time, even down to whether beans are missing a Serializable implementation.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nCaused by: org.jboss.weld.DeploymentException: Injection point has ambiguous dependencies. \r\nInjection point: field eedemo.ItemProcessor.itemDao; Qualifiers: &#x5B;@javax.enterprise.inject.Default()]; \r\n\r\nPossible dependencies: &#x5B;eedemo.AnotherItemDao, eedemo.DefaultItemDao]\r\n<\/pre>\n<p>We could make our <code>itemDao<\/code> field in the item processor a type that matches one of the implementation types (<code>AnotherItemDao<\/code> or  <code>DefaultItemDao<\/code>) since it would then match one and only one class type. However, then we would lose the benefits of coding to an interface and find it harder to change implementations without changing the field type. A better solution is to instead look at CDI Qualifiers.<\/p>\n<h1>Qualifiers<\/h1>\n<p>A CDI qualifier is an annotation that can be applied at the class level to indicate the kind of bean the class is, and also at the field level (among other places) to indicate what kind of bean needs to be injected at that point.<\/p>\n<p>When CDI inspects an injection point to find a suitable bean to inject it takes not only the class type into account, but also any qualifiers. Without knowing it, we have already used one qualifier which is the default qualifier called <code>@Any<\/code>. Let&#8217;s create a <code>@Demo<\/code> qualifier which we can apply to our <code>DefaultItemDao<\/code> implementation and also to the injection point.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage eedemo.qualifier;\r\n\r\nimport static java.lang.annotation.ElementType.TYPE;\r\nimport static java.lang.annotation.ElementType.FIELD;\r\nimport static java.lang.annotation.ElementType.PARAMETER;\r\nimport static java.lang.annotation.ElementType.METHOD;\r\nimport java.lang.annotation.Retention;\r\nimport java.lang.annotation.RetentionPolicy;\r\nimport java.lang.annotation.Target;\r\nimport javax.inject.Qualifier;\r\n\r\n@Retention(RetentionPolicy.RUNTIME)\r\n@Target({FIELD,METHOD,PARAMETER,TYPE})\r\n@Qualifier\r\npublic @interface Demo {\r\n\r\n}\r\n<\/pre>\n<p>First, we&#8217;ll add this qualifier to our default dao implementation at the class level.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Demo\r\npublic class DefaultItemDao implements SomeDao {\r\n\r\n    public List&lt;Item&gt; fetchItems() {\r\n..\r\n..\r\n\r\n<\/pre>\n<p>If you save and deploy this file now, you may notice that we don&#8217;t have any errors, and if you go to the web page and click the execute button, you can see that in the console, it displays the list of items from the <code>AnotherItemDao<\/code> dao (remember we annotated the <code>DefaultItemDao<\/code> implementation but not the injection point). By adding the <code>Demo<\/code> qualifier to the default dao implementation, we made the other implementation a more suitable match for the injection point as it matched on type and on qualifiers and the <code>DefaultItemDao<\/code> has a qualifier that is not on the injection point making it less suitable.<\/p>\n<p>If we add the <code>Demo<\/code> annotation to the injection point and deploy it, when we click our button we use the default implementation again. This is because we are matching based on type and qualifiers and the <code>DefaultItemDao<\/code> is the only bean with both the correct type and the <code>Demo<\/code> annotation.<\/p>\n<h1>Alternative Injection Methods<\/h1>\n<p>There are multiple ways to define an injection point on the injected class. So far we have annotated the fields that will reference the injected object. You do not need to provide getters and setters for field injection. If we wish to create immutable managed beans with final fields, we can use injection in the constructor by annotating the constructor with the <code>Inject<\/code> annotation. We can then apply any annotations to constructor parameters to qualify beans for injection (of course, each parameter has a type that can assist in qualifying beans for injection). A bean may only have one constructor with injection points defined, but it may implement more than one constructor.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;itemProcessor&quot;)\r\n@RequestScoped\r\npublic class ItemProcessor {\r\n\r\n    private final ItemDao itemDao;\r\n\r\n    @Inject\r\n    public ItemProcessor(@Demo ItemDao itemDao) {\r\n        this.itemDao = itemDao;\r\n    }\r\n}\r\n<\/pre>\n<p>We can also call an inialization method which can be passed the beans to be injected.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;itemProcessor&quot;)\r\n@RequestScoped\r\npublic class ItemProcessor {\r\n\r\n    private ItemDao itemDao;\r\n\r\n    @Inject\r\n    public void setItemDao(@Demo ItemDao itemDao) {\r\n        this.itemDao = itemDao;\r\n    }\r\n} \r\n<\/pre>\n<p>While in the above case we used the setter method for initialization we can create any method and use it for initialization with as many beans as we want in the method call. We can also have multiple initialization methods in a bean. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n    @Inject\r\n    public void initBeans(@Demo ItemDao itemDao,@SomeQualifier SomeType someBean) {\r\n        this.itemDao = itemDao;\r\n        this.bean = someBean;\r\n    }\r\n\r\n<\/pre>\n<p>The same rules apply to bean matching regardless of how the injection point is defined, it will try and find the best match based on type and qualifiers and will fail on deployment if there are multiple matching beans or no matching beans for an injection point.<\/p>\n<p>Let&#8217;s look at the other aspects of our application, we get a list of items, iterate through them and test each one and if it fails that test, we pass it on to an error handler. We&#8217;ll create an <code>ItemValidator<\/code> interface to determines whether an item is valid or not.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic interface ItemValidator {\r\n    boolean isValid(Item item);\r\n}\r\n<\/pre>\n<p>We&#8217;ll expand our <code>ItemProcessor<\/code> class to incorporate the new feature.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;itemProcessor&quot;)\r\n@RequestScoped\r\npublic class ItemProcessor {\r\n\r\n    @Inject @Demo\r\n    private ItemDao itemDao;\r\n    \r\n    @Inject\r\n    private ItemValidator itemValidator;\r\n\r\n    public void execute() {\r\n      List&lt;Item&gt;  items = itemDao.fetchItems();\r\n      for (Item item : items) {          \r\n          System.out.println(&quot;Item = &quot;+item+&quot; valid = &quot;+itemValidator.isValid(item));\r\n      }\r\n    }\r\n} \r\n<\/pre>\n<p>Our first implementation will be <code>DefaultItemValidator<\/code> which will simply test the limit against the value.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class DefaultItemValidator implements ItemValidator {\r\n\r\n    public boolean isValid(Item item) {\r\n        return item.getValue() &lt; item.getLimit();\r\n    }\r\n}\r\n<\/pre>\n<p>If we save our changes, go to our web page and click our button, in the console you will see that our items are being validated and the only valid item is where the value is less than the limit.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nINFO: Item = eedemo.Item@25dd89 &#x5B;Value=34, Limit=7] valid = false\r\nINFO: Item = eedemo.Item@1f37ca2 &#x5B;Value=4, Limit=37] valid = true\r\nINFO: Item = eedemo.Item@7b8d67 &#x5B;Value=24, Limit=19] valid = false\r\nINFO: Item = eedemo.Item@18075da &#x5B;Value=89, Limit=32] valid = false\r\n<\/pre>\n<p>Now lets consider the scenario where we have a deployment to a different site that is more relaxed and considers an item invalid only if the value is more than twice the limit. We may want to have another bean that implements the validator interface for that logic.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class RelaxedItemValidator implements ItemValidator {\r\n\r\n    public boolean isValid(Item item) {\r\n        return item.getValue() &lt; (item.getLimit() * 2);\r\n    }\r\n}\r\n\r\n<\/pre>\n<p>Now we have an ambiguous dependency problem since we have two classes implementing the same interface. The only difference is based on deployment so for most deployments, we want to use the default, but for one deployment, we want to use the relaxed implementation. CDI offers the use of the <code>Alternative<\/code> annotation which lets you package multiple beans that match an injection point without ambiguity errors, and the bean to use is defined in the <code>beans.xml<\/code>. This means you can deploy both implementations in the same module with the only difference being the beans.xml definition which can change over different deployments. <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\r\n@Alternative\r\npublic class DefaultItemValidator implements ItemValidator {\r\n\r\n    public boolean isValid(Item item) {\r\n        return item.getValue() &lt; item.getLimit();\r\n    }\r\n}\r\n<\/pre>\n<p>and<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Alternative\r\npublic class RelaxedItemValidator implements ItemValidator {\r\n\r\n    public boolean isValid(Item item) {\r\n        return item.getValue() &lt; (item.getLimit() * 2);\r\n    }\r\n}\r\n<\/pre>\n<p>If we deploy our application now, we will get an unsatisfied dependency error since we defined the two matching beans as alternative but we didn&#8217;t enable either of them in the <code>beans.xml<\/code> file.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;beans\r\n    xmlns=&quot;http:\/\/java.sun.com\/xml\/ns\/javaee&quot;\r\n    xmlns:xsi=&quot;http:\/\/www.w3.org\/2001\/XMLSchema-instance&quot;\r\n    xsi:schemaLocation=&quot;\r\n    http:\/\/java.sun.com\/xml\/ns\/javaee\r\n    http:\/\/java.sun.com\/xml\/ns\/javaee\/beans_1_0.xsd&quot;&gt;\r\n    &lt;alternatives&gt;\r\n        &lt;class&gt;eedemo.RelaxedItemValidator&lt;\/class&gt;\r\n    &lt;\/alternatives&gt;\r\n&lt;\/beans&gt; \r\n<\/pre>\n<p>This tells CDI that for this deployment we want to use the <code>RelaxedItemValidator<\/code>. You can think of the alternative annotation as effectively disabling the bean making it unavailable for injection, but allowing the implementation to be packaged with the other beans. Adding it as an alternative in the <code>beans.xml<\/code> file effectively enables the bean making it available for injection. By moving this type of metadata to the <code>beans.xml<\/code> file, we can bundle different versions of the file with different deployments.<\/p>\n<h1>Handling Invalid Items<\/h1>\n<p>Continuing the example, invalid items are sent to the <code>ItemErrorHandler<\/code> as they are discovered.<\/p>\n<pre name=\"code\" class=\"java\">\r\npublic interface ItemErrorHandler {\r\n    void handleItem(Item item);\r\n}\r\n<\/pre>\n<p>Let&#8217;s start by implementing a fake handler that saves the item details to a file. We want to open the file before we start handling items, leave it open for the duration of the process as we add content to the file, and then close the file when we are done with our processing. We could manually add <code>initProcess()<\/code> and <code>finishProcess()<\/code> methods to the error reporter bean, but then we couldn&#8217;t code to the interface since the caller would need to know about those class specific methods. We could add those same methods to the <code>ItemErrorReporter<\/code> interface but then we would have to unnecessarily implement those methods in every class that implements that interface. Instead, we can use some of the lifecycle annotations from the Managed Bean spec to call methods on the bean at certain points in the bean lifecycle. A <code>PostConstruct<\/code> annotated method is called when the bean has been constructed and any dependencies the bean has have been injected. Likewise, a <code>PreDestroy<\/code> annotated method is called just before the bean is disposed of by the container.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class FileErrorReporter implements ItemErrorHandler {\r\n\r\n    @PostConstruct\r\n    public void init() {\r\n        System.out.println(&quot;Creating file error reporter&quot;);\r\n    }\r\n\r\n @PreDestroy\r\n    public void release() {\r\n        System.out.println(&quot;Closing file error reporter&quot;);\r\n    }\r\n\r\n    public void handleItem(Item item) {\r\n        System.out.println(&quot;Saving &quot;+item+&quot; to file&quot;);        \r\n    }\r\n}\r\n<\/pre>\n<p>Our final change is to add the item error handling into our <code>ItemProcessor<\/code> bean.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;itemProcessor&quot;)\r\n@RequestScoped\r\npublic class ItemProcessor {\r\n\r\n    @Inject @Demo\r\n    private ItemDao itemDao;\r\n    \r\n    @Inject\r\n    private ItemValidator itemValidator;\r\n\r\n    @Inject \r\n    private ItemErrorHandler itemErrorHandler;\r\n\r\n    public void execute() {\r\n      List&lt;Item&gt;  items = itemDao.fetchItems();\r\n      for (Item item : items) {\r\n          if (!itemValidator.isValid(item)) {\r\n              itemErrorHandler.handleItem(item);\r\n          }\r\n      }\r\n    } \r\n} \r\n<\/pre>\n<p>When we run this from our browser we see the following in the console ;<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nINFO: Creating file error reporter\r\nINFO: Saving eedemo.Item@d8b01e &#x5B;Value=34, Limit=7] to file\r\nINFO: Saving eedemo.Item@12a5e8 &#x5B;Value=89, Limit=32] to file\r\nINFO: Closing file error reporter\r\n<\/pre>\n<h1>Next Time<\/h1>\n<p>Different application deployments might use different rules for handling invalid items. such as rejecting the item, to sending notifications to individuals or just flagging them or listing them in an output file. In addition, we may want to do a combination of these (reject an order, send email to the sales rep, and list it in a file).<br \/>\nOne great way to handle this kind of multi-faceted problem is using events which we&#8217;ll look at next time.<\/p>\n<p>Click to view <a href=\"http:\/\/www.andygibson.net\/blog\/tutorial\/getting-started-with-jsf-2-0-and-cdi-part-3\/\">getting started with jsf 2.0 and CDI part 3<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In part 1, we looked at creating a JEE 6 application with Netbeans using JSF and CDI running on Glassfish. Now we&#8217;ll take a closer look at using CDI for managing dependencies in a Java EE 6 environment.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[64],"tags":[49,20,6,50,32,53,54],"_links":{"self":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts\/706"}],"collection":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/comments?post=706"}],"version-history":[{"count":51,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts\/706\/revisions"}],"predecessor-version":[{"id":1215,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts\/706\/revisions\/1215"}],"wp:attachment":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/media?parent=706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/categories?post=706"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/tags?post=706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}