Tổng quan ba thành phần trong java web

Listener
Theo dõi các sự kiện khởi tạo hay hủy bỏ của những đối tượng như application, session hay request để thực thi các đoạn mã tương ứng với các sự kiện.Filter
Khi request đến servlet và trả về response có thể đi qua một hoặc nhiều filter(còn gọi là filter chain). Filter có thể thực hiện kiểm tra, sửa đổi các thuộc tính của request hay response.Servlet
Là module chính đóng vai trò như lớp trung gian giữa client và application phía server. Servlet tiếp nhận client request, xử lý rồi sau đó gửi trả response. Ngoài ra, để hiểu rõ hơn cách tomcat xử lí một file jsp bạn có thể tham khao qua link sau.Thực hiện tạo jsp memory webshell
Listener type
Ví dụ thêm listener trong tomcat

ServletRequestListener có chức năng lắng nghe sự khởi tạo hay hủy bỏ của ServletRequest
object và invoke hai method tương ứng là requestInitialized()
và requestDestroyed()
mỗi khi request được thực hiện. Lợi dụng class này ta có thể override lại hai method trên để gọi lệnh thực thi OS command, ở đây mình sẽ override method requestInitialized()
.
Đầu tiên, để lấy được các tham số truyền vào từ request thì method requestInitialized()
có nhận parameter là một ServletRequestEvent nhờ đó có thể lấy được ServletRequest thông qua ServletRequestEvent#getServletRequest()
.
Tiếp đến, để có thể trả về output của command được thực thi cần có HttpServletResponse để gửi trả response. Tuy nhiên, mặc định thì không thể lấy được object này từ class ServletRequestListener
, do đó ta cần tạo một custom listener có constructor nhận argument là một HttpServletResponse
object.
Vấn đề tiếp theo là làm sao để thêm custom listener trên vào tomcat trong runtime.
Thử debug thì thấy khi request đi qua StandardContext#fireRequestInitEvent()
thì method getApplicationEventListeners()
sẽ lấy tất cả listeners được định nghĩa ở file web.xml rồi kiểm tra từng object có phải instanceof class ServletRequestListener
hay không, nếu đúng thì gọi method requestInitialized()
như dòng 3752

StandardContext
có method addApplicationEventListener(T)
mà ta có thể dùng để thêm custom listener:

Cuối cùng, làm sao để lấy được StandardContext
từ request.
-
Đầu tiên có thể lấy
ServletContext
từ request bằng cách dùngrequest.getServletContext()
, thật ra method này không trực tiếp trả vềServletContext
mà trả vềApplicationContextFacade
, cái implementsServletContext
-
Hơn nữa, class
ApplicationContextFacade
có thuộc tính(ApplicationContext)context
và classApplicationContext
lại có thuộc tính(StandardContext)context
. Tuy nhiên có thể thấy các thuộc tính này đều là private nên ta cần dùng cơ chế java reflection để có thể lấy được.







ListenerShell#requestInitialized()
sẽ được gọi ở mọi request, nếu request có đúng param name như đã tự định nghĩa - ở đây là cmd_secret
thì command sẽ được thực thi và response trả về ouput, nếu không request sẽ tiếp tục một cách bình thường.
Filter type
Ví dụ thêm filter trong tomcat:

doFilter()
để thực hiện filter tasks. Đặt breakpoint tại FilterTest#doFilter
ta được đoạn stack frames đáng chú ý sau:
StandardWrapperValve#invoke()
-> StandardWrapperValve#filterChain.doFilter()
-> ApplicationFilterChain#this.internalDoFilter()
-> ApplicationFilterChain#filter.doFilter()
-> FilterTest#doFilter()

StandardWrapperValve#invoke()
thì filterChain.doFilter()
sẽ được gọi, biến filterChain
thì được khởi tạo ở factory.createFilterChain()
:


filterMaps
từ context.findFilterMaps()
dựa vào cấu hình ở file web.xml, các filterMap chứa toàn bộ thông tin về filter-mapping bao gồm cả filterName
và urlPatterns
:

List filterMaps
này sau đó sẽ đi vào vòng lặp for để so sánh urlPattern
của từng filterMap
có match với requestPath
của request hiện tại, nếu đúng sẽ lấy filterConfig
từ filterMap
này và cuối cùng đi vào filterChain.addFilter(filterConfig)
.
Cấu trúc của (ApplicationFilterConfig)filterConfig
như sau, chú ý member filterDef là object chứa các thông tin về filter như filterName
hay filterClass
:

StandardWrapperValve#invoke()
, filterChain sẽ bắt đầu thực thi method doFilter()
của từng filter:



Do vậy, để thêm một custom filter:
- Tạo
FilterDef
object chứa thông tin về custom filter muốn thêm. - Thêm
FilterDef
vàoStandardContext
và regenerate lạifilterConfig
bằngStandardContext#filterStart()

FilterMap
chứa thông tin về mapping vào StandardContext
Cuối cùng, ta được filter-type memory webshell như sau:




Servlet type
Ví dụ thêm servlet trong tomcat:

StandardContext
sau khi load các servlet:

children
là một HashMap với các item là StandardWrapper wrap các servlet.

servletMappings
cũng là một HashMap với key - value là mapping giữa các urlPattern và servlet:

Tóm lại, để thêm một custom servlet ta cần:
- Tạo
StandardWrapper
object wrap custom servlet cần thêm - Thêm
StandardWrapper
object vào memberchildren
củaStandardContext
- Thêm mapping giữa urlPattern và custom servlet vào member
servletMapping
củaStandardContext
Ta được servlet-type memory webshell sau:



ServletShell
thực thi command:
