View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package edu.internet2.middleware.shibboleth.common.config;
18  
19  import java.util.HashMap;
20  import java.util.Map;
21  
22  import javax.xml.namespace.QName;
23  
24  import org.opensaml.xml.util.XMLHelper;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  import org.springframework.beans.factory.config.BeanDefinition;
28  import org.springframework.beans.factory.config.BeanDefinitionHolder;
29  import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
30  import org.springframework.beans.factory.xml.BeanDefinitionParser;
31  import org.springframework.beans.factory.xml.NamespaceHandler;
32  import org.springframework.beans.factory.xml.ParserContext;
33  import org.w3c.dom.Attr;
34  import org.w3c.dom.Element;
35  import org.w3c.dom.Node;
36  
37  /**
38   * A base class for {@link NamespaceHandler} implementations.
39   * 
40   * This code is heavily based on Spring's <code>NamespaceHandlerSupport</code>. The largest difference is that bean
41   * definition parsers may be registered against either an elements name or schema type. During parser lookup the schema
42   * type is preferred.
43   */
44  public abstract class BaseSpringNamespaceHandler implements NamespaceHandler {
45  
46      /** Class logger. */
47      private final Logger log = LoggerFactory.getLogger(BaseSpringNamespaceHandler.class);
48  
49      /**
50       * Stores the {@link BeanDefinitionParser} implementations keyed by the local name of the {@link Element Elements}
51       * they handle.
52       */
53      private Map<QName, BeanDefinitionParser> parsers = new HashMap<QName, BeanDefinitionParser>();
54  
55      /**
56       * Stores the {@link BeanDefinitionDecorator} implementations keyed by the local name of the
57       * {@link Element Elements} they handle.
58       */
59      private Map<QName, BeanDefinitionDecorator> decorators = new HashMap<QName, BeanDefinitionDecorator>();
60  
61      /**
62       * Stores the {@link BeanDefinitionParser} implementations keyed by the local name of the {@link Attr Attrs} they
63       * handle.
64       */
65      private Map<QName, BeanDefinitionDecorator> attributeDecorators = new HashMap<QName, BeanDefinitionDecorator>();
66  
67      /**
68       * Decorates the supplied {@link Node} by delegating to the {@link BeanDefinitionDecorator} that is registered to
69       * handle that {@link Node}.
70       * 
71       * @param node the node decorating a the given bean definition
72       * @param definition the bean being decorated
73       * @param parserContext the current parser context
74       * 
75       * @return the deocrated bean definition
76       */
77      public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
78          return findDecoratorForNode(node).decorate(node, definition, parserContext);
79      }
80  
81      /**
82       * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is registered for that
83       * {@link Element}.
84       * 
85       * @param element the element to be parsed into a bean definition
86       * @param parserContext the context within which the bean definition is created
87       * 
88       * @return the bean definition created from the given element
89       */
90      public BeanDefinition parse(Element element, ParserContext parserContext) {
91          return findParserForElement(element).parse(element, parserContext);
92      }
93  
94      /**
95       * Locates the {@link BeanDefinitionParser} from the register implementations using the local name of the supplied
96       * {@link Element}.
97       * 
98       * @param element the element to locate the bean definition parser for
99       * 
100      * @return the parser for the given bean element
101      */
102     protected BeanDefinitionParser findParserForElement(Element element) {
103         QName parserId;
104         BeanDefinitionParser parser = null;
105 
106         parserId = XMLHelper.getXSIType(element);
107         if (parserId != null) {
108             log.trace("Attempting to find parser for element of type: {}", parserId);
109             parser = parsers.get(parserId);
110         }
111 
112         if (parser == null) {
113             parserId = XMLHelper.getNodeQName(element);
114             log.trace("Attempting to find parser with element name: {}", parserId);
115             parser = parsers.get(parserId);
116         }
117 
118         if (parser == null) {
119             log.error("Cannot locate BeanDefinitionParser for element: " + parserId);
120             throw new IllegalArgumentException("Cannot locate BeanDefinitionParser for element: " + parserId);
121         }
122 
123         return parser;
124     }
125 
126     /**
127      * Locates the {@link BeanDefinitionParser} from the register implementations using the local name of the supplied
128      * {@link Node}. Supports both {@link Element Elements} and {@link Attr Attrs}.
129      * 
130      * @param node the node to locate the decorator for
131      * 
132      * @return the decorator for the given node
133      */
134     protected BeanDefinitionDecorator findDecoratorForNode(Node node) {
135         BeanDefinitionDecorator decorator = null;
136 
137         if (node instanceof Element) {
138             decorator = decorators.get(XMLHelper.getXSIType((Element) node));
139             if (decorator == null) {
140                 decorator = decorators.get(XMLHelper.getNodeQName(node));
141             }
142         } else if (node instanceof Attr) {
143             decorator = attributeDecorators.get(node.getLocalName());
144         } else {
145             throw new IllegalArgumentException("Cannot decorate based on Nodes of type [" + node.getClass().getName()
146                     + "]");
147         }
148 
149         if (decorator == null) {
150             throw new IllegalArgumentException("Cannot locate BeanDefinitionDecorator for " + " ["
151                     + node.getLocalName() + "]");
152         }
153 
154         return decorator;
155     }
156 
157     /**
158      * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to handle the specified element.
159      * The element name is the local (non-namespace qualified) name.
160      * 
161      * @param elementNameOrType the element name or schema type the parser is for
162      * @param parser the parser to register
163      */
164     protected void registerBeanDefinitionParser(QName elementNameOrType, BeanDefinitionParser parser) {
165         parsers.put(elementNameOrType, parser);
166     }
167 
168     /**
169      * Subclasses can call this to register the supplied {@link BeanDefinitionDecorator} to handle the specified
170      * element. The element name is the local (non-namespace qualified) name.
171      * 
172      * @param elementNameOrType the element name or schema type the parser is for
173      * @param decorator the decorator to register
174      */
175     protected void registerBeanDefinitionDecorator(QName elementNameOrType, BeanDefinitionDecorator decorator) {
176         decorators.put(elementNameOrType, decorator);
177     }
178 
179     /**
180      * Subclasses can call this to register the supplied {@link BeanDefinitionDecorator} to handle the specified
181      * attribute. The attribute name is the local (non-namespace qualified) name.
182      * 
183      * @param attributeName the name of the attribute to register the decorator for
184      * @param decorator the decorator to register
185      */
186     protected void registerBeanDefinitionDecoratorForAttribute(QName attributeName, BeanDefinitionDecorator decorator) {
187         attributeDecorators.put(attributeName, decorator);
188     }
189 }