{"id":313,"date":"2012-04-08T17:41:42","date_gmt":"2012-04-09T00:41:42","guid":{"rendered":"http:\/\/sloanseaman.com\/wordpress\/?p=313"},"modified":"2012-04-10T10:49:59","modified_gmt":"2012-04-10T17:49:59","slug":"spring-custom-tags-extensible-xml-part-2","status":"publish","type":"post","link":"http:\/\/sloanseaman.com\/wordpress\/2012\/04\/08\/spring-custom-tags-extensible-xml-part-2\/","title":{"rendered":"Spring Custom Tags (Extensible XML) &#8211; Part 2"},"content":{"rendered":"<h3>The DefinitionParser<\/h3>\n<p>Ok, we&#8217;ve got it all set up, now we need to code the thing.  Let me state, if you didn&#8217;t grasp this, that this is more of an advanced example.  If you want a basic example take a look at the Spring documentation linked to at the top of the document.<\/p>\n<p\/>\nYou still here?  Ok, I warned you. Follow me into the depths of code \ud83d\ude42<\/p>\n<p\/>\nWell, what does custom tag code do?  In a nutshell it is in charge of registering a <code>org.springframework.beans.factory.FactoryBean<\/code> wherever your tag exists in your XML so that when it comes time to use the value that your tag represents it can easily get it following the Spring FactoryBean methodology.<\/p>\n<p\/>\nAny class that wishes to handle the parsing of custom tags must implement <code>org.springframework.beans.factory.xml.BeanDefinitionParser<\/code>.  It&#8217;s just an interface that passed in the W3C Dom Element that is the tag itself and the Context in which the parsing is occurring.  The Element object is easy enough, but what is the Context?<\/p>\n<p>\nSpring passes you an instance of <code>org.springframework.beans.factory.xml.ParserContext<\/code> that represents where the tag is in the context of things (example: is it a nested tag?) as well as references to the BeanDefinitionRegistry (where Spring keeps info on all the beans you have every defined) and a few other things.  This is helpful later as you may want to lookup another bean or perhaps treat the bean created from your tag differently if it is nested.<\/p>\n<p\/>\nLets approach this by following the tag structure.  First we will write the code that will parse the <code>fileList<\/code> followed by the <code>fileFilter<\/code>.  Lastly we will code a util to handle the Spring bean\/ref\/idref\/value tags.  I&#8217;m making this a util since it might be handy for other tags in the future.<\/p>\n<p\/>\n<h4>FileListDefinitionParser<\/h4>\n<p>The <code>FileListDefinitionParser<\/code> is in charge of handling the parsing of the <code>fileList<\/code> tag.  To make this easier to follow I&#8217;m going to put the comments about the code directly in the code (my college professors would be so proud of me) so that I can explain things better.<\/p>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\npackage com.example.core.commons.tag;\r\n\r\nimport java.io.File;\r\nimport java.io.FileFilter;\r\nimport java.util.ArrayList;\r\nimport java.util.Arrays;\r\nimport java.util.Collection;\r\nimport java.util.HashSet;\r\nimport java.util.List;\r\n\r\nimport org.springframework.beans.factory.FactoryBean;\r\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\r\nimport org.springframework.beans.factory.support.ManagedList;\r\nimport org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;\r\nimport org.springframework.beans.factory.xml.ParserContext;\r\nimport org.springframework.util.xml.DomUtils;\r\nimport org.w3c.dom.Element;\r\nimport org.w3c.dom.NodeList;\r\n\r\n\/**\r\n * Returns a list of files that are in a directory.  The list may be limited\r\n * by nesting a core-commons:fileFilter tag that will do the intersection\r\n * of all the fileFilters vs what is in the directory.\r\n * &lt;p\/&gt;\r\n *\r\n * Also supports the nesting of beans, idref, ref, and value tags that return\r\n * File objects (the value tag's value will be converted to a new File)\r\n *\r\n * &lt;p\/&gt;\r\n * Examples:\r\n * &lt;br\/&gt;\r\n *\r\n * Get all the files in the current directory\r\n * &lt;core-commons:fileList directory=&quot;.&quot;\/&gt;\r\n *\r\n * Get all the files in the current directory that end in XML\r\n * &lt;core-commons:fileList directory=&quot;.&quot;&gt;\r\n * \t\t&lt;core-commons:fileFilter&gt;\r\n *      \t&lt;bean class=&quot;org.apache.commons.io.filefilter.RegexFileFilter&quot;&gt;\r\n *          \t&lt;constructor-arg value=&quot;.*.xml&quot;\/&gt;\r\n *           &lt;\/bean&gt;\r\n *      &lt;\/core-commons:fileFilter&gt;\r\n * &lt;\/core-commons:fileList&gt;\r\n *\r\n * Get all the files in the current directory that end in XML (specify the fileList separately)\r\n * &lt;core-commons:fileList&gt;\r\n *  \t&lt;core-commons:fileFilter&gt;\r\n *      \t&lt;bean class=&quot;org.apache.commons.io.filefilter.RegexFileFilter&quot;&gt;\r\n *          \t&lt;constructor-arg value=&quot;.*.xml&quot;\/&gt;\r\n *          &lt;\/bean&gt;\r\n *      &lt;\/core-commons:fileFilter&gt;\r\n *      &lt;core-commons:fileList directory=&quot;.&quot;\/&gt;\r\n * &lt;\/core-commons:fileList&gt;\r\n *\r\n * Get all files in the \/tmp and \/something directory that end in .xml\r\n * &lt;core-commons:fileList directory=&quot;\/tmp&quot;&gt;\r\n * \t\t&lt;core-commons:fileFilter&gt;\r\n * \t\t\t&lt;bean class=&quot;org.apache.commons.io.filefilter.RegexFileFilter&quot;&gt;\r\n * \t\t\t\t&lt;constructor-arg value=&quot;.*.xml&quot;\/&gt;\r\n * \t\t\t&lt;\/bean&gt;\r\n * \t\t&lt;\/core-commons:fileFilter&gt;\r\n * \t\t&lt;core-commons:fileList directory=&quot;\/something&quot;\/&gt;\r\n * &lt;\/core-commons:fileList&gt;\r\n *\r\n * Get all files in the \/tmp and \/something directory that end in .xml and can be written to\r\n * &lt;core-commons:fileList directory=&quot;\/tmp&quot;&gt;\r\n * \t\t&lt;core-commons:fileFilter&gt;\r\n * \t\t\t&lt;bean class=&quot;org.apache.commons.io.filefilter.CanWriteFileFilter&quot;\/&gt;\r\n * \t\t\t&lt;bean class=&quot;org.apache.commons.io.filefilter.RegexFileFilter&quot;&gt;\r\n * \t\t\t\t&lt;constructor-arg value=&quot;.*.xml&quot;\/&gt;\r\n * \t\t\t&lt;\/bean&gt;\r\n * \t\t&lt;\/core-commons:fileFilter&gt;\r\n * \t\t&lt;core-commons:fileList directory=&quot;\/something&quot;\/&gt;\r\n * &lt;\/core-commons:fileList&gt;\r\n *\r\n * Get all files in the \/tmp directory and the pom.xml file in another directory\r\n * &lt;core-commons:fileList&gt;\r\n * \t\t&lt;core-commons:fileList directory=&quot;\/tmp&quot;\/&gt;\r\n * \t\t&lt;value&gt;pom.xml&lt;\/value&gt;\r\n * &lt;\/core-commons:fileList&gt;\r\n *\r\n * @author seamans\r\n *\r\n *\/\r\npublic class FileListDefinitionParser\r\n\textends AbstractSingleBeanDefinitionParser\r\n{\r\n\r\n\t\/**\r\n\t * The bean that is created for this tag element\r\n\t * \r\n\t * @param element The tag element\r\n\t * @return A FileListFactoryBean\r\n\t *\/\r\n\t@Override\r\n\tprotected Class&lt;?&gt; getBeanClass(Element element) {\r\n\t\treturn FileListFactoryBean.class;\r\n\t}\r\n\r\n\t\/**\r\n\t * Called when the fileList tag is to be parsed\r\n\t * \r\n\t * @param element The tag element\r\n\t * @param ctx The context in which the parsing is occuring\r\n\t * @param builder The bean definitions build to use\r\n\t *\/\r\n\t@Override\r\n\tprotected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) {\r\n\t\t\/\/ Set the directory property\r\n\t\tbuilder.addPropertyValue(&quot;directory&quot;, element.getAttribute(&quot;directory&quot;));\r\n\t\t\r\n\t\t\/\/ Set the scope\r\n\t\tbuilder.setScope(element.getAttribute(&quot;scope&quot;));\r\n\r\n\t\t\/\/ We want any parsing to occur as a child of this tag so we need to make\r\n\t\t\/\/ a new one that has this as it's owner\/parent\r\n\t\tParserContext nestedCtx = new ParserContext(ctx.getReaderContext(), ctx.getDelegate(), builder.getBeanDefinition());\r\n\r\n\t\t\/\/ Support for filters\r\n\t\tElement exclusionElem = DomUtils.getChildElementByTagName(element, &quot;fileFilter&quot;);\r\n\t\tif (exclusionElem != null) {\r\n\t\t\t\/\/ Just make a new Parser for each one and let the parser do the work\r\n\t\t\tFileFilterDefinitionParser ff = new FileFilterDefinitionParser();\r\n\t\t\tbuilder.addPropertyValue(&quot;filters&quot;, ff.parse(exclusionElem, nestedCtx));\r\n\t\t}\r\n\r\n\t\t\/\/ Support for nested fileList\r\n\t\tList&lt;Element&gt; fileLists = DomUtils.getChildElementsByTagName(element, &quot;fileList&quot;);\r\n\t\t\/\/ Any objects that created will be placed in a ManagedList\r\n\t\t\/\/ so Spring does the bulk of the resolution work for us\r\n\t\tManagedList&lt;Object&gt; nestedFiles = new ManagedList&lt;Object&gt;();\r\n\t\tif (fileLists.size() &gt; 0) {\r\n\t\t\t\/\/ Just make a new Parser for each one and let them do the work\r\n\t\t\tFileListDefinitionParser fldp = new FileListDefinitionParser();\r\n\t\t\tfor (Element fileListElem : fileLists) {\r\n\t\t\t\tnestedFiles.add(fldp.parse(fileListElem, nestedCtx));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/\/ Support for other tags that return File (value will be converted to file)\r\n\t\ttry {\r\n\t\t\t\/\/ Go through any other tags we may find.  This does not mean we support\r\n\t\t\t\/\/ any tag, we support only what parseLimitedList will process\r\n\t\t\tNodeList nl = element.getChildNodes();\r\n\t\t\tfor (int i=0; i&lt;nl.getLength(); i++) {\r\n\t\t\t\t\/\/ Parse each child tag we find in the correct scope but we \r\n\t\t\t\t\/\/ won't support custom tags at this point as it coudl destablize things\r\n\t\t\t\tDefinitionParserUtil.parseLimitedList(nestedFiles, nl.item(i), ctx,\r\n\t\t\t\t\tbuilder.getBeanDefinition(), element.getAttribute(&quot;scope&quot;), false);\r\n\t\t\t}\r\n\t\t}\r\n\t\tcatch (Exception e) {\r\n\t\t\tthrow new RuntimeException(e);\r\n\t\t}\r\n\r\n\t\t\/\/ Set the nestedFiles in the properties so it is set on the FactoryBean\r\n\t\tbuilder.addPropertyValue(&quot;nestedFiles&quot;, nestedFiles);\r\n\r\n\t}\r\n\r\n\tpublic static class FileListFactoryBean\r\n\t\timplements FactoryBean&lt;Collection&lt;File&gt;&gt;\r\n\t{\r\n\r\n\t\tString directory;\r\n\t\tprivate Collection&lt;FileFilter&gt; filters;\r\n\t\tprivate Collection&lt;File&gt; nestedFiles;\r\n\r\n\t\t@Override\r\n\t\tpublic Collection&lt;File&gt; getObject() throws Exception {\r\n\t\t\t\/\/ These can be an array list because the directory will have unique's and the nested is already only unique's\r\n\t\t\tCollection&lt;File&gt; files = new ArrayList&lt;File&gt;();\r\n\t\t\tCollection&lt;File&gt; results = new ArrayList&lt;File&gt;(0);\r\n\r\n\t\t\tif (directory != null) {\r\n\t\t\t\t\/\/ get all the files in the directory\r\n\t\t\t\tFile dir = new File(directory);\r\n\t\t\t\tFile[] dirFiles = dir.listFiles();\r\n\t\t\t\tif (dirFiles != null) {\r\n\t\t\t\t\tfiles = Arrays.asList(dirFiles);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t\/\/ If there are any files that were created from the nested tags,\r\n\t\t\t\/\/ add those to the list of files\r\n\t\t\tif (nestedFiles != null) {\r\n\t\t\t\tfiles.addAll(nestedFiles);\r\n\t\t\t}\r\n\r\n\t\t\t\/\/ If there are filters we need to go through each filter\r\n\t\t\t\/\/ and see if the files in the list pass the filters.\r\n\t\t\t\/\/ If the files does not pass any one of the filters then it\r\n\t\t\t\/\/ will not be included in the list\r\n\t\t\tif (filters != null) {\r\n\t\t\t\tboolean add;\r\n\t\t\t\tfor (File f : files) {\r\n\t\t\t\t\tadd = true;\r\n\t\t\t\t\tfor (FileFilter ff : filters) {\r\n\t\t\t\t\t\tif (!ff.accept(f)) {\r\n\t\t\t\t\t\t\tadd = false;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (add) results.add(f);\r\n\t\t\t\t}\r\n\t\t\t\treturn results;\r\n\t\t\t}\r\n\r\n\t\t\treturn files;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic Class&lt;?&gt; getObjectType() {\r\n\t\t\treturn Collection.class;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic boolean isSingleton() {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tpublic void setDirectory(String dir) {\r\n\t\t\tthis.directory = dir;\r\n\t\t}\r\n\r\n\t\tpublic void setFilters(Collection&lt;FileFilter&gt; filters) {\r\n\t\t\tthis.filters = filters;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * What we actually get from the processing of the nested tags\r\n\t\t * is a collection of files within a collection so we flatten it and\r\n\t\t * only keep the uniques\r\n\t\t *\/\r\n\t\tpublic void setNestedFiles(Collection&lt;Collection&lt;File&gt;&gt; nestedFiles) {\r\n\t\t\tthis.nestedFiles = new HashSet&lt;File&gt;(); \/\/ keep the list unique\r\n\t\t\tfor (Collection&lt;File&gt; nested : nestedFiles) {\r\n\t\t\t\tthis.nestedFiles.addAll(nested);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n}\r\n\r\n\r\n<\/pre>\n<p>You can see in the code above that I directly reference <code>FileFilterDefinitionParser<\/code> which is the part of the code that lets us filter any files that are in the list of files in the directory.  Lets go into that code:<\/p>\n<h4>FileFilterDefinitionParser<\/h4>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\npackage com.example.core.commons.tag;\r\n\r\nimport java.io.FileFilter;\r\nimport java.util.ArrayList;\r\nimport java.util.Collection;\r\nimport java.util.List;\r\n\r\nimport org.apache.commons.io.filefilter.NameFileFilter;\r\nimport org.springframework.beans.factory.FactoryBean;\r\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\r\nimport org.springframework.beans.factory.support.ManagedList;\r\nimport org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;\r\nimport org.springframework.beans.factory.xml.ParserContext;\r\nimport org.w3c.dom.Element;\r\nimport org.w3c.dom.NodeList;\r\n\r\n\/**\r\n * Creates a list of FileFilters based on the configured value, ref, idRef, or bean.\r\n * &lt;p\/&gt;\r\n *\r\n * Note: value defaults to a NamedFileFilter\r\n *\r\n * Example:\r\n *\r\n * Create a NamedFileFilter that will filter for the specific name\r\n *   &lt;core-commons:fileFilter id=&quot;a&quot;&gt;\r\n *       &lt;value&gt;someFile.txt&lt;\/value&gt;\r\n *   &lt;\/core-commons:fileFilter&gt;\r\n *\r\n * Create a filter that will filter for any file ending in .xml and that is writable\r\n *   &lt;core-commons:fileFilter id=&quot;a&quot;&gt;\r\n *       &lt;bean class=&quot;org.apache.commons.io.filefilter.CanWriteFileFilter&quot;\/&gt;\r\n *       &lt;bean class=&quot;org.apache.commons.io.filefilter.RegexFileFilter&quot;&gt;\r\n *           &lt;constructor-arg value=&quot;.*.xml&quot;\/&gt;\r\n *       &lt;\/bean&gt;\r\n *   &lt;\/core-commons:fileFilter&gt;\r\n *\r\n * @author seamans\r\n *\r\n *\/\r\npublic class FileFilterDefinitionParser\r\n\textends AbstractSingleBeanDefinitionParser\r\n{\r\n\r\n\t\/**\r\n\t * The bean that is created for this tag element\r\n\t * \r\n\t * @param element The tag element\r\n\t * @return A FileFilterFactoryBean\r\n\t *\/\r\n\t@Override\r\n\tprotected Class&lt;?&gt; getBeanClass(Element element) {\r\n\t\treturn FileFilterFactoryBean.class;\r\n\t}\r\n\r\n\t\/**\r\n\t * Called when the fileFilter tag is to be parsed\r\n\t * \r\n\t * @param element The tag element\r\n\t * @param ctx The context in which the parsing is occuring\r\n\t * @param builder The bean definitions build to use\r\n\t *\/\r\n\t@Override\r\n\tprotected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) {\r\n\t\t\r\n\t\t\/\/ Set the scope\r\n\t\tbuilder.setScope(element.getAttribute(&quot;scope&quot;));\r\n\t\t\r\n\t\ttry {\r\n\t\t\t\/\/ All of the filters will eventually end up in this list\r\n\t\t\t\/\/ We use a 'ManagedList' and not a regular list because anything\r\n\t\t\t\/\/ placed in a ManagedList object will support all of Springs\r\n\t\t\t\/\/ functionalities and scopes for us, we dont' have to code anything\r\n\t\t\t\/\/ in terms of reference lookups, EL, etc\r\n\t\t\tManagedList&lt;Object&gt; filters = new ManagedList&lt;Object&gt;();\r\n\r\n\t\t\t\/\/ For each child node of the fileFilter tag, parse it and place it\r\n\t\t\t\/\/ in the filtes list\r\n\t\t\tNodeList nl = element.getChildNodes();\r\n\t\t\tfor (int i=0; i&lt;nl.getLength(); i++) {\r\n\t\t\t\tDefinitionParserUtil.parseLimitedList(filters, nl.item(i), ctx, builder.getBeanDefinition(), element.getAttribute(&quot;scope&quot;));\r\n\t\t\t}\r\n\r\n\t\t\t\/\/ Add the filtes to the list of properties (this is applied\r\n\t\t\t\/\/ to the factory beans setFilters below)\r\n\t\t\tbuilder.addPropertyValue(&quot;filters&quot;, filters);\r\n\t\t}\r\n\t\tcatch (Exception e) {\r\n\t\t\tthrow new RuntimeException(e);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static class FileFilterFactoryBean\r\n\t\timplements FactoryBean&lt;Collection&lt;FileFilter&gt;&gt;\r\n\t{\r\n\r\n\t\tprivate final List&lt;FileFilter&gt; filters = new ArrayList&lt;FileFilter&gt;();\r\n\r\n\t\t@Override\r\n\t\tpublic Collection&lt;FileFilter&gt; getObject() throws Exception {\r\n\t\t\treturn filters;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic Class&lt;?&gt; getObjectType() {\r\n\t\t\treturn Collection.class;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic boolean isSingleton() {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Go through the list of filters and convert the String ones\r\n\t\t * (the ones that were set with &lt;value&gt; and make them NameFileFilters\r\n\t\t *\/\r\n\t\tpublic void setFilters(Collection&lt;Object&gt; filterList) {\r\n\t\t\tfor (Object o : filterList) {\r\n\t\t\t\tif (o instanceof String) {\r\n\t\t\t\t\tfilters.add(new NameFileFilter(o.toString()));\r\n\t\t\t\t}\r\n\t\t\t\telse if (o instanceof FileFilter) {\r\n\t\t\t\t\tfilters.add((FileFilter)o);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n}\r\n\r\n<\/pre>\n<p>As you saw in the XSD definition (previous post) for the <code>fileFilter<\/code> tag it supports child tags that are standard Spring tags (bean\/ref\/idref\/value).  We need to code support for this since unfortunately Spring does NOT provide a helper class for parsing its own tags.  I was a bit disappointed in this and I have to admit that most of my time went into the coding of this util class and how I could support the Spring <code>bean<\/code> tag properly.\n<\/p>\n<p>This piece of code may be of interest to others as it shows how to create a Spring bean programmatically and still get all its functionality<\/p>\n<p\/>\n<h4>DefinitionParserUtil<\/h4>\n<pre class=\"brush: java; title: ; wrap-lines: false; notranslate\" title=\"\">\r\npackage com.example.core.commons.tag;\r\npackage com.broadridge.adc.core.commons.tag;\r\n\r\nimport org.springframework.beans.factory.config.BeanDefinition;\r\nimport org.springframework.beans.factory.config.BeanDefinitionHolder;\r\nimport org.springframework.beans.factory.support.BeanDefinitionReaderUtils;\r\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\r\nimport org.springframework.beans.factory.support.ManagedList;\r\nimport org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;\r\nimport org.springframework.beans.factory.xml.ParserContext;\r\nimport org.springframework.expression.Expression;\r\nimport org.springframework.expression.ExpressionParser;\r\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\r\nimport org.w3c.dom.Element;\r\nimport org.w3c.dom.Node;\r\n\r\npublic class DefinitionParserUtil {\r\n\r\n\t\/**\r\n\t * Parses the children of the passed in ParentNode for the following tags:\r\n\t * &lt;br\/&gt;\r\n\t * value\r\n\t * ref\r\n\t * idref\r\n\t * bean\r\n\t * property\r\n\t * *custom*\r\n\t * &lt;p\/&gt;\r\n\t *\r\n\t * The value tag works with Spring EL even in a Spring Batch scope=&quot;step&quot;\r\n\t *\r\n\t * @param objects The list of resultings objects from the parsing (passed in for recursion purposes)\r\n\t * @param parentNode The node who's children should be parsed\r\n\t * @param ctx The ParserContext to use\r\n\t * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean\r\n\t * \t\t(i.e. the Bean that is the parentNode)\r\n\t * @param scope The scope to execute in.  Checked if 'step' to provide Spring EL\r\n\t * \t\tsupport in a Spring Batch env\r\n\t * @throws Exception\r\n\t *\/\r\n\tpublic static void parseLimitedList(ManagedList&lt;Object&gt; objects, Node node,\r\n\t\tParserContext ctx, BeanDefinition parentBean, String scope)\r\n\t\tthrows Exception\r\n\t{\r\n\t\tparseLimitedList(objects, node, ctx, parentBean, scope, true);\r\n\t}\r\n\r\n\t\/**\r\n\t * Parses the children of the passed in ParentNode for the following tags:\r\n\t * &lt;br\/&gt;\r\n\t * value\r\n\t * ref\r\n\t * idref\r\n\t * bean\r\n\t * property\r\n\t * *custom*\r\n\t * &lt;p\/&gt;\r\n\t *\r\n\t * The value tag works with Spring EL even in a Spring Batch scope=&quot;step&quot;\r\n\t *\r\n\t * @param objects The list of resultings objects from the parsing (passed in for recursion purposes)\r\n\t * @param parentNode The node who's children should be parsed\r\n\t * @param ctx The ParserContext to use\r\n\t * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean\r\n\t * \t\t(i.e. the Bean that is the parentNode)\r\n\t * @param scope The scope to execute in.  Checked if 'step' to provide Spring EL\r\n\t * \t\tsupport in a Spring Batch env\r\n\t * @param supportCustomTags Should we support custom tags within our tags?\r\n\t * @throws Exception\r\n\t *\/\r\n\tpublic static void parseLimitedList(ManagedList&lt;Object&gt; objects, Node node,\r\n\t\tParserContext ctx, BeanDefinition parentBean, String scope, boolean supportCustomTags)\r\n\t\tthrows Exception\r\n\t{\r\n\t\t\/\/ Only worry about element nodes\r\n\t\tif (node.getNodeType() == Node.ELEMENT_NODE) {\r\n\t\t\tElement elem = (Element)node;\r\n\t\t\tString tagName = node.getLocalName();\r\n\r\n\t\t\tif (tagName.equals(&quot;value&quot;)) {\r\n\t\t\t\tString val = node.getTextContent();\r\n\t\t\t\t\/\/ to get around an issue with Spring Batch not parsing Spring EL\r\n\t\t\t\t\/\/ we will do it for them\r\n\t\t\t\tif (scope.equals(&quot;step&quot;)\r\n\t\t\t\t\t&amp;&amp; (val.startsWith(&quot;#{&quot;) &amp;&amp; val.endsWith(&quot;}&quot;))\r\n\t\t\t\t\t&amp;&amp; (!val.startsWith(&quot;#{jobParameters&quot;))\r\n\t\t\t\t\t)\r\n\t\t\t\t{\r\n\t\t\t\t\t\/\/ Set up a new EL parser\r\n\t\t\t\t\tExpressionParser parser = new SpelExpressionParser();\r\n\t\t\t\t\t\/\/ Parse the value\r\n\t\t\t\t\tExpression exp = parser.parseExpression(val.substring(2, val.length()-1));\r\n\t\t\t\t\t\/\/ Place the results in the list of created objects\r\n\t\t\t\t\tobjects.add(exp.getValue());\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\t\/\/ Otherwise, just treat it as a normal value tag\r\n\t\t\t\t\tobjects.add(val);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\/\/ Either of these is a just a lookup of an existing bean \r\n\t\t\telse if (tagName.equals(&quot;ref&quot;) || tagName.equals(&quot;idref&quot;)) {\r\n\t\t\t\tobjects.add(ctx.getRegistry().getBeanDefinition(node.getTextContent()));\r\n\t\t\t}\r\n\t\t\t\/\/ We need to create the bean\r\n\t\t\telse if (tagName.equals(&quot;bean&quot;)) {\r\n\t\t\t\t\/\/ There is no quick little util I could find to create a bean\r\n\t\t\t\t\/\/ on the fly programmatically in Spring and still support all\r\n\t\t\t\t\/\/ Spring functionality so basically I mimic what Spring actually\r\n\t\t\t\t\/\/ does but on a smaller scale.  Everything Spring allows is\r\n\t\t\t\t\/\/ still supported\r\n\r\n\t\t\t\t\/\/ Create a factory to make the bean\r\n\t\t\t\tDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();\r\n\t\t\t\t\/\/ Set up a parser for the bean\r\n\t\t\t\tBeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext());\r\n\t\t\t\t\/\/ Parse the bean get its information, now in a DefintionHolder\r\n\t\t\t\tBeanDefinitionHolder bh = pd.parseBeanDefinitionElement(elem, parentBean);\r\n\t\t\t\t\/\/ Register the bean will all the other beans Spring is aware of\r\n\t\t\t\tBeanDefinitionReaderUtils.registerBeanDefinition(bh, beanFactory);\r\n\t\t\t\t\/\/ Get the bean from the factory.  This will allows Spring\r\n\t\t\t\t\/\/ to do all its work (EL processing, scope, etc) and give us \r\n\t\t\t\t\/\/ the actual bean itself\r\n\t\t\t\tObject bean = beanFactory.getBean(bh.getBeanName());\r\n\t\t\t\tobjects.add(bean);\r\n\t\t\t}\r\n\t\t\t\/*\r\n\t\t\t * This is handled a bit differently in that it actually sets the property\r\n\t\t\t * on the parent bean for us based on the property\r\n\t\t\t *\/\r\n\t\t\telse if (tagName.equals(&quot;property&quot;)) {\r\n\t\t\t\tBeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext());\r\n\t\t\t\t\/\/ This method actually set eh property on the parentBean for us so\r\n\t\t\t\t\/\/ we don't have to add anything to the objects object\r\n\t\t\t\tpd.parsePropertyElement(elem, parentBean);\r\n\t\t\t}\r\n\t\t\telse if (supportCustomTags) {\r\n\t\t\t\t\/\/ handle custom tag\r\n\t\t\t\tBeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext());\r\n\t\t\t\tBeanDefinition bd = pd.parseCustomElement(elem, parentBean);\r\n\t\t\t\tobjects.add(bd);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n<\/pre>\n<p>And that&#8217;s it!  Ok, so you may be saying, &#8220;Whoa, that was a lot of code&#8221;, well, I did say it was a more advanced example.<\/p>\n<p\/>\n<p>If you have any questions please post a comment.  I know it can be a bit confusing so I tried to document in-line in the code as much as possible to make it easy to follow.<\/p>\n<p\/>\n<p>Really though, this is a very powerful, under-utilized feature of Spring.  Knowing how to do this can really make your XML smaller while harnessing the power of converting things on the fly and keeping your beans as generic as possible.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The DefinitionParser Ok, we&#8217;ve got it all set up, now we need to code the thing. Let me state, if you didn&#8217;t grasp this, that this is more of an advanced example. If you want a basic example take a &hellip; <a href=\"http:\/\/sloanseaman.com\/wordpress\/2012\/04\/08\/spring-custom-tags-extensible-xml-part-2\/\">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\/313"}],"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=313"}],"version-history":[{"count":7,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/313\/revisions"}],"predecessor-version":[{"id":326,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/313\/revisions\/326"}],"wp:attachment":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/media?parent=313"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/categories?post=313"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/tags?post=313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}