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