001: /*
002: * $Id: MuleHierarchicalBeanDefinitionParserDelegate.java 11135 2008-02-29 18:48:53Z acooke $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010: package org.mule.config.spring;
011:
012: import org.mule.config.spring.util.SpringXMLUtils;
013: import org.mule.util.StringUtils;
014:
015: import javax.xml.parsers.DocumentBuilderFactory;
016: import javax.xml.parsers.ParserConfigurationException;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.springframework.beans.factory.config.BeanDefinition;
021: import org.springframework.beans.factory.config.BeanDefinitionHolder;
022: import org.springframework.beans.factory.parsing.BeanComponentDefinition;
023: import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
024: import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
025: import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
026: import org.springframework.beans.factory.xml.NamespaceHandler;
027: import org.springframework.beans.factory.xml.ParserContext;
028: import org.springframework.beans.factory.xml.XmlReaderContext;
029: import org.w3c.dom.Document;
030: import org.w3c.dom.Element;
031: import org.w3c.dom.NodeList;
032:
033: /**
034: * This parser enables Mule to parse heirarchical bean structures using spring Namespace handling
035: * There are 4 base DefinitionParsers supplied in Mule that most Parsers will extend from, these are
036: * {@link org.mule.config.spring.parsers.AbstractChildDefinitionParser}
037: * {@link org.mule.config.spring.parsers.AbstractMuleBeanDefinitionParser}
038: * {@link org.mule.config.spring.parsers.generic.ChildDefinitionParser}
039: * {@link org.mule.config.spring.parsers.generic.MuleOrphanDefinitionParser}
040: */
041: public class MuleHierarchicalBeanDefinitionParserDelegate extends
042: BeanDefinitionParserDelegate {
043:
044: public static final String BEANS = "beans"; // cannot find this in Spring api!
045: public static final String MULE_REPEAT_PARSE = "org.mule.config.spring.MuleHierarchicalBeanDefinitionParserDelegate.MULE_REPEAT_PARSE";
046: public static final String MULE_NO_RECURSE = "org.mule.config.spring.MuleHierarchicalBeanDefinitionParserDelegate.MULE_NO_RECURSE";
047: public static final String MULE_FORCE_RECURSE = "org.mule.config.spring.MuleHierarchicalBeanDefinitionParserDelegate.MULE_FORCE_RECURSE";
048: public static final String MULE_NO_REGISTRATION = "org.mule.config.spring.MuleHierarchicalBeanDefinitionParserDelegate.MULE_NO_REGISTRATION";
049: public static final String MULE_POST_CHILDREN = "org.mule.config.spring.MuleHierarchicalBeanDefinitionParserDelegate.MULE_POST_CHILDREN";
050: private DefaultBeanDefinitionDocumentReader spring;
051:
052: protected static final Log logger = LogFactory
053: .getLog(MuleHierarchicalBeanDefinitionParserDelegate.class);
054:
055: public MuleHierarchicalBeanDefinitionParserDelegate(
056: XmlReaderContext readerContext,
057: DefaultBeanDefinitionDocumentReader spring) {
058: super (readerContext);
059: this .spring = spring;
060: }
061:
062: public boolean isDefaultNamespace(String namespaceUri) {
063: if (StringUtils.isEmpty(namespaceUri)) {
064: return false;
065: } else {
066: return super .isDefaultNamespace(namespaceUri);
067: }
068: }
069:
070: public BeanDefinition parseCustomElement(Element element,
071: BeanDefinition parent) {
072: if (logger.isDebugEnabled()) {
073: logger.debug("parsing: "
074: + SpringXMLUtils.elementToString(element));
075: }
076: if (SpringXMLUtils.isBeansNamespace(element)) {
077: return handleSpringElements(element, parent);
078: } else {
079: String namespaceUri = element.getNamespaceURI();
080: NamespaceHandler handler = getReaderContext()
081: .getNamespaceHandlerResolver()
082: .resolve(namespaceUri);
083: if (handler == null) {
084: getReaderContext().error(
085: "Unable to locate NamespaceHandler for namespace ["
086: + namespaceUri + "]", element);
087: return null;
088: }
089:
090: boolean noRecurse = false;
091: boolean forceRecurse = false;
092: BeanDefinition finalChild;
093:
094: do {
095: ParserContext parserContext = new ParserContext(
096: getReaderContext(), this , parent);
097: finalChild = handler.parse(element, parserContext);
098: registerBean(element, finalChild);
099: noRecurse = noRecurse
100: || testFlag(finalChild, MULE_NO_RECURSE);
101: forceRecurse = forceRecurse
102: || testFlag(finalChild, MULE_FORCE_RECURSE);
103: } while (null != finalChild
104: && testFlag(finalChild, MULE_REPEAT_PARSE));
105:
106: // Only iterate and parse child mule name-spaced elements. Spring does not do
107: // hierarchical parsing by default so we need to maintain this behavior
108: // for non-mule elements to ensure that we don't break the parsing of any
109: // other custom name-spaces e.g spring-jee.
110:
111: // We also avoid parsing inside elements that have constructed a factory bean
112: // because that means we're dealing with (something like) ChildMapDefinitionParser,
113: // which handles iteration internally (this is a hack needed because Spring doesn't
114: // expose the DP for "<spring:entry>" elements directly).
115:
116: boolean isRecurse;
117: if (noRecurse) {
118: // no recursion takes precedence, as recursion is set by default
119: isRecurse = false;
120: } else {
121: if (forceRecurse) {
122: isRecurse = true;
123: } else {
124: // default behaviour if no control specified
125: isRecurse = SpringXMLUtils.isMuleNamespace(element);
126: }
127: }
128:
129: if (isRecurse) {
130: NodeList list = element.getChildNodes();
131: for (int i = 0; i < list.getLength(); i++) {
132: if (list.item(i) instanceof Element) {
133: parseCustomElement((Element) list.item(i),
134: finalChild);
135: }
136: }
137: }
138:
139: // If a parser requests post-processing we call again after children called
140:
141: if (testFlag(finalChild, MULE_POST_CHILDREN)) {
142: ParserContext parserContext = new ParserContext(
143: getReaderContext(), this , parent);
144: finalChild = handler.parse(element, parserContext);
145: }
146:
147: return finalChild;
148: }
149: }
150:
151: protected BeanDefinition handleSpringElements(Element element,
152: BeanDefinition parent) {
153:
154: // these are only called if they are at a "top level" - if they are nested inside
155: // other spring elements then spring will handle them itself
156:
157: if (SpringXMLUtils.isLocalName(element, BEANS)) {
158: // the delegate doesn't support the full spring schema, but it seems that
159: // we can invoke the DefaultBeanDefinitionDocumentReader via registerBeanDefinitions
160: // but we need to create a new DOM document from the element first
161: try {
162: Document doc = DocumentBuilderFactory.newInstance()
163: .newDocumentBuilder().newDocument();
164: doc.appendChild(doc.importNode(element, true));
165: spring.registerBeanDefinitions(doc, getReaderContext());
166: } catch (ParserConfigurationException e) {
167: throw new RuntimeException(e);
168: }
169: return parent;
170: }
171:
172: else if (SpringXMLUtils.isLocalName(element, PROPERTY_ELEMENT)) {
173: parsePropertyElement(element, parent);
174: return parent;
175: }
176:
177: // i am trying to keep these to a minimum - using anything but "bean" is a recipe
178: // for disaster - we already have problems with "property", for example.
179:
180: // else if (isLocalName(element, MAP_ELEMENT))
181: // {
182: // // currently unused?
183: // parseMapElement(element, bd);
184: // }
185: // else if (isLocalName(element, LIST_ELEMENT))
186: // {
187: // // currently unused?
188: // parseListElement(element, bd);
189: // }
190: // else if (isLocalName(element, SET_ELEMENT))
191: // {
192: // // currently unused?
193: // parseSetElement(element, bd);
194: // }
195:
196: else if (SpringXMLUtils.isLocalName(element, BEAN_ELEMENT)) {
197: BeanDefinitionHolder holder = parseBeanDefinitionElement(
198: element, parent);
199: registerBeanDefinitionHolder(holder);
200: return holder.getBeanDefinition();
201: } else {
202: throw new IllegalStateException(
203: "Unexpected Spring element: "
204: + SpringXMLUtils.elementToString(element));
205: }
206: }
207:
208: protected void registerBean(Element ele, BeanDefinition bd) {
209: if (bd == null) {
210: return;
211: }
212:
213: // Check to see if the Bean Definition represents a compound element - one represents a subset of
214: // configuration for the parent bean. Compound bean definitions should not be registered since the properties
215: // set on them are really set on the parent bean.
216: if (!testFlag(bd, MULE_NO_REGISTRATION)) {
217: String name = generateChildBeanName(ele);
218: logger.debug("register " + name + ": "
219: + bd.getBeanClassName());
220: registerBeanDefinitionHolder(new BeanDefinitionHolder(bd,
221: name));
222: }
223: }
224:
225: protected void registerBeanDefinitionHolder(
226: BeanDefinitionHolder bdHolder) {
227: //bdHolder = decorateBeanDefinitionIfRequired(ele, bdHolder);
228: // Register the final decorated instance.
229: BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
230: getReaderContext().getRegistry());
231: // Send registration event.
232: getReaderContext().fireComponentRegistered(
233: new BeanComponentDefinition(bdHolder));
234: }
235:
236: protected String generateChildBeanName(Element e) {
237: String id = SpringXMLUtils.getNameOrId(e);
238: if (StringUtils.isBlank(id)) {
239: String parentId = SpringXMLUtils.getNameOrId((Element) e
240: .getParentNode());
241: return "." + parentId + ":" + e.getLocalName();
242: } else {
243: return id;
244: }
245: }
246:
247: public static void setFlag(BeanDefinition bean, String flag) {
248: bean.setAttribute(flag, Boolean.TRUE);
249: }
250:
251: public static boolean testFlag(BeanDefinition bean, String flag) {
252: return null != bean && bean.hasAttribute(flag)
253: && bean.getAttribute(flag) instanceof Boolean
254: && ((Boolean) bean.getAttribute(flag)).booleanValue();
255: }
256:
257: }
|