{"id":285,"date":"2012-03-26T18:59:34","date_gmt":"2012-03-27T01:59:34","guid":{"rendered":"http:\/\/sloanseaman.com\/wordpress\/?p=285"},"modified":"2012-08-03T08:01:26","modified_gmt":"2012-08-03T15:01:26","slug":"spring-custom-tags-extensible-xml-part-1","status":"publish","type":"post","link":"http:\/\/sloanseaman.com\/wordpress\/2012\/03\/26\/spring-custom-tags-extensible-xml-part-1\/","title":{"rendered":"Spring Custom Tags (Extensible XML) &#8211; Part 1"},"content":{"rendered":"<p><b>***** WARNING &#8211; This is my Kill Bill post (was one post, was too long, is now two posts) *****<\/b><\/p>\n<p\/>\nI&#8217;m surprised by how many people don&#8217;t use a nice little ability in Spring, the custom tag support (or as they call it, Extensible XML Authoring).  Too many times you end up with drawn out bean definitions that are all flavors on a theme or you end up writing beans that do nothing but convert one object type to another to get it into some other bean object.<\/p>\n<p\/>\nA great example of this is the example that Spring uses in its documentation on creating a custom tag, creating a SimpleDateFormat external to an existing bean.  I won&#8217;t go into it too deeply (feel free to read it at <a href=\"http:\/\/static.springsource.org\/spring\/docs\/current\/spring-framework-reference\/html\/extensible-xml.html\">http:\/\/static.springsource.org\/spring\/docs\/current\/spring-framework-reference\/html\/extensible-xml.html<\/a>) but Spring allows you to write a tag that will create a SimpleDateFormat in one tag.  Ok, maybe this does require a bit of explanation.  See, in Spring you would normally do something like this:<\/p>\n<p\/>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;bean id=&quot;myFormatter&quot; class=&quot;java.text.SimpleDateFormat&quot;&gt;\r\n    &lt;constructor-arg value=&quot;yyyyMMdd&quot;\/&gt;\r\n&lt;\/bean&gt;\r\n<\/pre>\n<p\/>\nNow, that is all well and good, but with custom tags you could write some code and just do:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;mytag:dateFormat format=&quot;yyyyMMdd&quot;\/&gt;\r\n<\/pre>\n<p\/>\nShort, sweet, and to the point.<\/p>\n<p\/>\n<h3>Level up!<\/h3>\n<p>The Spring docs also give examples of slight more complex examples, but not really anything useful.  So let me show you something I did for a project I&#8217;m on that allows us to easily send List<File> into beans in a very cool (well, ok, I think it cool) and easy way while also providing file filtering.  The purpose of this example is to show you what can really be done with custom tags and how easy it is once you understand all the moving parts.<\/p>\n<p\/>\nThere are a few parts to writing a custom tag:<\/p>\n<ul>\n<li>The Spring Configuration for the Tags<\/li>\n<li>The Namespace Handler<\/li>\n<li>The XSD file<\/li>\n<li>The DefinitionParser<\/li>\n<\/ul>\n<p>I&#8217;m going to present them in the order above as I think it the most logical and easy to follow.<\/p>\n<p\/>\nNote: For the sake of this example I&#8217;m going to put everything in the XML Namespace <code>www.example.com\/core-commons<\/code>.  If you don&#8217;t know what Namespacing is in XML its (and this is simplfying it as much as I can) a way to say that certain tags are in a certain URL domain.  This makes sure that if you have to create and XML document that uses one schema that contains a <code>bean<\/code> and another schema that also contains a <code>bean<\/code> you can do so and still keep the <code>bean<\/code> tags separate.  Like I said, I tried to simplify it and I think I may have failed&#8230;<\/p>\n<h3>The Spring Configuration for the Tags<\/h3>\n<p>To get custom tags to work you first have to let Spring know that they exist.  This is actually quite simple. First, create a file in the root of your META-INF named <code>spring.schemas<\/code> and place the Namespace of your tag and the .xsd&#8217;s location in it:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\nhttp\\:\/\/www.example.com\/schema\/core-commons-1.0.xsd=com\/example\/core\/commons\/tag\/core-commons-1.0.xsd\r\n<\/pre>\n<p>Few things to note:<\/p>\n<ul>\n<li>The http\\: is required to get around the way things are parsed by Spring. You NEED that \\ before the :<\/li>\n<li>There is nothing wrong with placing the xsd inside a package structure. I prefer it as it ensured you don&#8217;t collide ont he classpath with another file of the same name from a different package<\/li>\n<li>Always put a version number on your xsd.  That way you can control versions at a later date<\/li>\n<\/ul>\n<p\/>\nSo that told Spring where it can find the XSD file to enfore the XML validation rules.  Next we need to tell Spring where it can find the code to handle the different tags you may have created.  This is placed in a file in the root of your META-INF named <code>spring.handlers<\/core> and looks like this:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\nhttp\\:\/\/www.example.com\/schema\/core-commons-1.0.xsd=com.example.core.commons.tag.CoreNamespaceHandler\r\n<\/pre>\n<p>As you can see the Namespace definition is the same as above but now we has specified a class that will handle the Namespace for us (should Namespace be capitalized like that?  I'm not sure, lets stick with it for now shall we?)<\/p>\n<p\/>\n<h3>The Namespace Handler<\/h3>\n<p>The NamespaceHandler (defined as <code>com.example.core.commons.tag.CoreNamespaceHandler<\/code> above) tells Spring what code to execute for whatever tag is used.  The easiest way to do this is to extend <code>org.springframework.beans.factory.xml.NamespaceHandlerSupport<\/code>.  For this example where we will support sending List<File> into beans my code looks like:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.example.core.commons.tag;\r\n\r\nimport org.springframework.beans.factory.xml.NamespaceHandlerSupport;\r\n\r\npublic class CoreNamespaceHandler\r\n    extends NamespaceHandlerSupport\r\n{\r\n\r\n    @Override\r\n    public void init() {\r\n        this.registerBeanDefinitionParser(&quot;fileList&quot;, new FileListDefinitionParser());\r\n        this.registerBeanDefinitionParser(&quot;fileFilter&quot;, new FileFilterDefinitionParser());\r\n    }\r\n}\r\n<\/pre>\n<p>What the code is doing is saying \"for the tag 'fileList' parse it with the object FileListDefinitionParser\".  Not much else to it.  See, I told you custom tags are easy!  Well, so far anyway.<\/p>\n<p\/>\n<h3>The XSD file<\/h3>\n<p>So, you have it so Spring knows where things are and what it should fire when it hits the custom tags, but what do the tags actually look like?  What do they support in terms of child elements and attributes?<\/p>\n<p\/>\nFor that you need to define the <code>.xsd<\/code> for the tag.   We already defined the name and location of the <code>.xsd<\/code> tag above in the <code>spring.schemas<\/code> file.  Now lets make it.  First, create the <code>.xsd<\/code> in the correct location (com\/example\/core\/commons\/tag) and name it <code>core-commons-1.0.xsd<\/code>.  In this file place your xsd.  For this example it's :<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\r\n&lt;xsd:schema xmlns=&quot;http:\/\/www.example.com\/schema\/core-commons-1.0&quot; \r\n\ttargetNamespace=&quot;http:\/\/www.example.com\/schema\/core-commons-1.0&quot; \r\n\txmlns:xsi=&quot;http:\/\/www.w3.org\/2001\/XMLSchema-instance&quot; \r\n\txmlns:xsd=&quot;http:\/\/www.w3.org\/2001\/XMLSchema&quot; \r\n\txmlns:beans=&quot;http:\/\/www.springframework.org\/schema\/beans&quot; \r\n\telementFormDefault=&quot;qualified&quot; \r\n\tattributeFormDefault=&quot;unqualified&quot;\r\n\tversion=&quot;1.0&quot;&gt;\r\n\t\r\n\t&lt;xsd:import namespace=&quot;http:\/\/www.springframework.org\/schema\/beans&quot; schemaLocation=&quot;http:\/\/www.springframework.org\/schema\/beans\/spring-beans-3.0.xsd&quot;\/&gt;\r\n\t\r\n    &lt;xsd:element name=&quot;fileList&quot;&gt;\r\n        &lt;xsd:complexType&gt;\r\n            &lt;xsd:complexContent&gt;\r\n                &lt;xsd:extension base=&quot;beans:identifiedType&quot;&gt;\r\n                    &lt;xsd:sequence&gt;\r\n                        &lt;xsd:element ref=&quot;fileFilter&quot; minOccurs=&quot;0&quot; maxOccurs=&quot;1&quot;\/&gt;\r\n                        &lt;xsd:element ref=&quot;fileList&quot; minOccurs=&quot;0&quot; maxOccurs=&quot;unbounded&quot;\/&gt;\r\n                    &lt;\/xsd:sequence&gt;\r\n                    &lt;xsd:attribute name=&quot;directory&quot; type=&quot;xsd:string&quot;\/&gt;\r\n                    &lt;xsd:attribute name=&quot;scope&quot; type=&quot;xsd:string&quot;\/&gt;\r\n                &lt;\/xsd:extension&gt;\r\n            &lt;\/xsd:complexContent&gt;\r\n        &lt;\/xsd:complexType&gt;\r\n    &lt;\/xsd:element&gt;\r\n    \r\n    &lt;xsd:element name=&quot;fileFilter&quot;&gt;\r\n        &lt;xsd:complexType&gt;\r\n            &lt;xsd:complexContent&gt;\r\n                &lt;xsd:extension base=&quot;beans:identifiedType&quot;&gt;\r\n                    &lt;xsd:group ref=&quot;limitedType&quot;\/&gt;\r\n                    &lt;xsd:attribute name=&quot;scope&quot; type=&quot;xsd:string&quot;\/&gt;\r\n                &lt;\/xsd:extension&gt;\r\n            &lt;\/xsd:complexContent&gt;\r\n        &lt;\/xsd:complexType&gt;\r\n    &lt;\/xsd:element&gt;\r\n\r\n    &lt;xsd:group name=&quot;limitedType&quot;&gt;\r\n        &lt;xsd:sequence&gt;\r\n            &lt;xsd:choice minOccurs=&quot;1&quot; maxOccurs=&quot;unbounded&quot;&gt;\r\n                &lt;xsd:element ref=&quot;beans:bean&quot;\/&gt;\r\n                &lt;xsd:element ref=&quot;beans:ref&quot;\/&gt;\r\n                &lt;xsd:element ref=&quot;beans:idref&quot;\/&gt;\r\n                &lt;xsd:element ref=&quot;beans:value&quot;\/&gt;\r\n                &lt;xsd:any minOccurs=&quot;0&quot;\/&gt;\r\n            &lt;\/xsd:choice&gt;\r\n        &lt;\/xsd:sequence&gt;\r\n    &lt;\/xsd:group&gt;\r\n&lt;\/xsd:schema&gt;\r\n<\/pre>\n<p>Ok, now, if you don't know XSD you may be pretty lost at this point and I would be to so let me try to put what is going on in simple terms without taking the time to teach XSD (there are many tutorials on that already). I'm going to do this by line and I'll skip the lines that really are not that important but you should keep in<\/p>\n<p\/>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:schema xmlns=&quot;http:\/\/www.example.com\/schema\/core-commons-1.0&quot; \r\n<\/pre>\n<p>The default Namespace of this document<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\ntargetNamespace=&quot;http:\/\/www.example.com\/schema\/core-commons-1.0&quot; \r\n<\/pre>\n<p>Define the Namespace this file represents.  It should match what you put in <code>spring.schemas<\/code> (this first part)<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\nxmlns:beans=&quot;http:\/\/www.springframework.org\/schema\/beans&quot; \r\n<\/pre>\n<p>On line 11 we pull in the Spring XSD.  So we have access to its tags we place it in the <code>beans<\/code> Namespace<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:import namespace=&quot;http:\/\/www.springframework.org\/schema\/beans&quot; schemaLocation=&quot;http:\/\/www.springframework.org\/schema\/beans\/spring-beans-3.0.xsd&quot;\/&gt;\r\n<\/pre>\n<p>Pull in the Spring XSD file<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:element name=&quot;fileList&quot;&gt;\r\n<\/pre>\n<p>Define our first tag, <code>fileList<\/code><\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:extension base=&quot;beans:identifiedType&quot;&gt;\r\n<\/pre>\n<p>Extend Springs <code>identifiedType<\/code>.  This, in Spring, is what decides if a tag can have an <code>id<\/code> attribute on it.  Remember that nested Spring beans can't have the <code>id<\/code> tag on them.  This helps with that<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:element ref=&quot;fileFilter&quot; minOccurs=&quot;0&quot; maxOccurs=&quot;1&quot;\/&gt;\r\n<\/pre>\n<p>Allow a child tag named <code>fileFilter<\/code> that can occur 0..1 times<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:element ref=&quot;fileList&quot; minOccurs=&quot;0&quot; maxOccurs=&quot;unbounded&quot;\/&gt;\r\n<\/pre>\n<p>Allow a child tag named <code>fileList<\/code> (hey! That's what we are doing now. We're going recursive! Woot!) that can occur 0..N times<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:attribute name=&quot;directory&quot; type=&quot;xsd:string&quot;\/&gt;\r\n<\/pre>\n<p><Define an attribute named <code>directory<\/code>.  This will allow us to define the directory we want to read files from<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:attribute name=&quot;scope&quot; type=&quot;xsd:string&quot;\/&gt;\r\n<\/pre>\n<p>Define an attribute named <code>scope<\/code>.  This will allow us to set the scope of the bean (Later in the code you will see that I also support Spring Batch&#8217;s <code>step<\/code> scope<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:element name=&quot;fileFilter&quot;&gt;\r\n<\/pre>\n<p>Define a tag named <code>fileFilter<\/code>. Since we have defined it this way it can be used on its own or, since like 18 defined it as a child of <code>fileList<\/code>, it can be used there as well<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:group ref=&quot;limitedType&quot;\/&gt;\r\n<\/pre>\n<p>Bring in a group of already defined (see line 39) tags that we will support as children to <code>fileFilter<\/code><\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:group name=&quot;limitedType&quot;&gt;\r\n<\/pre>\n<p>Define a group of tags for use in other tags<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:element ref=&quot;beans:bean&quot;\/&gt;\r\n&lt;xsd:element ref=&quot;beans:ref&quot;\/&gt;\r\n&lt;xsd:element ref=&quot;beans:idref&quot;\/&gt;\r\n&lt;xsd:element ref=&quot;beans:value&quot;\/&gt;\r\n<\/pre>\n<p>Add support for a set of Spring tags.  This is the main reason we imported the Spring XSD on line 11<\/br><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;xsd:any minOccurs=&quot;0&quot;\/&gt;\r\n<\/pre>\n<p><code>any<\/code> allows us to support any other tag that may be defined (additional custom tags perhaps?)<\/p>\n<p\/>\nI hope that wasn&#8217;t too hard to follow.  If you know XSD it shouldn&#8217;t have been.  If you don&#8217;t know XSD and are still very confused please ask questions and I&#8217;ll do my best to answer.<\/p>\n<p\/>\n<p>This concludes Part 1.  In Part two I&#8217;ll go into the code that is actually involved \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>***** WARNING &#8211; This is my Kill Bill post (was one post, was too long, is now two posts) ***** I&#8217;m surprised by how many people don&#8217;t use a nice little ability in Spring, the custom tag support (or as &hellip; <a href=\"http:\/\/sloanseaman.com\/wordpress\/2012\/03\/26\/spring-custom-tags-extensible-xml-part-1\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7,10],"tags":[29,15],"_links":{"self":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/285"}],"collection":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/comments?post=285"}],"version-history":[{"count":29,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/285\/revisions"}],"predecessor-version":[{"id":377,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/285\/revisions\/377"}],"wp:attachment":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/media?parent=285"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/categories?post=285"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/tags?post=285"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}