AA部署中,修改Context属性造成的问题

此处利用了https://github.com/jeonghanlee/epicsarchiverap-env将AA安装在/opt/epicsarchiverap/下,并以系统服务的方式启停。

问题产生原因

地球人都知道,URL请求:xxx:17665/mgmt/ui/index.html用于访问AA管理页面。这样很不安全,希望更改Context path,将mgmt改为别的字符串,来隐藏AA管理页面。
修改/opt/epicsarchiverap/mgmt/conf/server.xml文件中的Context属性:

...
<Host ...>
	<Context docBase="mgmt" path="/hahaha"></Context> # 加入此行,将URL请求中原本是mgmt的部分改为hahaha
	...
</Host>

重启AA服务,在浏览器导航栏输入URL请求:xxx:17665/hahaha/ui/index.html,报错500。查看日志文件,/opt/epicsarchiverap/mgmt/logs/localhost.2022-07-19.log中报空指针异常:

19-Jul-2022 09:52:24.307 SEVERE [http-nio-17665-exec-5] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [StaticContent] in context with path [/hahaha] threw exception
	java.lang.NullPointerException
		at org.epics.archiverappliance.mgmt.MgmtUIFilter.doFilter(MgmtUIFilter.java:53)
		at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
		at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
		at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
		at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
		at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
		at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
		at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
		at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
		at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
		at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
		at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
		at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
		at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
		at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1726)
		at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
		at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
		at java.base/java.lang.Thread.run(Thread.java:834)

发现是在调用org.epics.archiverappliance.mgmt.MgmtUIFilter的doFilter()方法时,报的空指针异常。MgmtUIFilter.java的第53行:

52  MgmtRuntimeState runtime = configService.getMgmtRuntimeState();
53  if(runtime.haveChildComponentsStartedUp()) {
54  			chain.doFilter(request, response);
55  			...

那么就是此处的runtime是空指针。runtime来自哪里呢?在源码包epicsarchiverap-master\src\main\org\epics\archiverappliance\config\DefaultConfigService.java中找到答案:

	...
	protected MgmtRuntimeState mgmtRuntime = null;
	...
	@Override
	public void initialize(ServletContext sce) throws ConfigException {
		this.servletContext = sce;
		String contextPath = sce.getContextPath();
		logger.info("DefaultConfigService was created with a servlet context " + contextPath);
		...
		switch(contextPath) {
		case "/mgmt":
			warFile = WAR_FILE.MGMT;
			this.mgmtRuntime = new MgmtRuntimeState(this);
			break;
		...
		default:
			logger.error("We seem to have introduced a new component into the system " + contextPath);
		}
		...
	}
		
	@Override
	public MgmtRuntimeState getMgmtRuntimeState() {
		return mgmtRuntime;
	}

可见,由于此时的Context属性中,contextPath被设置为了“hahaha",所以在initialize()方法中不会再执行this.mgmtRuntime = new MgmtRuntimeState(this);因此mgmtRuntime一直是空指针。
查看/opt/epicsarchiverap/mgmt/logs/archappl_service.log日志文件也可证明contextPath被设置为了“hahaha":

83   [main] INFO  org.epics.archiverappliance.config.DefaultConfigService  - DefaultConfigService was created with a servlet context /hahaha
...
97   [main] ERROR org.epics.archiverappliance.config.DefaultConfigService  - We seem to have introduced a new component into the system /hahaha

那么,最根本的问题:为什么会调用到org.epics.archiverappliance.mgmt.MgmtUIFilter.doFilter()方法?
这是因为在/opt/epicsarchiverap/mgmt/webapps/mgmt/WEB-INF/web.xml文件中,定义了一个过滤器:

<web-app>
   <filter>
       <filter-name>MgmtUIFilter</filter-name>
       <filter-class>org.epics.archiverappliance.mgmt.MgmtUIFilter</filter-class>
   </filter>
   ...
	<filter-mapping>
       <filter-name>MgmtUIFilter</filter-name>
       <url-pattern>/ui/*</url-pattern>
       <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    ...

在此名为“hahaha”的web应用(Context)中,所有类似于“/ui/*”的URL请求都需要经过此过滤器过滤。因此,我们输入URL请求:xxx:17665/hahaha/ui/index.html时,首先会经过MgmtUIFilter过滤器的doFilter()方法进行过滤。

解决方法

由于在web应用初始化过程中,需要对ConfigService(储存了该web应用的相关信息)进行初始化,然而其初始化代码中,只对标准的Context path(mgmt、etl、engine、retrieve)进行了初始化操作,对于我们自定义的Context path(如“hahaha”)缺少了很多初始化信息,这些信息指针为空。而后续的Servlet(如MgmtUIFilter、StaticContentServlet等)中会使用到这些缺少的初始化信息,因此解决办法还是应该使这些信息得到初始化。
目前的DefaultConfigService.java源码不支持我们修改mgmt、etl、engine、retrieve这4个web应用的Context属性,可以修改DefaultConfigService.java这部分的代码,使我们自定义的Context path也可以得到相应的初始化。

还有一种治标不治本的方法,就是去掉mgmt/WEB-INF/web.xml文件中的过滤器部分,也就是不添加MgmtUIFilter过滤器。重启AA后,URL请求:xxx:17665/hahaha/ui/index.html可以正常打开该web页面。但这种解决方法坏处在于:

  1. 未解决其他Servlet(如StaticContentServlet等)的信息空指针异常问题,后续HTTP请求及web页面还是会出问题。如:
    19-Jul-2022 11:15:47.172 SEVERE [http-nio-17665-exec-10] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [StaticContent] in context with path [/hahaha] threw exception
    	java.lang.NullPointerException
    		at org.epics.archiverappliance.common.StaticContentServlet$PathSequence.templateReplace(StaticContentServlet.java:475)
    
  2. MgmtUIFilter主要作用是对ConfigService是否正确初始化以及其他几个web应用(etl、engine、retrieve)是否已启动完成的查验,若这些查验不通过,则返回错误信息给web页面,避免进一步的HTTP请求和处理。MgmtUIFilter实际上是对AA服务的一种保护,将其删掉不合适。

附加内容

DefaultConfigService 的创建及初始化时间,是在整个web应用初始化时。

来自epicsarchiverap-master\src\main\org\epics\archiverappliance\config\ArchServletContextListener.java:

public class ArchServletContextListener implements ServletContextListener {
	...
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		...
			configService = new DefaultConfigService();
		...
		configService.initialize(sce.getServletContext());			
		sce.getServletContext().setAttribute(ConfigService.CONFIG_SERVICE_NAME, configService);
		...

来自/opt/epicsarchiverap/mgmt/webapps/mgmt/WEB-INF/web.xml:

	<listener>
		<listener-class>org.epics.archiverappliance.config.ArchServletContextListener</listener-class>
	</listener> 

Tomcat 的 Filter定义、初始化、调用

  1. Filter编写和注册:Tomcat过滤器
  2. Filter和FilterChain初始化:【Tomcat】Filter 原理
  3. Filter和FilterChain的调用:Http请求到达后进行的处理——StandardWrapperValve的invoke方法创建一个ApplicationFilterChain;接着调用FilterChain的doFilter方法,对filter进行逐个调用,触发其生命周期事件;doFilter方法最后调用了该Http请求对应的Servlet的service方法,所有的内容都以Response返回。浅析Tomcat之StandardWrapperValve的Servlet请求处理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值