001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.spring;
022
023 import java.util.HashMap;
024 import java.util.List;
025 import java.util.Map;
026
027 import org.springframework.beans.factory.config.BeanDefinition;
028 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
029 import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
030 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
031 import org.springframework.beans.factory.support.ManagedList;
032 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
033 import org.springframework.beans.factory.xml.ParserContext;
034 import org.springframework.core.Conventions;
035 import org.springframework.util.StringUtils;
036 import org.springframework.util.xml.DomUtils;
037 import org.w3c.dom.Element;
038
039 /**
040 * @author William Drai
041 */
042 public class FlexFilterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
043
044 private static final String FLEX_FILTER_BEAN_NAME = "org.granite.spring.FlexFilter";
045
046 private static final String GRANITE_CONFIG_BEAN_NAME = "org.granite.spring.SpringGraniteConfig";
047
048 public static final String GRAVITY_FACTORY_BEAN_NAME = "org.granite.spring.gravityFactory";
049
050 private static final String DEFAULT_HANDLER_MAPPING_CLASS_NAME = "org.springframework.web.servlet.handler.SimpleUrlHandlerMapping";
051
052
053 @Override
054 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
055 CompositeComponentDefinition componentDefinition = new CompositeComponentDefinition(element.getLocalName(),
056 parserContext.extractSource(element));
057 parserContext.pushContainingComponent(componentDefinition);
058
059 element.setAttribute(ID_ATTRIBUTE, FLEX_FILTER_BEAN_NAME);
060
061 mapOptionalAttributes(element, parserContext, builder, "tide");
062
063 Object source = parserContext.extractSource(element);
064
065 ManagedList<String> roles = new ManagedList<String>();
066 roles.setSource(source);
067 List<Element> rolesElements = DomUtils.getChildElementsByTagName(element, "tide-roles");
068 for (Element rolesElement : rolesElements) {
069 List<Element> valueElements = DomUtils.getChildElementsByTagName(rolesElement, "value");
070 for (Element valueElement : valueElements)
071 roles.add(valueElement.getTextContent());
072 }
073 if (!roles.isEmpty())
074 builder.addPropertyValue("tideRoles", roles);
075
076 ManagedList<String> tideAnnotations = new ManagedList<String>();
077 tideAnnotations.setSource(source);
078 List<Element> tideAnnotationsElements = DomUtils.getChildElementsByTagName(element, "tide-annotations");
079 for (Element tideAnnotationsElement : tideAnnotationsElements) {
080 List<Element> valueElements = DomUtils.getChildElementsByTagName(tideAnnotationsElement, "value");
081 for (Element valueElement : valueElements)
082 tideAnnotations.add(valueElement.getTextContent());
083 }
084 builder.addPropertyValue("tideAnnotations", tideAnnotations);
085
086 ManagedList<String> tideInterfaces = new ManagedList<String>();
087 tideInterfaces.setSource(source);
088 List<Element> tideInterfacesElements = DomUtils.getChildElementsByTagName(element, "tide-interfaces");
089 for (Element tideInterfacesElement : tideInterfacesElements) {
090 List<Element> valueElements = DomUtils.getChildElementsByTagName(tideInterfacesElement, "value");
091 for (Element valueElement : valueElements)
092 tideInterfaces.add(valueElement.getTextContent());
093 }
094 builder.addPropertyValue("tideInterfaces", tideInterfaces);
095
096 ManagedList<String> tideNames = new ManagedList<String>();
097 tideNames.setSource(source);
098 List<Element> tideNamesElements = DomUtils.getChildElementsByTagName(element, "tide-names");
099 for (Element tideNamesElement : tideNamesElements) {
100 List<Element> valueElements = DomUtils.getChildElementsByTagName(tideNamesElement, "value");
101 for (Element valueElement : valueElements)
102 tideNames.add(valueElement.getTextContent());
103 }
104 builder.addPropertyValue("tideNames", tideNames);
105
106 ManagedList<String> tideTypes = new ManagedList<String>();
107 tideTypes.setSource(source);
108 List<Element> tideTypesElements = DomUtils.getChildElementsByTagName(element, "tide-types");
109 for (Element tideTypesElement : tideTypesElements) {
110 List<Element> valueElements = DomUtils.getChildElementsByTagName(tideTypesElement, "value");
111 for (Element valueElement : valueElements)
112 tideTypes.add(valueElement.getTextContent());
113 }
114 builder.addPropertyValue("tideTypes", tideTypes);
115
116 ManagedList<String> exceptionConverters = new ManagedList<String>();
117 exceptionConverters.setSource(source);
118 List<Element> exceptionConvertersElements = DomUtils.getChildElementsByTagName(element, "exception-converters");
119 for (Element exceptionConvertersElement : exceptionConvertersElements) {
120 List<Element> valueElements = DomUtils.getChildElementsByTagName(exceptionConvertersElement, "value");
121 for (Element valueElement : valueElements)
122 exceptionConverters.add(valueElement.getTextContent());
123 }
124 builder.addPropertyValue("exceptionConverters", exceptionConverters);
125
126 Element amf3MessageInterceptor = DomUtils.getChildElementByTagName(element, "amf3-message-interceptor");
127 if (amf3MessageInterceptor != null)
128 builder.addPropertyReference("amf3MessageInterceptor", amf3MessageInterceptor.getTextContent());
129
130 configureGraniteDS(element, parserContext, DomUtils.getChildElementByTagName(element, "granite-config"));
131
132 registerHandlerMappings(element, parserContext, element.getAttribute("url-pattern"));
133
134 parserContext.popAndRegisterContainingComponent();
135 }
136
137 @Override
138 protected String getBeanClassName(Element element) {
139 return FlexFilter.class.getName();
140 }
141
142 private void configureGraniteDS(Element parent, ParserContext parserContext, Element graniteConfigElement) {
143 if (parserContext.getRegistry().containsBeanDefinition(GRANITE_CONFIG_BEAN_NAME))
144 return;
145
146 Element source = graniteConfigElement != null ? graniteConfigElement : parent;
147
148 BeanDefinitionBuilder graniteConfigBuilder = BeanDefinitionBuilder.genericBeanDefinition(SpringGraniteConfig.class);
149 registerInfrastructureComponent(source, parserContext, graniteConfigBuilder, GRANITE_CONFIG_BEAN_NAME);
150
151 BeanDefinitionBuilder gravityFactoryBuilder = BeanDefinitionBuilder.rootBeanDefinition(SpringGravityFactoryBean.class);
152 registerInfrastructureComponent(source, parserContext, gravityFactoryBuilder, GRAVITY_FACTORY_BEAN_NAME);
153 }
154
155 @SuppressWarnings({ "unchecked", "rawtypes" })
156 private void registerHandlerMappings(Element parent, ParserContext parserContext, String urlPattern) {
157 BeanDefinitionBuilder handlerMappingBuilder = BeanDefinitionBuilder.genericBeanDefinition(DEFAULT_HANDLER_MAPPING_CLASS_NAME);
158
159 Map mappings = new HashMap();
160 if (urlPattern != null)
161 mappings.put(urlPattern, parent.getAttribute(ID_ATTRIBUTE));
162
163 handlerMappingBuilder.addPropertyValue("urlMap", mappings);
164 registerInfrastructureComponent(parent, parserContext, handlerMappingBuilder,
165 parent.getAttribute(ID_ATTRIBUTE) + "_handlerMapping");
166 }
167
168
169 // From Spring-Flex
170
171 static void registerInfrastructureComponent(Element element, ParserContext parserContext, BeanDefinitionBuilder componentBuilder, String beanName) {
172 componentBuilder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
173 componentBuilder.getRawBeanDefinition().setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
174 parserContext.registerBeanComponent(new BeanComponentDefinition(componentBuilder.getBeanDefinition(), beanName));
175 }
176
177 static void mapOptionalAttributes(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, String... attrs) {
178 for (String attr : attrs) {
179 String value = element.getAttribute(attr);
180 if (StringUtils.hasText(value)) {
181 String propertyName = Conventions.attributeNameToPropertyName(attr);
182 if (validateProperty(element, parserContext, propertyName, attr)) {
183 builder.addPropertyValue(propertyName, value);
184 }
185 }
186 }
187 }
188
189 private static boolean validateProperty(Element element, ParserContext parserContext, String propertyName, String attr) {
190 if (!StringUtils.hasText(propertyName)) {
191 parserContext.getReaderContext().error(
192 "Illegal property name trying to convert from attribute '" + attr + "' : cannot be null or empty.",
193 parserContext.extractSource(element));
194 return false;
195 }
196 return true;
197 }
198 }