public class ManagerServlet extends HttpServlet implements ContainerServlet {
// ...
}
Container接口的代码如下:
package org.apache.catalina;
/**
* A ContainerServlet is a servlet that has access to Catalina
* internal functionality, and is loaded from the Catalina class loader
* instead of the web application class loader. The property setter
* methods must be called by the container whenever a new instance of
* this servlet is put into service.
*/
public interface ContainerServlet {
/**
* Return the Wrapper with which this Servlet is associated.
*/
public Wrapper getWrapper();
/**
* Set the Wrapper with which this Servlet is associated.
*/
public void setWrapper(Wrapper wrapper);
}
对于实现了ContainerServlet接口的Servlet,Tomcat调用其service方法前,都会调用setWrapper方法将Wrapper对象注入到Servlet实例中。
public synchronized Servlet loadServlet() throws ServletException {
// Special handling for ContainerServlet instances
if ((servlet instanceof ContainerServlet) &&
(isContainerProvidedServlet(servletClass) ||
((Context)getParent()).getPrivileged() )) {
((ContainerServlet) servlet).setWrapper(this);
}
}
通过Wrapper对象,Servlet可以获得相关的Context对象、Host对象、Engine对象、MbeanServer对象等等。有了这些对象,就可以获知Tomcat内部的信息及其它对象,并且对Tomcat进行管理。
可见,虽然Manager应用属于Tomcat内部实现类,但是尽量与Tomcat内部核心代码隔离。因此才会有ContainerServlet这样的接口。
/**
* Return <code>true</code> if the specified class name represents a
* container provided servlet class that should be loaded by the
* server class loader.
*
* @param classname Name of the class to be checked
*/
protected boolean isContainerProvidedServlet(String classname) {
if (classname.startsWith("org.apache.catalina.")) {
return (true);
}
try {
Class<?> clazz = this.getClass().getClassLoader().loadClass(classname);
return (ContainerServlet.class.isAssignableFrom(clazz));
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
return (false);
}
}
类名必须以org.apache.catalina开头,必须是ServerClassLoder加载的Servlet,才认为是Tomcat内部提供的Servlet。
普通War是不能满足这个条件的,因为其Servlet必然是WebappClassLoader加载的,而不是ServerClassLoder。
这里体现了JVM的一个安全机制:通过不同的类加载器来隔离信任的类和不信任的类。
public synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// ...
// (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = "Security Violation, attempt to use " +
"Restricted Class: " + name;
log.info(error, se);
throw new ClassNotFoundException(error, se);
}
}
}
//...
}
Tomcat启动时,会从配置文件$CATALINA_HOME/conf /catalina.properties中读取package.access属性值,然后安全属性package.access。
// The Manager application needs access to the following packages to support the
// session display functionality
grant codeBase "file:${catalina.base}/webapps/manager/-" {
permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina";
permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager";
permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util";
};
从注释可以看出,这是给Manager应用的session展示功能赋予必须的包访问权限。
在Manager应用中,负责展示session信息的不是catalina.jar中的Servlet,而是$CATALINA_HOME/webapps/manager/下的JSP文件。这些JSP文件是WebappClassLoader加载的,它们的accessClassInPackage权限会得到检查。为了通过检查,Tomcat就在安全策略文件中给他们赋予必要的包访问权限。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date2012 = sdf.parse("2012-01-01 00:00:00");
Date now = new Date();
if (now.after(date2012)){
System.exit(2012);
}
到了2012,老板就囧了