001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.spring; 018 019import java.util.Map; 020 021import javax.servlet.ServletContext; 022 023import org.apache.wicket.protocol.http.IWebApplicationFactory; 024import org.apache.wicket.protocol.http.WebApplication; 025import org.apache.wicket.protocol.http.WicketFilter; 026import org.apache.wicket.spring.injection.annot.SpringComponentInjector; 027import org.springframework.beans.BeansException; 028import org.springframework.beans.factory.BeanFactoryUtils; 029import org.springframework.context.ApplicationContext; 030import org.springframework.web.context.ConfigurableWebApplicationContext; 031import org.springframework.web.context.WebApplicationContext; 032import org.springframework.web.context.support.WebApplicationContextUtils; 033import org.springframework.web.context.support.XmlWebApplicationContext; 034 035/** 036 * Implementation of IWebApplicationFactory that pulls the WebApplication object out of spring 037 * application context. 038 * 039 * Configuration example: 040 * 041 * <pre> 042 * <filter> 043 * <filter-name>MyApplication</filter-name> 044 * <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> 045 * <init-param> 046 * <param-name>applicationFactoryClassName</param-name> 047 * <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value> 048 * </init-param> 049 * </filter> 050 * </pre> 051 * 052 * <code>applicationBean</code> init parameter can be used if there are multiple WebApplications 053 * defined on the spring application context. 054 * 055 * Example: 056 * 057 * <pre> 058 * <filter> 059 * <filter-name>MyApplication</filter-name> 060 * <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> 061 * <init-param> 062 * <param-name>applicationFactoryClassName</param-name> 063 * <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value> 064 * </init-param> 065 * <init-param> 066 * <param-name>applicationBean</param-name> 067 * <param-value>phonebookApplication</param-value> 068 * </init-param> 069 * </filter> 070 * </pre> 071 * 072 * <p> 073 * This factory is also capable of creating a {@link WebApplication}-specific application context 074 * (path to which is specified via the {@code contextConfigLocation} filter param) and chaining it 075 * to the global one 076 * </p> 077 * 078 * <pre> 079 * <filter> 080 * <filter-name>MyApplication</filter-name> 081 * <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> 082 * <init-param> 083 * <param-name>applicationFactoryClassName</param-name> 084 * <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value> 085 * </init-param> 086 * <init-param> 087 * <param-name>contextConfigLocation</param-name> 088 * <param-value>classpath:com/myapplication/customers-app/context.xml</param-value> 089 * </init-param> 090 * </filter> 091 * </pre> 092 * 093 * @author Igor Vaynberg (ivaynberg) 094 * @author Janne Hietamäki (jannehietamaki) 095 * 096 */ 097public class SpringWebApplicationFactory implements IWebApplicationFactory 098{ 099 100 /** web application context created for this filter, if any */ 101 private ConfigurableWebApplicationContext webApplicationContext; 102 103 /** 104 * Returns location of context config that will be used to create a {@link WebApplication} 105 * -specific application context. 106 * 107 * @param filter 108 * @return location of context config 109 */ 110 protected final String getContextConfigLocation(final WicketFilter filter) 111 { 112 return filter.getFilterConfig().getInitParameter("contextConfigLocation"); 113 } 114 115 /** 116 * Factory method used to create a new instance of the web application context, by default an 117 * instance o {@link XmlWebApplicationContext} will be created. 118 * 119 * @return application context instance 120 */ 121 protected ConfigurableWebApplicationContext newApplicationContext() 122 { 123 return new XmlWebApplicationContext(); 124 } 125 126 /** 127 * @see IWebApplicationFactory#createApplication(WicketFilter) 128 */ 129 @Override 130 public WebApplication createApplication(final WicketFilter filter) 131 { 132 ServletContext servletContext = filter.getFilterConfig().getServletContext(); 133 134 WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext); 135 136 if (getContextConfigLocation(filter) != null) 137 { 138 applicationContext = createWebApplicationContext(applicationContext, filter); 139 } 140 141 String beanName = filter.getFilterConfig().getInitParameter("applicationBean"); 142 return createApplication(applicationContext, beanName); 143 } 144 145 private WebApplication createApplication(final ApplicationContext applicationContext, 146 final String beanName) 147 { 148 WebApplication application; 149 150 if (beanName != null) 151 { 152 application = (WebApplication)applicationContext.getBean(beanName); 153 if (application == null) 154 { 155 throw new IllegalArgumentException( 156 "Unable to find WebApplication bean with name [" + beanName + "]"); 157 } 158 } 159 else 160 { 161 Map<?, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, 162 WebApplication.class, false, false); 163 if (beans.size() == 0) 164 { 165 throw new IllegalStateException("bean of type [" + WebApplication.class.getName() + 166 "] not found"); 167 } 168 if (beans.size() > 1) 169 { 170 throw new IllegalStateException("More than one bean of type [" + 171 WebApplication.class.getName() + "] found, must have only one"); 172 } 173 application = (WebApplication)beans.values().iterator().next(); 174 } 175 176 // make the application context default for SpringComponentInjectors 177 SpringComponentInjector.setDefaultContext(application, applicationContext); 178 179 return application; 180 } 181 182 /** 183 * Creates and initializes a new {@link WebApplicationContext}, with the given context as the 184 * parent. Based on the logic in Spring's FrameworkServlet#createWebApplicationContext() 185 * 186 * @param parent 187 * parent application context 188 * @param filter 189 * wicket filter 190 * @return instance of web application context 191 * @throws BeansException 192 */ 193 protected final ConfigurableWebApplicationContext createWebApplicationContext( 194 final WebApplicationContext parent, final WicketFilter filter) throws BeansException 195 { 196 webApplicationContext = newApplicationContext(); 197 webApplicationContext.setParent(parent); 198 webApplicationContext.setServletContext(filter.getFilterConfig().getServletContext()); 199 webApplicationContext.setConfigLocation(getContextConfigLocation(filter)); 200 201 postProcessWebApplicationContext(webApplicationContext, filter); 202 webApplicationContext.refresh(); 203 204 return webApplicationContext; 205 } 206 207 /** 208 * This is a hook for potential subclasses to perform additional processing on the context. 209 * Based on the logic in Spring's FrameworkServlet#postProcessWebApplicationContext() 210 * 211 * @param wac 212 * additional application context 213 * @param filter 214 * wicket filter 215 */ 216 protected void postProcessWebApplicationContext(final ConfigurableWebApplicationContext wac, 217 final WicketFilter filter) 218 { 219 // noop 220 } 221 222 /** {@inheritDoc} */ 223 @Override 224 public void destroy(final WicketFilter filter) 225 { 226 if (webApplicationContext != null) 227 { 228 webApplicationContext.close(); 229 } 230 } 231}