/*
* Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package org.ireland.jnetty.dispatch.servlet;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
import javax.servlet.FilterChain;
import javax.servlet.MultipartConfigElement;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletSecurityElement;
import javax.servlet.UnavailableException;
import javax.servlet.annotation.MultipartConfig;
import org.ireland.jnetty.dispatch.filterchain.ServletFilterChain;
import org.ireland.jnetty.webapp.WebApp;
import org.springframework.util.Assert;
/**
* Configuration for a servlet.
*
* 表示一个Servlet的所有配置(代表一个web.xml的<servlet>标签)
*
<servlet>
<description>This is FirstServlet</description>
<display-name>FirstServlet</display-name>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>myServlet.FirstServlet</servlet-class>
<init-param>
<param-name>abc</param-name>
<param-value>12</param-value>
</init-param>
<init-param>
<param-name>addsf</param-name>
<param-value>155</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
<async-supported>true</async-supported>
</servlet>
*
*/
public class ServletConfigImpl implements ServletConfig, ServletRegistration.Dynamic
{
protected static final Log log = LogFactory.getLog(ServletConfigImpl.class.getName());
private static final boolean debug = log.isDebugEnabled();
private final WebApp _webApp;
private final ServletContext _servletContext;
private final ServletManager _servletManager;
private final ServletMapper _servletMapper;
// Servlet的类的硬盘本地位置
private String _location;
private String _servletName;
private String _servletNameDefault;
private String _servletClassName;
private Class<? extends Servlet> _servletClass;
private String _displayName;
private int _loadOnStartup = Integer.MIN_VALUE;
private boolean _asyncSupported;
private HashMap<String, String> _initParams = new HashMap<String, String>();
// used for params defined prior to applying fragments.
private Set<String> _paramNames = new HashSet<String>();
private MultipartConfigElement _multipartConfigElement;
//Servlet的实例(单例)
private Servlet _servlet;
//Servlet实例对应的ServletFilterChain
private FilterChain _servletChain;
/**
* Creates a new servlet configuration object.
*/
public ServletConfigImpl(WebApp _webApp, ServletContext _servletContext, ServletManager _servletManager, ServletMapper _servletMapper)
{
Assert.notNull(_webApp);
Assert.notNull(_servletContext);
Assert.notNull(_servletManager);
Assert.notNull(_servletMapper);
this._webApp = _webApp;
this._servletContext = _servletContext;
this._servletManager = _servletManager;
this._servletMapper = _servletMapper;
}
protected void copyFrom(ServletConfigImpl source)
{
_initParams.putAll(source._initParams);
}
public WebApp getWebApp()
{
return _webApp;
}
/**
* Sets the config location.
*/
public void setConfigUriLocation(String location, int line)
{
_location = location + ":" + line + ": ";
}
/**
* Sets the servlet name.
*/
public void setServletName(String name)
{
_servletName = name;
}
/**
* Gets the servlet name.
*/
@Override
public String getServletName()
{
return _servletName;
}
@Override
public String getName()
{
return getServletName();
}
@Override
public String getClassName()
{
return _servletClassName;
}
/**
* 实际上是add初始化参数
*/
@Override
public boolean setInitParameter(String name, String value)
{
if (_initParams.containsKey(name))
return false;
_initParams.put(name, value);
return true;
}
@Override
public void setMultipartConfig(MultipartConfigElement multipartConfig)
{
if (multipartConfig == null)
throw new IllegalArgumentException();
_multipartConfigElement = multipartConfig;
}
public MultipartConfigElement getMultipartConfig()
{
if (_multipartConfigElement == null)
{
Class<?> servletClass = null;
try
{
servletClass = getServletClass();
}
catch (Exception e)
{
log.debug(e.toString(), e);
}
if (servletClass != null)
{
MultipartConfig config = (MultipartConfig) servletClass.getAnnotation(MultipartConfig.class);
if (config != null)
_multipartConfigElement = new MultipartConfigElement(config);
}
}
return _multipartConfigElement;
}
/**
* Maps or exists if any of the patterns in urlPatterns already map to a different servlet
*
* @param urlPatterns
* @return a Set of patterns previously mapped to a different servlet
*/
@Override
public Set<String> addMapping(String... urlPatterns)
{
try
{
Set<String> conflictPattern = new HashSet<String>();
// server/12t8 vs server/12uc
for (String urlPattern : urlPatterns)
{
ServletMapping mapping = _servletMapper.getServletMapping(urlPattern);
if (mapping == null)
{
continue;
}
String servletName = mapping.getServletConfig().getServletName();
if (!_servletName.equals(servletName) && servletName != null) //检查存在urlPattern冲突(同一个urlPattern映射多个Servlet)
{
if (debug)
{
log.debug("programmatic addMapping for '"+urlPattern+"' ignored because of existing servlet-mapping to '"+servletName+"'");
}
conflictPattern.add(urlPattern);
}
}
if (conflictPattern.size() > 0)
{
return conflictPattern;
}
//创建并添加ServletMapping到ServletContext
ServletMapping mapping = _webApp.createNewServletMapping(this);
for (String urlPattern : urlPatterns)
{
mapping.addURLPattern(urlPattern);
}
_webApp.addServletMapping(mapping);
return Collections.unmodifiableSet(conflictPattern);
}
catch (ServletException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public Collection<String> getMappings()
{
Set<String> patterns = _servletMapper.getUrlPatterns(_servletName);
if (patterns != null)
return Collections.unmodifiableSet(new LinkedHashSet<String>(patterns));
else
return new LinkedHashSet<String>();
}
/**
* 设置初始化参数
*/
@Override
public Set<String> setInitParameters(Map<String, String> initParameters)
{
Set<String> conflicting = new HashSet<String>();
for (Map.Entry<String, String> param : initParameters.entrySet())
{
if (_initParams.containsKey(param.getKey()))
conflicting.add(param.getKey());
else
_initParams.put(param.getKey(), param.getValue());
}
return Collections.unmodifiableSet(conflicting);
}
public Map<String, String> getInitParameters()
{
return _initParams;
}
public void setAsyncSupported(boolean asyncSupported)
{
_asyncSupported = asyncSupported;
}
public boolean isAsyncSupported()
{
return _asyncSupported;
}
/**
* Sets the servlet name default when not specified
*/
public void setServletNameDefault(String name)
{
_servletNameDefault = name;
}
/**
* Gets the servlet name default.
*/
public String getServletNameDefault()
{
return _servletNameDefault;
}
/**
* Gets the servlet name.
*/
public String getServletClassName()
{
return _servletClassName;
}
/**
*
* @return true:表示Servlet已配置完成
*/
public boolean isServletConfig()
{
return _servletClassName != null;
}
/**
* Sets the servlet class. 设置Servlet Class 并加载
*/
public void setServletClass(String servletClassName)
{
_servletClassName = servletClassName;
ClassLoader loader = _webApp.getClassLoader();
try
{
_servletClass = (Class<? extends Servlet>) Class.forName(servletClassName, false, loader);
}
catch (ClassNotFoundException e)
{
log.debug(e.toString(), e);
}
}
public void setServletClass(Class<? extends Servlet> servletClass)
{
if (servletClass == null)
{
throw new NullPointerException();
}
_servletClass = servletClass;
_servletClassName = servletClass.getName();
}
/**
* Gets the servlet class. 加载Servlet的Class
*/
public Class<? extends Servlet> getServletClass()
{
if (_servletClassName == null)
return null;
if (_servletClass == null)
{
try
{
ClassLoader loader = _webApp.getClassLoader();
_servletClass = (Class<? extends Servlet>) Class.forName(calculateServletClassName(), false, loader);
}
catch (Exception e)
{
error(_servletClassName+" | is not a known servlet class. Servlets belong in the classpath, for example WEB-INF/classes.", e);
}
}
return _servletClass;
}
protected String calculateServletClassName()
{
return getServletClassName();
}
public void setServlet(Servlet servlet)
{
_servlet = servlet;
}
/**
* Sets an init-param
*/
public void setInitParam(String param, String value)
{
_initParams.put(param, value);
}
/**
* Gets the init params
*/
public Map getInitParamMap()
{
return _initParams;
}
/**
* Gets the init params
*/
public String getInitParameter(String name)
{
return _initParams.get(name);
}
/**
* Gets the init params
*/
@Override
public Enumeration<String> getInitParameterNames()
{
return Collections.enumeration(_initParams.keySet());
}
/**
* Returns the servlet context.
*/
public ServletContext getServletContext()
{
return _servletContext;
}
/**
* Returns the servlet manager.
*/
public ServletManager getServletManager()
{
return _servletManager;
}
/**
* Sets the load-on-startup
*/
public void setLoadOnStartup(int loadOnStartup)
{
_loadOnStartup = loadOnStartup;
}
/**
* Gets the load-on-startup value.
*/
public int getLoadOnStartup()
{
if (_loadOnStartup > Integer.MIN_VALUE)
return _loadOnStartup;
else
return Integer.MIN_VALUE;
}
/**
* Sets the display name
*/
public void setDisplayName(String displayName)
{
_displayName = displayName;
}
/**
* Gets the display name
*/
public String getDisplayName()
{
return _displayName;
}
/**
* Sets the description
*/
public void setDescription(String description)
{
}
/**
* Returns the servlet.
*/
public Servlet getServlet()
{
return _servlet;
}
public void merge(ServletConfigImpl config)
{
if (_loadOnStartup == Integer.MIN_VALUE)
_loadOnStartup = config._loadOnStartup;
if (!getClassName().equals(config.getClassName()))
throw new RuntimeException(
"Illegal attempt to specify different servlet-class ["+config.getClassName()+"] for servlet '"+_servletName+"'. Servlet '"+_servletName+" has already been defined with servlet-class '"+_servletClassName+"'. Consider using <absolute-ordering> to exclude conflicting web-fragment."
);
for (Map.Entry<String, String> param : config._initParams.entrySet())
{
if (_paramNames.contains(param.getKey()))
{
}
else if (!_initParams.containsKey(param.getKey()))
_initParams.put(param.getKey(), param.getValue());
else if (!_initParams.get(param.getKey()).equals(param.getValue()))
{
throw new RuntimeException(
"Illegal attempt to specify different param-value of ["+param.getValue()+"] for parameter ["+param.getKey()+"]. This error indicates that two web-fragments use different values. Consider defining the parameter in web.xml to override definitions in web-fragment."
);
}
}
}
/**
* Initialize the servlet config. 初始化Servlet配置
*/
public void init() throws ServletException
{
if (_servletName == null)
{
if (getServletNameDefault() != null)
_servletName = getServletNameDefault();
else
setServletName(_servletClassName);
}
}
/**
* 验证Servlet的Class是否存在,是否实现javax.servlet.Servlet接口
*
* @param requireClass
* @throws ServletException
*/
protected void validateClass(boolean requireClass) throws ServletException
{
if (_loadOnStartup >= 0)
requireClass = true;
if (_servletClassName == null)
{
}
else
{
if(_servletClass == null)
{
try
{
_servletClass = (Class<? extends Servlet>) Class.forName(_servletClassName, false, _webApp.getClassLoader());
}
catch (ClassNotFoundException e)
{
log.debug(e.toString(), e);
}
}
if (_servletClass != null)
{
}
else if (requireClass)
{
throw error(_servletClassName+" | is not a known servlet class. Servlets belong in the classpath, for example WEB-INF/classes.");
}
else
{
String location = _location != null ? _location : "";
log.warn(location + _servletClassName+ " | is not a known servlet. Servlets belong in the classpath, often in WEB-INF/classes.");
return;
}
if (!Servlet.class.isAssignableFrom(_servletClass))
{
throw error(_servletClassName+" | must implement javax.servlet.Servlet All servlets must implement the Servlet interface.");
}
}
}
/**
* Checks the class constructor for the public-zero arg. 检查Class是否实现了无参数的public的构造方法
*/
public void checkConstructor() throws ServletException
{
Constructor[] constructors = _servletClass.getDeclaredConstructors();
Constructor zeroArg = null;
for (int i = 0; i < constructors.length; i++)
{
if (constructors[i].getParameterTypes().length == 0)
{
zeroArg = constructors[i];
break;
}
}
if (zeroArg == null)
throw error(_servletClassName+" | must have a zero arg constructor. Servlets must have public zero-arg constructors.\n"+(constructors != null ? constructors[0] : null)+" is not a valid constructor."
);
if (!Modifier.isPublic(zeroArg.getModifiers()))
throw error(zeroArg+" | must be public. '"+_servletClassName+"' must have a public, zero-arg constructor.");
}
public FilterChain createServletChain() throws ServletException
{
synchronized (this)
{
if (_servletChain == null)
_servletChain = createServletChainImpl();
return _servletChain; //Servlet是单例的,ServletFilterChain也可以是单例的,免去了每次都创建ServletFilterChain
}
}
/**
*
*
* @return
* @throws ServletException
*/
private FilterChain createServletChainImpl() throws ServletException
{
FilterChain servletChain = null;
if (_servlet != null)
{
servletChain = new ServletFilterChain(this);
return servletChain;
}
validateClass(true);
Class<?> servletClass = getServletClass();
if (servletClass == null)
{
throw new IllegalStateException("servlet class for '"+getServletName()+"' can't be null");
}
else
{
servletChain = new ServletFilterChain(this);
}
return servletChain;
}
/**
* Instantiates a servlet given its configuration.
*
* 返回Servlet的实例(单例的,多次调用返回同一个实例)
*
* 1:如果Servlet实例不存在,则创建一个Servlet实例,
* 并初始化它(调用javax.servlet.Servlet#init(ServletConfig config)
*
* 2:返回已初始化的Servlet实例.
*
*
* @return the initialized servlet.
*/
public Servlet getInstance() throws ServletException
{
// server/102e
if (_servlet != null)
return _servlet;
_servlet = createServletAndInit();
return _servlet;
}
/*
*
*
* 实例化一个Servlet,并初始化它
*
* servlet.init(this);
*/
private Servlet createServletAndInit() throws ServletException
{
Class<? extends Servlet> servletClass = getServletClass();
Servlet servlet;
if (servletClass == null)
throw new ServletException("Null servlet class for "+_servletName);
try
{
servlet = servletClass.newInstance();
}
catch (Exception e)
{
throw new ServletException(e);
}
// 配置Servlet
configureServlet(servlet);
//初始化
servlet.init(this);
if (debug)
log.debug("Servlet[" + _servletName + "] instantiated and inited");
return servlet;
}
/**
* Configure the servlet (everything that is done after instantiation but before servlet.init()
*/
void configureServlet(Object servlet)
{
}
/**
* @see javax.servlet.Servlet#destroy()
*/
public void destroyServlet()
{
Object servlet = _servlet;
_servlet = null;
if (servlet instanceof Servlet)
{
((Servlet) servlet).destroy();
}
}
/**
* 销毁Servlet
*/
public void close()
{
destroyServlet();
}
protected ServletException error(String msg)
{
ServletException e;
if (_location != null)
e = new ServletException(_location + msg);
else
e = new ServletException(msg);
log.warn(e.getMessage());
return e;
}
protected ServletException error(String msg, Throwable e)
{
ServletException e1;
if (_location != null)
e1 = new ServletException(_location + msg, e);
else
e1 = new ServletException(msg, e);
log.warn(e1.getMessage());
return e1;
}
protected RuntimeException error(Throwable e)
{
RuntimeException e1;
if (_location != null)
e1 = new RuntimeException(_location + e.getMessage(), e);
else
e1 = new RuntimeException(e);
log.warn(e1.toString());
return e1;
}
/**
* Returns a printable representation of the servlet config object.
*/
public String toString()
{
return getClass().getSimpleName() + "[name=" + _servletName + ",class=" + _servletClass + "]";
}
//unused-------------------------------------------------
@Override
public String getRunAsRole()
{
return null;
}
@Override
public void setRunAsRole(String roleName)
{
}
@Override
public Set<String> setServletSecurity(ServletSecurityElement constraint)
{
return null;
}
public ServletMapper getServletMapper()
{
return _servletMapper;
}
}