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 */
022package org.granite.spring;
023
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.springframework.beans.factory.config.BeanDefinition;
029import org.springframework.beans.factory.parsing.BeanComponentDefinition;
030import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
031import org.springframework.beans.factory.support.BeanDefinitionBuilder;
032import org.springframework.beans.factory.support.ManagedList;
033import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
034import org.springframework.beans.factory.xml.ParserContext;
035import org.springframework.core.Conventions;
036import org.springframework.util.StringUtils;
037import org.springframework.util.xml.DomUtils;
038import org.w3c.dom.Element;
039
040/**
041 * @author William Drai
042 */
043public 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}