博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Liferay Dynamic CSS Filter方法的研究 - 总体过程
阅读量:7124 次
发布时间:2019-06-28

本文共 6198 字,大约阅读时间需要 20 分钟。

背景知识:

最近项目组遇到一个问题就是改了一个new theme之后导致某些css文件不起作用了,这也激起了我的好奇心,让我有机会去研究下Liferay Dynamic CSS Filter的原理。

引入

这个Filter 和一般的Filter一样,会配置在portal-web.xml中,并且声明了对于.css文件和.jsp资源文件请求时候会触发:

然后执行它的processFilter 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected 
void 
processFilter(
            
HttpServletRequest request, HttpServletResponse response,
            
FilterChain filterChain)
        
throws 
Exception {
        
Object parsedContent = getDynamicContent(
            
request, response, filterChain);
        
if 
(parsedContent == 
null
) {
            
processFilter(
                
DynamicCSSFilter.
class
, request, response, filterChain);
        
}
        
else 
{
            
if 
(parsedContent 
instanceof 
File) {
                
ServletResponseUtil.write(response, (File)parsedContent);
            
}
            
else 
if 
(parsedContent 
instanceof 
String) {
                
ServletResponseUtil.write(response, (String)parsedContent);
            
}
        
}
    
}

调试场景:

比如当我们刚加载 liferay首页,因为上面有许多css资源文件,所以会自动触发这个调用,走入processFilter方法,而它会调用getDynamicContent()方法来获取jRuby解析Sass后的变成的普通css文件。这方法是我们这文章研究的重点。

getDynamicContent()的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
protected 
Object getDynamicContent(
            
HttpServletRequest request, HttpServletResponse response,
            
FilterChain filterChain)
        
