blob: 3d565239425ba9c0e3e2ae124f5321d3f23d9cb3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.spring;
import java.util.Map;
import jakarta.servlet.ServletContext;
import org.apache.wicket.protocol.http.IWebApplicationFactory;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WicketFilter;
import org.apache.wicket.spring.injection.annot.SpringComponentInjector;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
/**
* Implementation of IWebApplicationFactory that pulls the WebApplication object out of spring
* application context.
*
* Configuration example:
*
* <pre>
* &lt;filter&gt;
* &lt;filter-name&gt;MyApplication&lt;/filter-name&gt;
* &lt;filter-class>org.apache.wicket.protocol.http.WicketFilter&lt;/filter-class&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;applicationFactoryClassName&lt;/param-name&gt;
* &lt;param-value&gt;org.apache.wicket.spring.SpringWebApplicationFactory&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;/filter&gt;
* </pre>
*
* <code>applicationBean</code> init parameter can be used if there are multiple WebApplications
* defined on the spring application context.
*
* Example:
*
* <pre>
* &lt;filter&gt;
* &lt;filter-name&gt;MyApplication&lt;/filter-name&gt;
* &lt;filter-class>org.apache.wicket.protocol.http.WicketFilter&lt;/filter-class&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;applicationFactoryClassName&lt;/param-name&gt;
* &lt;param-value&gt;org.apache.wicket.spring.SpringWebApplicationFactory&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;applicationBean&lt;/param-name&gt;
* &lt;param-value&gt;phonebookApplication&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;/filter&gt;
* </pre>
*
* <p>
* This factory is also capable of creating a {@link WebApplication}-specific application context
* (path to which is specified via the {@code contextConfigLocation} filter param) and chaining it
* to the global one
* </p>
*
* <pre>
* &lt;filter&gt;
* &lt;filter-name&gt;MyApplication&lt;/filter-name&gt;
* &lt;filter-class>org.apache.wicket.protocol.http.WicketFilter&lt;/filter-class&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;applicationFactoryClassName&lt;/param-name&gt;
* &lt;param-value&gt;org.apache.wicket.spring.SpringWebApplicationFactory&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
* &lt;param-value&gt;classpath:com/myapplication/customers-app/context.xml&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;/filter&gt;
* </pre>
*
* @author Igor Vaynberg (ivaynberg)
* @author Janne Hietam&auml;ki (jannehietamaki)
*
*/
public class SpringWebApplicationFactory implements IWebApplicationFactory
{
/** web application context created for this filter, if any */
private ConfigurableWebApplicationContext webApplicationContext;
/**
* Returns location of context config that will be used to create a {@link WebApplication}
* -specific application context.
*
* @param filter
* @return location of context config
*/
protected final String getContextConfigLocation(final WicketFilter filter)
{
return filter.getFilterConfig().getInitParameter("contextConfigLocation");
}
/**
* Factory method used to create a new instance of the web application context, by default an
* instance o {@link XmlWebApplicationContext} will be created.
*
* @return application context instance
*/
protected ConfigurableWebApplicationContext newApplicationContext()
{
return new XmlWebApplicationContext();
}
/**
* @see IWebApplicationFactory#createApplication(WicketFilter)
*/
@Override
public WebApplication createApplication(final WicketFilter filter)
{
ServletContext servletContext = filter.getFilterConfig().getServletContext();
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(new javax.servlet.ServletContext.Impl(servletContext));
if (getContextConfigLocation(filter) != null)
{
applicationContext = createWebApplicationContext(applicationContext, filter);
}
String beanName = filter.getFilterConfig().getInitParameter("applicationBean");
return createApplication(applicationContext, beanName);
}
private WebApplication createApplication(final ApplicationContext applicationContext,
final String beanName)
{
WebApplication application;
if (beanName != null)
{
application = (WebApplication)applicationContext.getBean(beanName);
if (application == null)
{
throw new IllegalArgumentException(
"Unable to find WebApplication bean with name [" + beanName + "]");
}
}
else
{
Map<?, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
WebApplication.class, false, false);
if (beans.size() == 0)
{
throw new IllegalStateException("bean of type [" + WebApplication.class.getName() +
"] not found");
}
if (beans.size() > 1)
{
throw new IllegalStateException("More than one bean of type [" +
WebApplication.class.getName() + "] found, must have only one");
}
application = (WebApplication)beans.values().iterator().next();
}
// make the application context default for SpringComponentInjectors
SpringComponentInjector.setDefaultContext(application, applicationContext);
return application;
}
/**
* Creates and initializes a new {@link WebApplicationContext}, with the given context as the
* parent. Based on the logic in {@link FrameworkServlet#createWebApplicationContext}
*
* @param parent
* parent application context
* @param filter
* wicket filter
* @return instance of web application context
* @throws BeansException
*/
protected final ConfigurableWebApplicationContext createWebApplicationContext(
final WebApplicationContext parent, final WicketFilter filter) throws BeansException
{
webApplicationContext = newApplicationContext();
webApplicationContext.setParent(parent);
webApplicationContext.setServletContext(new javax.servlet.ServletContext.Impl(filter.getFilterConfig().getServletContext()));
webApplicationContext.setConfigLocation(getContextConfigLocation(filter));
postProcessWebApplicationContext(webApplicationContext, filter);
webApplicationContext.refresh();
return webApplicationContext;
}
/**
* This is a hook for potential subclasses to perform additional processing on the context.
* Based on the logic in {@link FrameworkServlet#postProcessWebApplicationContext}
*
* @param wac
* additional application context
* @param filter
* wicket filter
*/
protected void postProcessWebApplicationContext(final ConfigurableWebApplicationContext wac,
final WicketFilter filter)
{
// noop
}
/** {@inheritDoc} */
@Override
public void destroy(final WicketFilter filter)
{
if (webApplicationContext != null)
{
webApplicationContext.close();
}
}
}