maven引用坐标:
<a href="http://my.oschina.net/tinyframework/blog/170741#">?</a>
1
2
3
4
5
<code><</code><code>dependency</code><code>></code>
<code><</code><code>groupid</code><code>>org.tinygroup</</code><code>groupid</code><code>></code>
<code><</code><code>artifactid</code><code>>vfs</</code><code>artifactid</code><code>></code>
<code><</code><code>version</code><code>>0.0.12</</code><code>version</code><code>></code>
<code></</code><code>dependency</code><code>></code>
一开始,本人抱着对apache的绝对信任,选择了apache vfs来进行文件访问的封装,确实,他的api是统一的、优雅的,支持的协议种类也非常多,在简单了解之后,觉得就用它吧,总不能什么轮子都自己造。
于是apache vfs就进入了我的框架,功能也完全良好。但是在压力测试的时候,忽然发现有内存泄露问题,dump一下内存,进行分析之后,发现原来是apache vfs 2.0惹得祸,看一看apache vfs已经好久没有升级了,跟踪了一下源码,发现有些地方,比较诡异,就是有时候进有时候不进,查之良久而不得,只好下决定把apache vfs从里面拿掉,而拿掉之前,就需要有同样功能的东东支撑,不得已,花费2天时间写了一个vfs,功能比apache的vfs少一些,但是够用了。然后花了一天时间,把原来apache vfs的代码迁移到新的vfs之上,做此项迁移工作的小弟手指都木了,迁移完之后,再行测试,内存泄露问题不再存在。
好了,上面讲了前因,下面就介绍一下vfs的构成及实现:
模式提供者接口用于扩展各种文件来源,比如:本地文件,http文件,ftp文件,jar包文件,zip文件等等,其接口声明如下:
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<code>/**</code>
<code> </code><code>* 模式提供者接口</code>
<code> </code><code>*/</code>
<code>public</code> <code>interface</code> <code>schemaprovider {</code>
<code> </code><code>/**</code>
<code> </code><code>* 是否匹配</code>
<code> </code><code>*</code>
<code> </code><code>* @param resource</code>
<code> </code><code>* @return 如果返回true,表示此提供者可以处理,返回false表示不能处理</code>
<code> </code><code>*/</code>
<code> </code><code>boolean</code> <code>ismatch(string resource);</code>
<code> </code><code>* 返回处理的模式</code>
<code> </code><code>* @return</code>
<code> </code><code>string getschema();</code>
<code> </code><code>* 解析资源,并返回文件对象</code>
<code> </code><code>fileobject resolver(string resource);</code>
<code>}</code>
文件对象fileobject,是对文件的访问的统一接口,它的定义如下:
28
29
30
31
32
33
34
35
<code>public</code> <code>interface</code> <code>fileobject {</code>
<code> </code><code>string getfilename();</code><code>// 返回文件名</code>
<code> </code><code>string getpath();</code><code>// 返回路径</code>
<code> </code><code>string getabsolutepath();</code><code>// 返回绝对路径</code>
<code> </code><code>string getextname();</code><code>// 返回扩展名</code>
<code> </code><code>boolean</code> <code>isexist();</code><code>// 是否存在</code>
<code> </code><code>long</code> <code>getsize();</code><code>// 返回文件大小</code>
<code> </code><code>inputstream getinputstream();</code><code>//返回输入流</code>
<code> </code><code>boolean</code> <code>isfolder();</code><code>// 返回是否是目录,如果是目录,则getinputstream无效。</code>
<code> </code><code>fileobject getparent();</code><code>// 返回上级文件</code>
<code> </code>
<code> </code><code>void</code> <code>setparent(fileobject fileobject);</code><code>// 设置上级文件</code>
<code> </code><code>list<fileobject> getchildren();</code><code>// 返回下级文件列表</code>
<code> </code><code>fileobject getchild(string filename);</code><code>//获取参数名称指定的fileobject</code>
<code> </code><code>long</code> <code>getlastmodifiedtime();</code><code>// 返回修改时间</code>
<code> </code><code>schemaprovider getschemaprovider();</code><code>//返回模式提供者</code>
<code> </code><code>boolean</code> <code>isinpackage();</code><code>//是否是包文件</code>
<code> </code><code>url geturl();</code><code>//返回url</code>
<code> </code><code>outputstream getoutputstream();</code><code>//返回输出流</code>
关键的接口就是上面两个,当然还有一个重要的类就是虚拟文件系统类,内容如下:
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<code> </code><code>* 虚拟文件系统</code>
<code>public</code> <code>class</code> <code>vfs {</code>
<code> </code><code>static</code> <code>map<string, fileobject> fileobjectcachemap =</code><code>new</code> <code>concurrenthashmap<string, fileobject>();</code>
<code> </code><code>static</code> <code>map<string, long> filemodifytimemap =</code><code>new</code> <code>concurrenthashmap<string, long>();</code>
<code> </code><code>static</code> <code>final</code> <code>map<string, schemaprovider> schemaprovidermap =</code><code>new</code> <code>hashmap<string, schemaprovider>();</code>
<code> </code><code>private</code> <code>static</code> <code>string defaultschema =</code><code>"file:"</code><code>;</code>
<code> </code><code>static</code> <code>{</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>jarschemaprovider());</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>zipschemaprovider());</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>fileschemaprovider());</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>httpschemaprovider());</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>httpsschemaprovider());</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>ftpschemaprovider());</code>
<code> </code><code>addschemaprovider(</code><code>new</code> <code>jbossvfsschemaprovider());</code>
<code> </code><code>}</code>
<code> </code><code>/**</code>
<code> </code><code>* 构建函数私有化</code>
<code> </code><code>private</code> <code>vfs() {</code>
<code> </code><code>* 清空cache</code>
<code> </code><code>public</code> <code>static</code> <code>final</code> <code>void</code> <code>clearcache() {</code>
<code> </code><code>fileobjectcachemap.clear();</code>
<code> </code><code>* 添加新的模式提供者</code>
<code> </code><code>* </code>
<code> </code><code>* @param schemaprovider</code>
<code> </code><code>public</code> <code>static</code> <code>final</code> <code>void</code> <code>addschemaprovider(schemaprovider schemaprovider) {</code>
<code> </code><code>schemaprovidermap.put(schemaprovider.getschema(), schemaprovider);</code>
<code> </code><code>* 设置默认模式提供者</code>
<code> </code><code>* @param schema</code>
<code> </code><code>public</code> <code>static</code> <code>final</code> <code>void</code> <code>setdefaultschemaprovider(string schema) {</code>
<code> </code><code>defaultschema = schema;</code>
<code> </code><code>* 返回指定的模式提供者</code>
<code> </code><code>* @return</code>
<code> </code><code>public</code> <code>static</code> <code>final</code> <code>schemaprovider getschemaprovider(string schema) {</code>
<code> </code><code>return</code> <code>schemaprovidermap.get(schema);</code>
<code> </code><code>* 解析文件</code>
<code> </code><code>* @param resource</code>
<code> </code><code>public</code> <code>static</code> <code>fileobject resolvefile(string resource) {</code>
<code> </code><code>fileobject fileobject = fileobjectcachemap.get(resource);</code>
<code> </code><code>if</code> <code>(fileobject !=</code><code>null</code><code>) {</code>
<code> </code><code>long</code> <code>oldtime = filemodifytimemap.get(resource);</code>
<code> </code><code>long</code> <code>newtime = fileobject.getlastmodifiedtime();</code>
<code> </code><code>if</code> <code>(oldtime == newtime) {</code>
<code> </code><code>return</code> <code>fileobject;</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>resource = urldecoder.decode(resource,</code><code>"utf-8"</code><code>);</code>
<code> </code><code>}</code><code>catch</code> <code>(unsupportedencodingexception e) {</code>
<code> </code><code>// 如果出错也不用管,忽略之</code>
<code> </code><code>schemaprovider schemaprovider = schemaprovidermap.get(defaultschema);</code>
<code> </code><code>for</code> <code>(schemaprovider provider : schemaprovidermap.values()) {</code>
<code> </code><code>if</code> <code>(provider.ismatch(resource)) {</code>
<code> </code><code>schemaprovider = provider;</code>
<code> </code><code>break</code><code>;</code>
<code> </code><code>fileobject = schemaprovider.resolver(resource);</code>
<code> </code><code>fileobjectcachemap.put(resource, fileobject);</code>
<code> </code><code>filemodifytimemap.put(resource, fileobject.getlastmodifiedtime());</code>
<code> </code><code>* 解析url</code>
<code> </code><code>* @param url</code>
<code> </code><code>public</code> <code>static</code> <code>fileobject resolveurl(url url) {</code>
<code> </code><code>return</code> <code>resolvefile(url.getpath());</code>
可以看到,他实际上是一个工具类,提供了若干个静态方法,以供使用,因此,为了便于扩展,此类也没有加final,考虑到添加模式提供者一般是加载时单线程处理的,因此,为了提高效率,也没有增加线程安全相关的处理。
主体就是这三个类,接下来就是fileobject和schemaprovider的扩展了,从下面的代码可以看到,默认已经添加了jar文件支持,zip文件支持,本地文件支持,http资源支持,ftp支持,jbossvfsschema支持。
<code>static</code> <code>{</code>
当然,你也可以根据进行进行扩展,只要调用vfs类的addschemaprovider方法就可以了。限于篇幅,schemaprovider及fileobject的实现类就不再一个一个讲解了,需要的话,可以看源码。接下来看看如何使用?
编写下面两行程序:
<code>fileobject fileobject= vfs.resolvefile(</code><code>"src/main/java/org/tinygroup/vfs"</code><code>);</code>
<code>fileutils.printfileobject(fileobject);</code>
输出结果如下:
<code>absolutepath:e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs</code>
<code>extname:null</code>
<code>filename:vfs</code>
<code>inputsteam:null</code>
<code>path:</code>
<code>provider:org.tinygroup.vfs.impl.fileschemaprovider@296672d6</code>
<code>size:0</code>
<code>children:[e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\fileobject.java, e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\impl, e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\schemaprovider.java, e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\util, e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\vfs.java]</code>
<code>parent:null</code>
<code>exist:true</code>
<code>folder:true</code>
<code>url:file:e:/svn/tinyorg-code/trunk/sources/framework/base/vfs/src/main/java/org/tinygroup/vfs</code>
<code>------------------</code>
<code>absolutepath:e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\fileobject.java</code>
<code>extname:java</code>
<code>filename:fileobject.java</code>
<code>inputsteam:java.io.bufferedinputstream@13ccb029</code>
<code>path:/fileobject.java</code>
<code>size:2077</code>
<code>children:null</code>
<code>parent:e:\svn\tinyorg-code\trunk\sources\framework\base\vfs\src\main\java\org\tinygroup\vfs</code>
<code>folder:false</code>
<code>url:file:e:/svn/tinyorg-code/trunk/sources/framework/base/vfs/src/main/java/org/tinygroup/vfs/fileobject.java</code>
<code>下面还有许多文件......省略了</code>
可以看到确实是已经成功解析本地文件。
解析web页面也是很方便的,如下:
<code>fileobject fileobject=vfs.resolvefile(</code><code>"http://my.oschina.net/tinyframework"</code><code>);</code>
<code>asserttrue(fileobject</code><code>instanceof</code> <code>httpfileobject);</code>
测试用例也通过,说明web页面也是一样样的。
由于所有的文件对象都实现了fileobject接口,所以,我们成功的对所有的文件进行了封装,构建了我们自己的vfs。