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}