throws 
Exception {
        
String requestURI = request.getRequestURI();
        
String requestPath = requestURI;
        
String contextPath = request.getContextPath();
        
if 
(!contextPath.equals(StringPool.SLASH)) {
            
requestPath = requestPath.substring(contextPath.length());
        
}
        
String realPath = ServletContextUtil.getRealPath(
            
_servletContext, requestPath);
        
if 
(realPath == 
null
) {
            
return 
null
;
        
}
        
realPath = StringUtil.replace(
            
realPath, CharPool.BACK_SLASH, CharPool.SLASH);
        
File file = 
new 
File(realPath);
        
String cacheCommonFileName = getCacheFileName(request);
        
File cacheContentTypeFile = 
new 
File(
            
cacheCommonFileName + 
"_E_CONTENT_TYPE"
);
        
File cacheDataFile = 
new 
File(cacheCommonFileName + 
"_E_DATA"
);
        
if 
((cacheDataFile.exists()) &&
            
(cacheDataFile.lastModified() >= file.lastModified())) {
            
if 
(cacheContentTypeFile.exists()) {
                
String contentType = FileUtil.read(cacheContentTypeFile);
                
response.setContentType(contentType);
            
}
            
return 
cacheDataFile;
        
}
        
String dynamicContent = 
null
;
        
String content = 
null
;
        
try 
{
            
if 
(realPath.endsWith(_CSS_EXTENSION) && file.exists()) {
                
if 
(_log.isInfoEnabled()) {
                    
_log.info(
"Parsing SASS on CSS " 
+ file);
                
}
                
content = FileUtil.read(file);
                
dynamicContent = DynamicCSSUtil.parseSass(
                    
request, realPath, content);
                
response.setContentType(ContentTypes.TEXT_CSS);
                
FileUtil.write(cacheContentTypeFile, ContentTypes.TEXT_CSS);
            
}
            
else 
if 
(realPath.endsWith(_JSP_EXTENSION) || !file.exists()) {
                
if 
(_log.isInfoEnabled()) {
                    
_log.info(
"Parsing SASS on JSP or servlet " 
+ realPath);
                
}
                
StringServletResponse stringResponse =
                    
new 
StringServletResponse(response);
                
processFilter(
                    
DynamicCSSFilter.
class
, request, stringResponse,
                    
filterChain);
                
CacheResponseUtil.setHeaders(
                    
response, stringResponse.getHeaders());
                
response.setContentType(stringResponse.getContentType());
                
content = stringResponse.getString();
                
dynamicContent = DynamicCSSUtil.parseSass(
                    
request, realPath, content);
                
FileUtil.write(
                    
cacheContentTypeFile, stringResponse.getContentType());
            
}
            
else 
{
                
return 
null
;
            
}
        
}
        
catch 
(Exception e) {
            
_log.error(
"Unable to parse SASS on CSS " 
+ realPath, e);
            
if 
(_log.isDebugEnabled()) {
                
_log.debug(content);
            
}
            
response.setHeader(
                
HttpHeaders.CACHE_CONTROL,
                
HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
        
}
        
if 
(dynamicContent != 
null
) {
            
FileUtil.write(cacheDataFile, dynamicContent);
        
}
        
else 
{
            
dynamicContent = content;
        
}
        
return 
dynamicContent;
    
}

我们附上调试信息:

从上述调试信息一目了然,主要是第5行获取当前请求的URI,从调试信息看,它的内容是:

html/portlet/login/css/main.css , 这个也正符合我们的猜想,因为当前请求的资源文件main.css符合CSS扩展名的模式,所以才被这个Dynamic CSS Filter所过滤到并且进入这个断点。

第16行获取这个资源文件的真实路径realPath(疑问1: 如何获取这个真实的path的? 答案在以后讨论中给出) ,这里给出的路径是

/app/Liferay/RI/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/webapps/ROOT/html/portlet/login/css/main.css

,这也正符合我们当初的设想,因为这个main.css我们的确是一年前把它手动复制到了该目录下。

然后第19行计算出这个文件对应的缓存base文件名,因为一个缓存文件总是由2部分组成,一个是内容类型文件,一个是数据文件,他们的各自名字都是由base名字加上指定后缀拼接而成。内容类型文件的名字是<cacheCommonFileName>_E_CONTENT_TYPE,而缓存数据文件的名字是<cacheCommonFileName>_E_DATA. (疑问2:如何计算得到这个缓存base文件名?答案也在后续讨论中给出) ,所以我们通过计算得到的缓存base文件名为:

/app/Liferay/RI/liferay-portal-6.1.0-ce-ga1/tomcat-7.0.23/temp/liferay/css/portal/623927847558055413

下面既然得到了缓存base文件名,并且依照后缀定则拼接了相应的内容类型文件名和数据文件名,那么下面的工作就是第20行和第21行在相应位置创建相应的File对象了。因为new File()按照我们对于java的语义,就是如果这个文件不存在,那么则创建新文件,如果存在,只File对象指向已知文件。

我们到服务器目录下看到了这个2个文件:

当缓存内容类型文件和缓存内容文件都固定下来后,下面就考虑到更新或者填入内容到这些文件了。

首先,从第35行开始,还是从原始的带有Sass的css文件入手:

1
2
3
4
5
6
7
8
9
10
if 
(realPath.endsWith(_CSS_EXTENSION) && file.exists()) {
                
if 
(_log.isInfoEnabled()) {
                    
_log.info(
"Parsing SASS on CSS " 
+ file);
                
}
                
content = FileUtil.read(file);
                
dynamicContent = DynamicCSSUtil.parseSass(
                    
request, realPath, content);
                
response.setContentType(ContentTypes.TEXT_CSS);
                
FileUtil.write(cacheContentTypeFile, ContentTypes.TEXT_CSS);
            
}

它先判断原始文件是不是CSS扩展名的文件,如果是,那么就读取这个原始的css文件到一个字符串变量content中,见以下的调试信息:

然后调用DynamicCSSUtil的parseSass()方法吧这个带Sass的css文件解析成一个不带Sass的普通css文件,并且结果存放在dynamicContent变量中,比如上述content被解析后存放到的dynamicContent的值如下:

读者很容易看出这个新的样式文件是和原来不一样了,不仅仅是排版格式还有语法。

最后,把相应的内容写入到刚才最早的生成的内容类型文件和数据文件中。

源代码的第64行在内容类型文件(<cacheCommonFileName>_E_CONTENT_TYPE)中写入内容为 text/css

FileUtil.write(cacheContentTypeFile, ContentTypes.TEXT_CSS);

而第82行在数据文件中(<cacheCommonFileName>_E_DATA)写入刚才生成Sass解析后生成的普通css文件内容

FileUtil.write(cacheContentTypeFile, stringResponse.getContentType()

(疑问3:这个写入过程细节是这样的呢?比如文件为空和文件中已经有内容各是如何处理的? 这个答案也在以后讨论中给出)

最后在第88行中返回的动态生成的普通css文件字符串。

现在我们返回到processFilter方法中,既然已经得到了Sass解析后生成的普通css字符串,所以最后就是把这个字符串返回到客户端,所以在processFilter()方法的行末:

ServletResponseUtil.write(response, (String)parsedContent);

这样我们访问页面时候就可以正确的看到和使用这里的样式了。



总结:

从非常宏观的角度,我们至少有以下几点收获:

(1)DynamicCssFilter的调用时机是在站点请求响应的资源文件的时候触发的。

(2)访问资源文件时,它会从原始含有Sass语法的css文件中获取原始内容,然后用jRuby引擎进行解析从而获得新的解析后的普通css文件。

(3)解析后的css文件总会最终被写入到缓存内容文件中,这个缓存数据文件的后缀总是_E_DATA,并且它总是对应一个内容类型文件,这个内容类型文件的格式总是_E_CONTENT_TYPE.

(4)解析后的文件会被服务器写到最终输出流中,从而你在浏览器中可以看到并且使用这个被解析后的普通css文件。


我们还留着几个疑点,会在接下来的文章中得到解决。

本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/1282753,如需转载请自行联系原作者
你可能感兴趣的文章
通过ADG技术迁移单实例到rac集群上
查看>>
Socket中的异常和参数设置
查看>>
javascript资料
查看>>
python 进入windows指定路径,创建文件
查看>>
git 本地代码到github
查看>>
等价类划分法设计测试用例
查看>>
js 大图轮播
查看>>
二分搜索首次出现被搜索元素的位置
查看>>
oracle密码过期
查看>>
[SDOI2011]染色 BZOJ2243 树链剖分+线段树
查看>>
DNS检测
查看>>
ssl 握手过程【收藏】
查看>>
form表单数据进行json转换
查看>>
Linux下文件及文件夹的ctime atime mtime的含义详解
查看>>
spring springmvc 注解@service 运行时提示找不到这个service的问题
查看>>
Tomcat架构(四)
查看>>
Spark:交叉验证选择参数集
查看>>
Delphi常用取整函数
查看>>
树莓派高级GPIO库,wiringpi2 for python使用笔记(四)实战DHT11解码
查看>>
0307yuju
查看>>