天天看点

虚拟文件系统VFS

maven引用坐标:

<a href="http://my.oschina.net/tinyframework/blog/170741#">?</a>

1

2

3

4

5

<code>&lt;</code><code>dependency</code><code>&gt;</code>

<code>&lt;</code><code>groupid</code><code>&gt;org.tinygroup&lt;/</code><code>groupid</code><code>&gt;</code>

<code>&lt;</code><code>artifactid</code><code>&gt;vfs&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>&lt;</code><code>version</code><code>&gt;0.0.12&lt;/</code><code>version</code><code>&gt;</code>

<code>&lt;/</code><code>dependency</code><code>&gt;</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&lt;fileobject&gt; 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&lt;string, fileobject&gt; fileobjectcachemap =</code><code>new</code> <code>concurrenthashmap&lt;string, fileobject&gt;();</code>

<code> </code><code>static</code> <code>map&lt;string, long&gt; filemodifytimemap =</code><code>new</code> <code>concurrenthashmap&lt;string, long&gt;();</code>

<code> </code><code>static</code> <code>final</code> <code>map&lt;string, schemaprovider&gt; schemaprovidermap =</code><code>new</code> <code>hashmap&lt;string, schemaprovider&gt;();</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。