001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.context.support; 018 019import java.io.IOException; 020 021import groovy.lang.GroovyObject; 022import groovy.lang.GroovySystem; 023import groovy.lang.MetaClass; 024 025import org.springframework.beans.BeanWrapper; 026import org.springframework.beans.BeanWrapperImpl; 027import org.springframework.beans.BeansException; 028import org.springframework.beans.factory.NoSuchBeanDefinitionException; 029import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; 030import org.springframework.beans.factory.support.DefaultListableBeanFactory; 031 032/** 033 * {@link org.springframework.web.context.WebApplicationContext} implementation which takes 034 * its configuration from Groovy bean definition scripts and/or XML files, as understood by 035 * a {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader}. 036 * This is essentially the equivalent of 037 * {@link org.springframework.context.support.GenericGroovyApplicationContext} 038 * for a web environment. 039 * 040 * <p>By default, the configuration will be taken from "/WEB-INF/applicationContext.groovy" 041 * for the root context, and "/WEB-INF/test-servlet.groovy" for a context with the namespace 042 * "test-servlet" (like for a DispatcherServlet instance with the servlet-name "test"). 043 * 044 * <p>The config location defaults can be overridden via the "contextConfigLocation" 045 * context-param of {@link org.springframework.web.context.ContextLoader} and servlet 046 * init-param of {@link org.springframework.web.servlet.FrameworkServlet}. Config locations 047 * can either denote concrete files like "/WEB-INF/context.groovy" or Ant-style patterns 048 * like "/WEB-INF/*-context.groovy" (see {@link org.springframework.util.PathMatcher} 049 * javadoc for pattern details). Note that ".xml" files will be parsed as XML content; 050 * all other kinds of resources will be parsed as Groovy scripts. 051 * 052 * <p>Note: In case of multiple config locations, later bean definitions will 053 * override ones defined in earlier loaded files. This can be leveraged to 054 * deliberately override certain bean definitions via an extra Groovy script. 055 * 056 * <p><b>For a WebApplicationContext that reads in a different bean definition format, 057 * create an analogous subclass of {@link AbstractRefreshableWebApplicationContext}.</b> 058 * Such a context implementation can be specified as "contextClass" context-param 059 * for ContextLoader or "contextClass" init-param for FrameworkServlet. 060 * 061 * @author Juergen Hoeller 062 * @since 4.1 063 * @see #setNamespace 064 * @see #setConfigLocations 065 * @see org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader 066 * @see org.springframework.web.context.ContextLoader#initWebApplicationContext 067 * @see org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext 068 */ 069public class GroovyWebApplicationContext extends AbstractRefreshableWebApplicationContext implements GroovyObject { 070 071 /** Default config location for the root context */ 072 public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.groovy"; 073 074 /** Default prefix for building a config location for a namespace */ 075 public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; 076 077 /** Default suffix for building a config location for a namespace */ 078 public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".groovy"; 079 080 081 private final BeanWrapper contextWrapper = new BeanWrapperImpl(this); 082 083 private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass()); 084 085 086 /** 087 * Loads the bean definitions via an GroovyBeanDefinitionReader. 088 * @see org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader 089 * @see #initBeanDefinitionReader 090 * @see #loadBeanDefinitions 091 */ 092 @Override 093 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 094 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 095 GroovyBeanDefinitionReader beanDefinitionReader = new GroovyBeanDefinitionReader(beanFactory); 096 097 // Configure the bean definition reader with this context's 098 // resource loading environment. 099 beanDefinitionReader.setEnvironment(getEnvironment()); 100 beanDefinitionReader.setResourceLoader(this); 101 102 // Allow a subclass to provide custom initialization of the reader, 103 // then proceed with actually loading the bean definitions. 104 initBeanDefinitionReader(beanDefinitionReader); 105 loadBeanDefinitions(beanDefinitionReader); 106 } 107 108 /** 109 * Initialize the bean definition reader used for loading the bean 110 * definitions of this context. Default implementation is empty. 111 * <p>Can be overridden in subclasses. 112 * @param beanDefinitionReader the bean definition reader used by this context 113 */ 114 protected void initBeanDefinitionReader(GroovyBeanDefinitionReader beanDefinitionReader) { 115 } 116 117 /** 118 * Load the bean definitions with the given GroovyBeanDefinitionReader. 119 * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method; 120 * therefore this method is just supposed to load and/or register bean definitions. 121 * <p>Delegates to a ResourcePatternResolver for resolving location patterns 122 * into Resource instances. 123 * @throws IOException if the required Groovy script or XML file isn't found 124 * @see #refreshBeanFactory 125 * @see #getConfigLocations 126 * @see #getResources 127 * @see #getResourcePatternResolver 128 */ 129 protected void loadBeanDefinitions(GroovyBeanDefinitionReader reader) throws IOException { 130 String[] configLocations = getConfigLocations(); 131 if (configLocations != null) { 132 for (String configLocation : configLocations) { 133 reader.loadBeanDefinitions(configLocation); 134 } 135 } 136 } 137 138 /** 139 * The default location for the root context is "/WEB-INF/applicationContext.groovy", 140 * and "/WEB-INF/test-servlet.groovy" for a context with the namespace "test-servlet" 141 * (like for a DispatcherServlet instance with the servlet-name "test"). 142 */ 143 @Override 144 protected String[] getDefaultConfigLocations() { 145 if (getNamespace() != null) { 146 return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; 147 } 148 else { 149 return new String[] {DEFAULT_CONFIG_LOCATION}; 150 } 151 } 152 153 154 // Implementation of the GroovyObject interface 155 156 public void setMetaClass(MetaClass metaClass) { 157 this.metaClass = metaClass; 158 } 159 160 public MetaClass getMetaClass() { 161 return this.metaClass; 162 } 163 164 public Object invokeMethod(String name, Object args) { 165 return this.metaClass.invokeMethod(this, name, args); 166 } 167 168 public void setProperty(String property, Object newValue) { 169 this.metaClass.setProperty(this, property, newValue); 170 } 171 172 public Object getProperty(String property) { 173 if (containsBean(property)) { 174 return getBean(property); 175 } 176 else if (this.contextWrapper.isReadableProperty(property)) { 177 return this.contextWrapper.getPropertyValue(property); 178 } 179 throw new NoSuchBeanDefinitionException(property); 180 } 181 182}