{"id":5299,"date":"2012-06-19T10:53:28","date_gmt":"2012-06-19T08:53:28","guid":{"rendered":"http:\/\/blog.orange11.nl\/?p=5299"},"modified":"2012-06-19T10:53:28","modified_gmt":"2012-06-19T08:53:28","slug":"nodedata-blossom-spring-mvc","status":"publish","type":"post","link":"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/","title":{"rendered":"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers"},"content":{"rendered":"<h3>Resolve all content nodeData values in the signature of your controller method<\/h3>\n<p>When we were working on a recent Magnolia project here at Orange11, I introduced Magnolia and Blossom to a colleague of mine. Since Blossom enables the use of \u00a0Spring MVC, this enables developers to work on Magnolia integration features, without having to know the entire CMS by heart.<\/p>\n<p>One of the first things my colleague did, was addressing content node properties directly in the signature of the controller method.<br \/>\nWhen you are used to Spring MVC, you are also used to the fact that any relevant parameters in your method signature will automatically be resolved. But unfortunately I had to inform him that we only get the parameter types that come in through the\u00a0<a href=\"http:\/\/nexus.magnolia-cms.com\/content\/sites\/magnolia.public.sites\/modules\/magnolia-module-blossom\/1.2.3\/magnolia-module-blossom\/apidocs\/info\/magnolia\/module\/blossom\/web\/BlossomWebArgumentResolver.html\" target=\"_blank\" rel=\"noopener\">BlossomWebArgumentResolver<\/a> out-of-the-box.<br \/>\n<a href=\"http:\/\/tobias-mattsson-magnolia.blogspot.nl\/2011\/03\/spring-web-mvc-with-content.html\" target=\"_blank\" rel=\"noopener\">Here you can read a bit\u00a0more about Blossom&#8217;s argument resolver<\/a>,\u00a0as described by Tobias Mattsson, the founder and lead developer of Blossom.<\/p>\n<p>As the words left my mouth, we both realized that this would be a nice add-on for use in Blossom MVC controllers.<\/p>\n<p>So this is why we introduced the method parameter annotation: <em>@NodeData<\/em>.<br \/>\nThe annotation is quite useful in combination with Blossom. The argument resolver will try and resolve all annotated parameters from the current content node out of the aggregationState.<\/p>\n<p>An example of the use of this annotation:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n    @RequestMapping(&quot;\/myParagraph&quot;)\n    public String getStuffForMyBlossomParagraph(\n        ModelMap modelMap,\n        @NodeData String eventCode,\n        @NodeData(name=&quot;startDate&quot;) Date date,\n        @NodeData(defaultValue=&quot;true&quot;) boolean active) {\n        ....\n<\/pre>\n<p>The method signature above would lead in the resolving of the following parameter values out of the current content node:<\/p>\n<ul>\n<li>String <a href=\"http:\/\/nexus.magnolia-cms.com\/content\/sites\/magnolia.public.sites\/ref\/4.4.6\/apidocs\/info\/magnolia\/cms\/core\/NodeData.html\" target=\"_blank\" rel=\"noopener\">nodeData<\/a> with name <strong><em>eventCode<\/em><\/strong><\/li>\n<li>Date <a href=\"http:\/\/nexus.magnolia-cms.com\/content\/sites\/magnolia.public.sites\/ref\/4.4.6\/apidocs\/info\/magnolia\/cms\/core\/NodeData.html\" target=\"_blank\" rel=\"noopener\">nodeData<\/a> with name <strong><em>startDate<\/em><\/strong><\/li>\n<li>boolean <a href=\"http:\/\/nexus.magnolia-cms.com\/content\/sites\/magnolia.public.sites\/ref\/4.4.6\/apidocs\/info\/magnolia\/cms\/core\/NodeData.html\" target=\"_blank\" rel=\"noopener\">nodeData<\/a> with name <strong><em>active<\/em><\/strong>, that defaults to true when no value is found<\/li>\n<\/ul>\n<p>This is a very standard thing to do with Spring, but the results can be really powerful and makes the controller in combination with your content very easy to use. Also the mocking of your controller will become that much easier, because you won&#8217;t need to extract any data from a Content node anymore, but just pass the value directly as a method parameter.<\/p>\n<p>A full tutorial and zip with sourcecode after the jump.<br \/>\n<!--more--><br \/>\nAs I mentioned, creating an argumentResolver is quite trivial. But the results of such an implementation can be very useful. All that is needed for this case is:<\/p>\n<ol>\n<li>The NodeData annotation itself<\/li>\n<li>An argument resolver that resolves the actual nodeData values<\/li>\n<li>Register the argument resolver in your spring context<\/li>\n<\/ol>\n<h2>1. The NodeData annotation<\/h2>\n<p>We will start off by creating the annotation for this parameter type called <em>@NodeData<\/em>.<br \/>\nThe annotation will be accessible via reflection at runtime, and will apply to method parameters.<br \/>\nThe annotation will accept:<\/p>\n<ul>\n<li><strong>value<\/strong> an alias for the name<\/li>\n<li><strong>name<\/strong> the name of the nodeData<\/li>\n<li><strong>defaultValue<\/strong> a default value when no content was found in the node<\/li>\n<\/ul>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\npackage nl.orange11.spring.web.bind.annotation\n\/**\n * Annotation that describes a node data value\n *\n * @author erik @ Orange11\n *\n *\/\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface NodeData {\n\n    \/**\n     * Alias for String name()\n     *\n     * @return string\n     *\/\n    String value() default &quot;&quot;;\n\n    \/**\n     * The name of the nodeData\n     *\n     * @return string\n     *\/\n    String name() default &quot;&quot;;\n\n    \/**\n     * The default value\n     *\n     * @return string\n     *\/\n    String defaultValue() default &quot;&quot;;\n\n}\n<\/pre>\n<h2>2. An argument resolver that resolves the actual nodeData values<\/h2>\n<p>Now lets create the argument resolver for Spring that resolves the actual value of the parameter.<br \/>\nThe resolver displayed below is split up into four steps \/ excerpts of code. A link to the complete source in a zip (that will save you some copy paste work) is available at the bottom of this page.<\/p>\n<p><strong>One<\/strong>: Create a class that implements the WebArgumentResolver interface.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\npackage nl.orange11.spring.web.bind;\n\/**\n * WebArgumentResolver that resolves @NodeData annotated method\n * parameters to their actual node data values\n *\n * @author erik @ Orange11\n *\n *\/\npublic class NodeDataArgumentResolver implements WebArgumentResolver {\n    ...\n<\/pre>\n<p><strong>Two<\/strong>: Here we override the <em>resolveArgument<\/em> method.<br \/>\nWe try and get our <em>@NodeData<\/em> annotation. When the annotation is found, we will call a method for the actual resolving of it&#8217;s value.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n    @Override\n    public Object resolveArgument(MethodParameter methodParameter,\n            NativeWebRequest webRequest) throws Exception {\n        NodeData nodeDataAnnotation = methodParameter\n                .getParameterAnnotation(NodeData.class);\n        if (nodeDataAnnotation != null) {\n            return resolveNodeDataValue(methodParameter, nodeDataAnnotation);\n        }\n        return UNRESOLVED;\n    }\n<\/pre>\n<p><strong>Three<\/strong>: Check the name of the nodeData to resolve, of what type the parameter is, and based on that type, call the correct method resolving the correct value. Some code was left out for clarity here.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n    private Object resolveNodeDataValue(MethodParameter methodParameter,\n            NodeData nodeDataAnnotation) {\n        \/\/ set the nodeDataName to the name of the parameter\n        \/\/ or when not empty to the value or name of the annotation\n        String nodeDataName = methodParameter.getParameterName();\n        if (nodeDataAnnotation.value() != null\n                &amp;&amp; !nodeDataAnnotation.value().isEmpty()) {\n            nodeDataName = nodeDataAnnotation.value();\n        } else if (nodeDataAnnotation.name() != null\n                &amp;&amp; !nodeDataAnnotation.name().isEmpty()) {\n            nodeDataName = nodeDataAnnotation.name();\n        }\n\n        \/\/ get the default value.\n        String defaultValue = nodeDataAnnotation.defaultValue();\n\n        \/\/ get the current content node out of the aggregationState\n        Content currentContent = MgnlContext.getAggregationState()\n                .getCurrentContent();\n\n        \/\/ handle the different parameter types:\n        Class parameterType = methodParameter.getParameterType();\n        if (parameterType.equals(String.class)) {\n            return resolveString(defaultValue, currentContent, nodeDataName);\n\n        } else if (parameterType.equals(Boolean.class)\n                || parameterType.equals(boolean.class)) {\n            return resolveBoolean(defaultValue, currentContent, nodeDataName);\n\n        } else if (\n        ...\n        return null;\n    }\n<\/pre>\n<p><strong>Four<\/strong>: Now we need to resolve the different types of nodeData values. Below is an example implementation for resolving String and Boolean values.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n    private String resolveString(String defaultValue, Content currentContent,\n            String nodeDataName) {\n        \/\/ In case of a string: when empty default to null\n        if (defaultValue != null &amp;&amp; defaultValue.isEmpty()) {\n            defaultValue = null;\n        }\n        return NodeDataUtil.getString(currentContent, nodeDataName,\n                defaultValue);\n    }\n\n    private Boolean resolveBoolean(String defaultValue, Content currentContent,\n            String nodeDataName) {\n        \/\/ In case of a boolean: when empty default to false\n        if ((defaultValue != null &amp;&amp; defaultValue.isEmpty())\n            || defaultValue == null) {\n            defaultValue = Boolean.FALSE.toString();\n        }\n        return NodeDataUtil.getBoolean(currentContent, nodeDataName,\n                Boolean.valueOf(defaultValue));\n    }\n    ...\n<\/pre>\n<p>This is only an example of how to implement an argument resolver for your nodeData values. The implementation for resolving all of the other node types is also very trivial and more of the same, so left out for clarity, but available in a download at the bottom of this page.<\/p>\n<h2>3. Register the argument resolver in your spring context<\/h2>\n<p>Now that everything is in place, the only thing you would need to do here, is add your custom argument resolver to Springs&#8217;\u00a0<a href=\"http:\/\/static.springsource.org\/spring\/docs\/3.0.x\/javadoc-api\/org\/springframework\/web\/servlet\/mvc\/annotation\/AnnotationMethodHandlerAdapter.html\" target=\"_blank\" rel=\"noopener\">AnnotationMethodHandlerAdapter<\/a>.<\/p>\n<p>Locate the bean with class:<br \/>\n<em>org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter<\/em><br \/>\nin your blossom context xml, and add your custom argument resolver bean to the list already containing Blossoms&#8217; <a href=\"http:\/\/nexus.magnolia-cms.com\/content\/sites\/magnolia.public.sites\/modules\/magnolia-module-blossom\/1.2.3\/magnolia-module-blossom\/apidocs\/info\/magnolia\/module\/blossom\/web\/BlossomWebArgumentResolver.html\" target=\"_blank\" rel=\"noopener\">BlossomWebArgumentResolver<\/a>\u00a0like the example snippet below:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n    &lt;bean class=&quot;org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter&quot;&gt;\n        &lt;property name=&quot;customArgumentResolvers&quot;&gt;\n            &lt;list&gt;\n                &lt;bean class=&quot;info.magnolia.module.blossom.web.BlossomWebArgumentResolver&quot; \/&gt;\n                &lt;bean class=&quot;nl.orange11.spring.web.bind.NodeDataArgumentResolver&quot; \/&gt;\n            &lt;\/list&gt;\n        &lt;\/property&gt;\n    &lt;\/bean&gt;\n<\/pre>\n<h2>All done!<\/h2>\n<p>When this last step is complete, it&#8217;s all done, and you can start using the @NodeData annotation in your Blossom controllers!<\/p>\n<p>If and when you do implement this, do let us know how you get on, we&#8217;re always interested to hear about your experiences.<\/p>\n<p><a href=\"http:\/\/blog.orange11.nl\/wp-content\/uploads\/2012\/06\/NodeDataArgumentResolving.zip\">You can find the complete source described in this post here in a zip file<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Resolve all content nodeData values in the signature of your controller method When we were working on a recent Magnolia project here at Orange11, I introduced Magnolia and Blossom to a colleague of mine. Since Blossom enables the use of \u00a0Spring MVC, this enables developers to work on Magnolia integration features, without having to know [&hellip;]<\/p>\n","protected":false},"author":40,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[31,68,10],"tags":[297,298,95],"class_list":["post-5299","post","type-post","status-publish","format-standard","hentry","category-java","category-magnolia","category-development","tag-blossom","tag-magnolia","tag-spring-framework"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers - Trifork Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers - Trifork Blog\" \/>\n<meta property=\"og:description\" content=\"Resolve all content nodeData values in the signature of your controller method When we were working on a recent Magnolia project here at Orange11, I introduced Magnolia and Blossom to a colleague of mine. Since Blossom enables the use of \u00a0Spring MVC, this enables developers to work on Magnolia integration features, without having to know [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/\" \/>\n<meta property=\"og:site_name\" content=\"Trifork Blog\" \/>\n<meta property=\"article:published_time\" content=\"2012-06-19T08:53:28+00:00\" \/>\n<meta name=\"author\" content=\"Erik Alphenaar\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Erik Alphenaar\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/\",\"url\":\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/\",\"name\":\"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers - Trifork Blog\",\"isPartOf\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#website\"},\"datePublished\":\"2012-06-19T08:53:28+00:00\",\"author\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/63f17c99a57ab0e632f09f27214f9466\"},\"breadcrumb\":{\"@id\":\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/trifork.nl\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/trifork.nl\/blog\/#website\",\"url\":\"https:\/\/trifork.nl\/blog\/\",\"name\":\"Trifork Blog\",\"description\":\"Keep updated on the technical solutions Trifork is working on!\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/trifork.nl\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/63f17c99a57ab0e632f09f27214f9466\",\"name\":\"Erik Alphenaar\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f9734b3c518fbdb970d63dcea60b331d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f9734b3c518fbdb970d63dcea60b331d?s=96&d=mm&r=g\",\"caption\":\"Erik Alphenaar\"},\"url\":\"https:\/\/trifork.nl\/blog\/author\/erika\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers - Trifork Blog","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:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/","og_locale":"en_US","og_type":"article","og_title":"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers - Trifork Blog","og_description":"Resolve all content nodeData values in the signature of your controller method When we were working on a recent Magnolia project here at Orange11, I introduced Magnolia and Blossom to a colleague of mine. Since Blossom enables the use of \u00a0Spring MVC, this enables developers to work on Magnolia integration features, without having to know [&hellip;]","og_url":"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/","og_site_name":"Trifork Blog","article_published_time":"2012-06-19T08:53:28+00:00","author":"Erik Alphenaar","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Erik Alphenaar","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/","url":"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/","name":"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers - Trifork Blog","isPartOf":{"@id":"https:\/\/trifork.nl\/blog\/#website"},"datePublished":"2012-06-19T08:53:28+00:00","author":{"@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/63f17c99a57ab0e632f09f27214f9466"},"breadcrumb":{"@id":"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/trifork.nl\/blog\/nodedata-blossom-spring-mvc\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/trifork.nl\/blog\/"},{"@type":"ListItem","position":2,"name":"Resolve your NodeData automatically using a @NodeData parameter annotation in your Blossom powered Spring MVC controllers"}]},{"@type":"WebSite","@id":"https:\/\/trifork.nl\/blog\/#website","url":"https:\/\/trifork.nl\/blog\/","name":"Trifork Blog","description":"Keep updated on the technical solutions Trifork is working on!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/trifork.nl\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/63f17c99a57ab0e632f09f27214f9466","name":"Erik Alphenaar","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f9734b3c518fbdb970d63dcea60b331d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f9734b3c518fbdb970d63dcea60b331d?s=96&d=mm&r=g","caption":"Erik Alphenaar"},"url":"https:\/\/trifork.nl\/blog\/author\/erika\/"}]}},"_links":{"self":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/5299","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/users\/40"}],"replies":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/comments?post=5299"}],"version-history":[{"count":0,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/5299\/revisions"}],"wp:attachment":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media?parent=5299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/categories?post=5299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/tags?post=5299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}