View Javadoc

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