Welcome to click “Beauty of Algorithms and Programming” ↑ pay attention to us!
This article was first published on the wechat official account “Beauty of Algorithms and Programming”. Welcome to follow and learn more about this series of blogs in time.
From the previous two lectures, we know that the client request will pass through a series of filters and finally reach the Servlet. We have an in-depth understanding of the implementation mechanism of Tomcat filter and related processing of Servlet. With that in mind, we have to ask, what happens to the client request before it reaches this set of filters?
1 The target
The goal of this source code analysis is to understand what client requests do in StandardWrapperValve.
2 Analysis method
According to the first stack information, combined with Intellij Idea stack view, breakpoints, single step debugging and other means of source analysis.
9. atorg.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
3 Analysis process
As you can see from the figure above, the client request goes through a series of valves before it reaches the filter. In this talk, we’ll focus on the StandardWrapperValve.
3.1 Initializing ApplicationFilterChain
StandardWrapperValue’s Invoke () method has a lot of code, but the core code is how to instantiate the ApplicationFilterChain.
// Create the filter chain for this request
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request,wrapper, servlet);
Create the ApplicationFilterChain object in factory mode, which is a typical way to create objects using the factory pattern. When we are source code analysis, we often encounter the use of a variety of design patterns, so before the source code analysis work began, we must be familiar with the concept of a variety of classic patterns and general code writing ideas, which will greatly help you better and faster to understand the source code.
Every request that arrives in the Tomcat container goes through a series of valves, namely the StandardWrapperValve, where an ApplicationFilterChain object is instantiated, This kind of object creation is so frequent that a creative design pattern is needed to solve this problem. * * * *
Let’s focus on how the factory pattern creates objects.
When doing source code analysis, we focus only on the most core processes, and ignore other code that is not relevant to the core business.
filterChain= (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } * * * *
First check to see if the REQ already has a filterChain object. If not, instantiate it directly and assign it to the properties of the REQ.
Now that we have a basic filterChain object, we need to assign some of its key properties.
As we know from the last class, the core of Tomcat’s filter mechanism design is the filterChain, which manages all the filters, so all the filter information related to this request needs to be added during initialization. This work is also at the heart of filterChain initialization.
3.2 Adding a filter to filterChain
When we configure the filter, we first define the filter name:
sampleFilter
sampleFilter
There are two main ways to configure this mapping,
The first is url-pattern, as follows:
<filter-mapping>
<filter-name>sampleFilter</filter-name>
<url-pattern>/sample</url-pattern>
</filter-mapping>
The second method is servlet-name, as shown below:
sampleFilter
helloServlet
Therefore, the core of adding filters to filterChain is the implementation of these two methods, and we will continue to describe how Tomcat implements these two methods.
3.2.1 url – the pattern
Adding filters based on url-pattern:
// Add the relevant path-mapped filters to this filter chain
for (int i = 0; i < filterMaps.length; i++) {
if(! matchDispatcher(filterMaps[i],dispatcher)) {continue;
}
if(! matchFiltersURL(filterMaps[i],requestPath))continue;
ApplicationFilterConfig filterConfig= (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - logconfiguration problem
continue;
}
filterChain.addFilter(filterConfig);
}
Copy the code
The core method is matchFiltersURL(), which adds all filters that match the requested path.
private static boolean matchFiltersURL(String testPath, StringrequestPath) {
if (testPath == null)
return false;
// Case 1 - Exact Match
if (testPath.equals(requestPath))
return true;
// Case 2 - Path Match("/... / * ")
if (testPath.equals("/ *"))
return true;
if (testPath.endsWith("/ *")) {
if (testPath.regionMatches(0, requestPath, 0,
testPath.length() - 2)) {
if (requestPath.length() ==(testPath.length() - 2)) {return true;
} else if ('/' ==requestPath.charAt(testPath.length() - 2)) {
return true; }}return false;
}
// Case 3 - Extension Match
if (testPath.startsWith("*.")) {
int slash = requestPath.lastIndexOf('/');
int period = requestPath.lastIndexOf('. ');
if ((slash >= 0) && (period > slash) && (period ! =requestPath.length() -1)
&&((requestPath.length() - period)
== (testPath.length() - 1))) {
return (testPath.regionMatches(2, requestPath, period + 1,
testPath.length() - 2)); }}// Case 4 - "Default" Match
return false; // NOTE - Not relevantfor selecting filters
}
Copy the code
The above is the matching of the four situations, no more detailed description, interested students can have a deeper understanding, we will arrange the article introduction later.
3.2.2 servlet -name way
// Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if(! matchDispatcher(filterMaps[i],dispatcher)) {continue;
}
if(! matchFiltersServlet(filterMaps[i],servletName))continue;
ApplicationFilterConfig filterConfig= (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - logconfiguration problem
continue; } filterChain.addFilter(filterConfig); } The core method is matchFiltersServlet(), as follows:private static boolean matchFiltersServlet(FilterMap filterMap, String servletName) {
if (servletName == null) {
return false;
}
// Check the specific "*"special servlet name
else if (filterMap.getMatchAllServletNames()) {
return true;
} else {
String[] servletNames =filterMap.getServletNames();
for (int i = 0; i < servletNames.length; i++) {
if (servletName.equals(servletNames[i])){
return true; }}return false; }}Copy the code
Get the ServletNames of all filters, then iterate through the filters that match the current servletName and add them to the filterChain.
Note that the two methods of adding filters may be repeated. Therefore, filterChain also performs a repeat check before adding filters. If the array capacity is insufficient, the array capacity will be expanded.
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being addedmultiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if (n == filters.length) {
ApplicationFilterConfig[]newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
Copy the code
Also note that the array stored in ApplicationFilterChain is not Filter[], but ApplicationFilterConfig[], The ApplicationFilterConfig class implements the FilterConfig interface, which holds all information about the filter name, initialization parameters, and so on, as defined as follows:
public interface FilterConfig {
public String getFilterName(a);
public ServletContext getServletContext(a);
public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames(a);
}
Copy the code
4 summarizes
This talk focuses on the StandardWrapperValve execution process, especially the initialization of the ApplicationFilterChain, as the client request passes through a series of valves before reaching the filter.
In the next lecture we will examine the StandardContextValve component.
To know what will happen next, please continue to follow the “Beauty of Algorithms and programming” wechat public account, timely learn more exciting articles.
More source code analysis highlights:
DoGet method for Tomcat source Code Analysis (2)
DoGet method for Tomcat source Code Analysis (1)
Append method for StringBuffer source analysis