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 ServerFilterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
043    
044        private static final String SERVER_FILTER_BEAN_NAME = "org.granite.spring.ServerFilter";
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, SERVER_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 ServerFilter.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    }