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