blob: bfb53c588447be4d41592be2954e81298b73ed2d [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.cocoon.servletservice.spring;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.cocoon.jnet.URLHandlerFactoryCollector;
import org.apache.cocoon.servletservice.Mountable;
import org.apache.cocoon.servletservice.ServletServiceContext;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* @version $Id$
* @since 1.0.0
*/
public class ServletFactoryBean implements FactoryBean, ApplicationContextAware,
ServletContextAware, BeanNameAware {
private ApplicationContext parentContainer;
private ServletContext servletContext;
private String beanName;
private Servlet embeddedServlet;
private String mountPath;
private String contextPath;
private Map initParams;
private Map contextParams;
private Map connections;
private Map connectionServiceNames;
private String serviceName;
private ServletServiceContext servletServiceContext;
private URLHandlerFactoryCollector urlHandlerFactoryCollector;
public ServletFactoryBean() {
}
public void init() throws Exception {
this.servletServiceContext = new ServletServiceContext();
this.servletServiceContext.setServletContext(this.servletContext);
this.servletServiceContext.setMountPath(this.mountPath);
this.servletServiceContext.setInitParams(this.initParams);
this.servletServiceContext.setAttributes(this.contextParams);
this.servletServiceContext.setConnections(this.connections);
this.servletServiceContext.setConnectionServiceNames(this.connectionServiceNames);
this.servletServiceContext.setServiceName(this.serviceName);
// create a sub container that resolves paths relative to the servlet
// service context rather than the parent context and make it available
// in a context attribute
if (this.parentContainer == null) {
this.parentContainer = WebApplicationContextUtils.getRequiredWebApplicationContext(this.servletContext);
}
// set the context path
this.servletServiceContext.setContextPath(this.contextPath);
// create servlet-service specific Spring web application context
GenericWebApplicationContext container = new GenericWebApplicationContext();
container.setParent(this.parentContainer);
container.setServletContext(this.servletServiceContext);
container.refresh();
this.servletServiceContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, container);
// create a servlet config based on the block servlet context
ServletConfig blockServletConfig =
new ServletConfig() {
public String getInitParameter(String name) {
return ServletFactoryBean.this.servletServiceContext.getInitParameter(name);
}
public Enumeration getInitParameterNames() {
return ServletFactoryBean.this.servletServiceContext.getInitParameterNames();
}
public ServletContext getServletContext() {
return ServletFactoryBean.this.servletServiceContext;
}
public String getServletName() {
return ServletFactoryBean.this.beanName;
}
};
// create and initialize the embedded servlet
try {
//manual call to pushUrlHandlerFactories is needed due to issue COCOON-2236
urlHandlerFactoryCollector.pushUrlHandlerFactories();
this.embeddedServlet.init(blockServletConfig);
} finally {
urlHandlerFactoryCollector.popUrlHandlerFactories();
}
this.servletServiceContext.setServlet(this.embeddedServlet);
}
public void destroy() {
this.embeddedServlet.destroy();
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.parentContainer = applicationContext;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/**
* @param embeddedServlet the embeddedServlet to set
*/
public void setEmbeddedServlet(Servlet embeddedServlet) {
this.embeddedServlet = embeddedServlet;
}
/**
* @param mountPath
*/
public void setMountPath(String mountPath) {
this.mountPath = mountPath;
}
/**
* The path to the blocks resources relative to the servlet context URL,
* must start with an '/'.
* @param contextPath
*/
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
/**
* @param initParams
*/
public void setInitParams(Map initParams) {
this.initParams = initParams;
}
/**
* @param contextParams the contextParams to set
*/
public void setContextParams(Map contextParams) {
this.contextParams = contextParams;
}
/**
* @param connections
*/
public void setConnections(Map connections) {
this.connections = connections;
}
/**
* @param connectionServiceNames
*/
public void setConnectionServiceNames(Map connectionServiceNames) {
this.connectionServiceNames = connectionServiceNames;
}
public void setServiceName(String name) {
this.serviceName = name;
}
public Object getObject() throws Exception {
if (this.embeddedServlet == null) {
throw new FactoryBeanNotInitializedException("There might be a circular dependency inside the servlet connections.");
}
ProxyFactory proxyFactory = new ProxyFactory(this.embeddedServlet);
proxyFactory.addAdvice(new ServiceInterceptor());
if (this.mountPath != null) {
proxyFactory.addAdvisor(new MountableMixinAdvisor());
}
return proxyFactory.getProxy();
}
public Class getObjectType() {
if (this.embeddedServlet == null) {
//if embeddedServlet is null we don't know exactly what ObjectType this Factory will return but at least
//we are sure it is a class implementing Servlet interface so it's better to return it instead of null
return Servlet.class;
}
return this.embeddedServlet != null ? this.embeddedServlet.getClass() : null;
}
public boolean isSingleton() {
return true;
}
private class ServiceInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
if ("service".equals(invocation.getMethod().getName())) {
Object[] arguments = invocation.getArguments();
HttpServletRequest request = (HttpServletRequest) arguments[0];
HttpServletResponse response = (HttpServletResponse) arguments[1];
RequestDispatcher dispatcher =
ServletFactoryBean.this.servletServiceContext.getRequestDispatcher(request.getPathInfo());
dispatcher.forward(request, response);
return null;
} else if ("init".equals(invocation.getMethod().getName())) {
// The embedded servlet is initialized by this factory bean, ignore other containers
return null;
} else if ("destroy".equals(invocation.getMethod().getName())) {
// The embedded servlet is destroyed up by this factory bean, ignore other containers
return null;
}
return invocation.proceed();
}
}
private class MountableMixin extends DelegatingIntroductionInterceptor
implements Mountable {
public String getMountPath() {
return ServletFactoryBean.this.mountPath;
}
}
private class MountableMixinAdvisor extends DefaultIntroductionAdvisor {
public MountableMixinAdvisor() {
super(new MountableMixin(), Mountable.class);
}
}
public void setUrlHandlerFactoryCollector(URLHandlerFactoryCollector urlHandlerFactoryCollector) {
this.urlHandlerFactoryCollector = urlHandlerFactoryCollector;
}
}