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    }