2006年01月02日

  新的事务模型的目的是逐渐使最终用户能够用标准化的和常见的软件管理、检索和操作存储的多媒体资源–例如相片、视频和行情资料。在利用现有的内部技术来降低成本和产生利润的时候,现有的媒体业务还用来实现访问它们的多媒体资源的标准化值。尽管在过去几年中存储量、处理能力和软件都有重大的发展,但是管理数字媒体资源仍然是一件代价相当高的事情。一些研究表明,大多数的多媒体文件是非结构化的资源;只有很少一部分存在于关系数据库和现有的应用程序中。结构化的缺乏使有效地访问和重新利用数字资源变得非常困难。
  中间件平台–特别是应用程序服务器–总是处理数据资源的操作。在创建多媒体增强应用程序的过程中使用应用服务器好像是对这种技术固有强度的一种自然延伸。然而,和数字资源相关的大小、结构和元数据的基本的差异使你需要采用与J2EE平台创建的关系数据库和已有资源不同的方式来操作。本文将从现在可用的和正在开发这两个角度来探讨创建多媒体应用程序所需要的标准和技术。我还将讨论在存储、索引、访问和检索多媒体资源的过程中J2EE所起到的作用,以期把这个平台的领域扩展到数字资源领域。最后,我还将探讨J2EE平台必须解决的问题,以使用户可以最优化地使用多媒体资源。

  三个特性区分和定义了一个多媒体资源。在多媒体资源和已有的相关数据之间最大的基本差别是媒体文件的大小。虽然压缩技术正在不断地改善,但是复杂的视频或者音频数据流仍然动辄以千兆字节计。虽然现在已经有了存储和管理极大数据流的数字内容管理系统,但是没有用于访问这些保存的资源的标准化应用程序编程接口或者机制。

  还可以从结构上来区分多媒体资源和其他数据。一般来说,你可以把传统资源作为单独的组件来访问和使用。但是多媒体资源可能包含若干个元素,例如视频流、音频流、相关的字幕信息和其他数据集。维护这个结构是数字资源管理系统的一个基本要求。

  最后,多媒体文件通常由二进制信息组成。因此,传统的查询、索引和检索文件的方法不适用于多媒体领域。为了应用程序能够成功地管理、检索并且操作一个多媒体组件,你必须维护数字资源和描述这种资源特征的元数据信息之间的关系。

  诸如JDBC和JCA这样的现有的J2EE平台规范阐明了用于数据访问的协议,你可以遵循这些协议创建一个基于标准方法的程序来检索多媒体资源。新的标准还必须进一步增强定义的J2EE组件模型的多媒体能力。

  获得多媒体和中间件平台之间最佳组合的方法主要在于你如何定义一个用于访问数字资源的存储抽象层。为了保持应用程序移植性,你必须利用或者扩展现有的标准来解决数字媒体存储特性,比如插入、更新或者查询资源。


图⒈定义一个存储抽象

  WebDAV规范是一个对HTTP进行扩展的协议,用于解决数字媒体存储大小、结构和元数据这三个方面(见图1)。它提供了跨 Internet协议的分布式编辑和版本控制的能力,可以和现有的HTTP客户端交互操作。WebDAV被使用在网络存储解决方案和Web服务器、许多编辑工具(包括微软公司出品的Internet Explorer浏览器、Apache Slide客户端、Apple OS X Finder、Microsoft Office、和Adobe应用程序)和大部分的操作系统中。许多解决多媒体存储的内容管理产品支持WebDAV。例如Apache Slide体系机构使用WebDAV作为客户端访问协议。Slide提供一个抽象层,允许对机制类型的选择用于所有它的存储,包括内容和元数据。这把内存存储、数据库存储、基于XML的存储等考虑进去。

  惠普多媒体平台和Apache Slide工程利用WebDAV协议和所提供的关联的客户机和服务器应用程序编程接口来创建数字存储抽象功能。这种解决方案提供一个使用规格化、标准化和简单方式访问多后端内容管理程序的方法。这些平台提供了像WebDAV servlet这样的Web组件让开发者和任何WebDAV服务器接口,把许多WebDAV服务器整合到一个联合内容服务器中,或者创建基于请求信息的自定义解决方案。你可以使用HP WebDAV servlet和可以截取WebDAV请求和在存储和检索操作期间执行数据处理的servlet过滤器同时使用。有用的操作包括元数据和内容的提取、变换或者索引。

  通过利用标准化J2EE组件,你可以创建一个可伸缩和容错的基于中间件的内容管理系统。例如,你可以联合WebDAV servlet、相关的处理过滤器和Apache Slide来生成一个内容服务器,能够存储文件、这些文件附属的元数据属性和基于元数据属性的文件的搜索。这样一个系统在J2EE应用程序体系结构平台上执行,并且可以使用平台的性能、可伸缩性、安全和可移植性等特性。

  客户端的存储器接口还可以利用J2SE和 J2EE这两个版本的属性和设备。因为URL设置被构建进J2SE平台中,你可以在Java虚拟机中安装一个WebDAV协议处理程序来简化到WebDAV内容管理系统的客户接口。J2EE组件可以潜在地利用JCA连接器实现来创建存储企业组件和应用程序。例如HP多媒体平台的WebDAV连接器访问遵从WebDAV协议的服务器作为企业资源:


ConnectionSpec spec;
ConnectionFactory factory;
WebDAVConnection conn;

factory =(ConnectionFactory)ctx.lookup("java:comp/env/webdav/local" );
spec = new WebDAVConnectionSpec("/", "username", "password" );
conn = (WebDAVConnection)connectionFactory.getConnection
( spec );

  发现和访问

  一旦你已经创建一个基于标准Java的机制来用于存储和检索多媒体资源,你需要一个查询和发现存储的文件的方法来创建增强的多媒体应用程序。元数据的关键用途是改善信息的查询和检索。元数据实质上是关于信息的信息;它提供或者访问关于另一个信息资源的信息。在多媒体的上下文中,元数据简化了发现的访问数字内容的过程。

  各种元数据标准分别在某些信息领域解决不同的问题。Dublin Core元数据标准被开发来提供一个描述文档,象HTML文档、PDF文件和图像这样的资源的标准方法。它已经被扩展,现在库、档案和联机内容的发行者使用Dublin Core作为一种通用的元数据标准。

  Dublin Core标准描述了适用于在很宽的资源范围内的描述性元素的小集合。这些元素包括象标题、创建者、主题、日期、格式和语言等属性。即使还没有元数据表达机制被普遍接受,但是Dublin Core项目已经采用了资源描述框架( RDF)。RDF提供了一个描述和交换元数据的方法。这那些框架支持元数据与支持用于语义、语法和结构的标准协定机制的互操作性。RDF不强制用于不同资源描述共同体的语义。取而代之的是,它提供了用于这些团体来根据需要定义新的元数据元素的方法。RDF通常使用XML作为一种元数据交换和处理的机制。XML的使用促进了元数据元素集合之间的互操作性,以及在完全不同的团体之间的扩展名和元数据的再使用。此外,RDF简化了词汇的发布,不仅能使词汇被人阅读而且可以很容易地被应用程序处理。

  列表1:


<?xml version=’1.0′?>
<rdf:RDF xmlns:rdf=’http://www.w3.org/1999/
02/22-rdf-syntax-ns#’
xmlns:rdfs= ‘http://www.w3.org/2000/01/
rdf-schema#’
xmlns:hp=’http://www.hpmiddleware.com/xml/
namespaces/metadata-java-1.0#’>

<rdf:Property rdf:about="http://
www.hpmiddleware.com/rdf/maiden/1.0#name">
<rdfs:isDefinedBy rdf:resource="http://
www.hpmiddleware.com/rdf/maiden/1.0#" />
<hp:datatype>java.lang.String</hp:datatype>
</rdf:Property>
<rdf:Property rdf:about="http://
www.hpmiddleware.com/rdf/maiden/1.0#title">
<rdfs:isDefinedBy rdf:resource="http://
www.hpmiddleware.com/rdf/maiden/1.0#" />
<hp:datatype>java.lang.String</hp:datatype>
</rdf:Property>
</rdf:RDF>


  列表1是描述两个元数据属性的RDF文档示例:name和title。<rdf:Property>节点定义了元数据资源。rdf:about属性标识了元数据资源。(你总是通过一个URI描述一种资源。)<rdfs:isDefinedBy>元素指出定义了相关元数据资源的资源。这两个元素共同描述了元数据资源的语义。在列表1中?maiden/1.0#域名空间定义了name元数据属性。<hp:datatype>元素定义了元数据资源的本地Java类型。在列表1中,name元数据资源的本地Java类型是java.lang.String。

  许多容器模型已经被提出来用于访问各种可能存在的用于描述日益不同的资源的元数据集合。这样一个提议是Warwick Framework。Warwick Framework提出了一个容器体系机构的概念模型,在其中你可以部署多个元数据集合,例如你已经习惯于用于创建企业应用程序来使用地现有的应用程序组件。这个框架的特定实现必须提供一个用于处理容器和它的元数据包的实际方法。

图2、实现Warwick Framework容器


  图2显示了Warwick Framework容器的典型的Java实现。这个框架使用了一种普通的方法描述了元数据集合中的属性之间的关系,这样你就可以部署你可以在不同平台上实现的不同容器的描述。此外,你使用一种本地类型绑定描述(特别是对于一种程序设计语言)来生成实现绑定的代码。一个存储绑定允许在多个元数据集合中的属性来映射到存储的单一值(canonical属性)。存储绑定的一部分定义了所需的代码转换来转换存储中编码的属性值为用于指定的元数据集合的固有的编码。

  发现和访问

  一旦你已经创建一个基于标准Java的机制来用于存储和检索多媒体资源,你需要一个查询和发现存储的文件的方法来创建增强的多媒体应用程序。元数据的关键用途是改善信息的查询和检索。元数据实质上是关于信息的信息;它提供或者访问关于另一个信息资源的信息。在多媒体的上下文中,元数据简化了发现的访问数字内容的过程。

  各种元数据标准分别在某些信息领域解决不同的问题。Dublin Core元数据标准被开发来提供一个描述文档,象HTML文档、PDF文件和图像这样的资源的标准方法。它已经被扩展,现在库、档案和联机内容的发行者使用Dublin Core作为一种通用的元数据标准。

  Dublin Core标准描述了适用于在很宽的资源范围内的描述性元素的小集合。这些元素包括象标题、创建者、主题、日期、格式和语言等属性。即使还没有元数据表达机制被普遍接受,但是Dublin Core项目已经采用了资源描述框架( RDF)。RDF提供了一个描述和交换元数据的方法。这那些框架支持元数据与支持用于语义、语法和结构的标准协定机制的互操作性。RDF不强制用于不同资源描述共同体的语义。取而代之的是,它提供了用于这些团体来根据需要定义新的元数据元素的方法。RDF通常使用XML作为一种元数据交换和处理的机制。XML的使用促进了元数据元素集合之间的互操作性,以及在完全不同的团体之间的扩展名和元数据的再使用。此外,RDF简化了词汇的发布,不仅能使词汇被人阅读而且可以很容易地被应用程序处理。

  列表1:


<?xml version=’1.0′?>
<rdf:RDF xmlns:rdf=’http://www.w3.org/1999/
02/22-rdf-syntax-ns#’
xmlns:rdfs= ‘http://www.w3.org/2000/01/
rdf-schema#’
xmlns:hp=’http://www.hpmiddleware.com/xml/
namespaces/metadata-java-1.0#’>

<rdf:Property rdf:about="http://
www.hpmiddleware.com/rdf/maiden/1.0#name">
<rdfs:isDefinedBy rdf:resource="http://
www.hpmiddleware.com/rdf/maiden/1.0#" />
<hp:datatype>java.lang.String</hp:datatype>
</rdf:Property>
<rdf:Property rdf:about="http://
www.hpmiddleware.com/rdf/maiden/1.0#title">
<rdfs:isDefinedBy rdf:resource="http://
www.hpmiddleware.com/rdf/maiden/1.0#" />
<hp:datatype>java.lang.String</hp:datatype>
</rdf:Property>
</rdf:RDF>


  列表1是描述两个元数据属性的RDF文档示例:name和title。<rdf:Property>节点定义了元数据资源。rdf:about属性标识了元数据资源。(你总是通过一个URI描述一种资源。)<rdfs:isDefinedBy>元素指出定义了相关元数据资源的资源。这两个元素共同描述了元数据资源的语义。在列表1中?maiden/1.0#域名空间定义了name元数据属性。<hp:datatype>元素定义了元数据资源的本地Java类型。在列表1中,name元数据资源的本地Java类型是java.lang.String。

  许多容器模型已经被提出来用于访问各种可能存在的用于描述日益不同的资源的元数据集合。这样一个提议是Warwick Framework。Warwick Framework提出了一个容器体系机构的概念模型,在其中你可以部署多个元数据集合,例如你已经习惯于用于创建企业应用程序来使用地现有的应用程序组件。这个框架的特定实现必须提供一个用于处理容器和它的元数据包的实际方法。

图2、实现Warwick Framework容器


  图2显示了Warwick Framework容器的典型的Java实现。这个框架使用了一种普通的方法描述了元数据集合中的属性之间的关系,这样你就可以部署你可以在不同平台上实现的不同容器的描述。此外,你使用一种本地类型绑定描述(特别是对于一种程序设计语言)来生成实现绑定的代码。一个存储绑定允许在多个元数据集合中的属性来映射到存储的单一值(canonical属性)。存储绑定的一部分定义了所需的代码转换来转换存储中编码的属性值为用于指定的元数据集合的固有的编码。
  一个客户应用程序使用Warwick Framework实现来调用容器自己、保存属性值的对象和部署到容器上的任何元数据集合。一个Java名称和目录接口( JNDI)查找可以获得到容器的引用。一个配置的存储驱动程序管理属性值的持续性。一个低级的存储层提供了元数据属性的本体类型绑定。面向客户端的元数据集合应用编程接口不仅使用合适的本地类型而且使用用于元数据集合的合适的编码来传递属性值。

  列表2:


package metadataset;

import com.hp.mw.richmedia.metadata.*;
import com.hp.mw.richmedia.metadata.spi.TranscodingMetadataProperty;

public interface MaidenAppSample extends
 com.hp.mw.richmedia.metadata.MetadataSet {
  public static final TranscodingMetadataProperty
   NAME_METADATA_PROPERTY =
    new TranscodingMetadataProperty("http://www.hpmiddleware.com/rdf/maiden/1.0#name" );
  public static final TranscodingMetadataProperty
   TITLE_METADATA_PROPERTY =
    new TranscodingMetadataProperty("http://www.hpmiddleware.com/rdf/maiden/1.0#title" );
  public void addName( java.lang.String name ) throws MetadataException;

  public PropertyValueCollection getNameCollection() throws MetadataException;

  public void setName( java.lang.String name ) throws CardinalityConstraintException,
MetadataException;

 public java.lang.String getFirstName() throws MetadataException;

 public void addTitle( java.lang.String title ) throws MetadataException;

 public PropertyValueCollection getTitleCollection() throws MetadataException;

 public void setTitle( java.lang.String title ) throws CardinalityConstraintException,
MetadataException;

 public java.lang.String getFirstTitle() throws MetadataException;
}


  列表2说明了一个Warwick Framework生成的面向客户端的应用编程接口的示例。列表3中的JSP代码说明了你如何通过JNDI查找、检索元数据集合对象和查询用于name元数据属性的值的对象来访问Warwick Framework。

  列表3:


<%@ page ?>

<% Hashtable env = new Hashtable();
env.put( Context.URL_PKG_PREFIXES,
"com.hp.mw.richmedia" );
InitialContext ctx = new InitialContext( env );

MetadataContainer container =
( MetadataContainer ) ctx.lookup(
"metadata:/metadata-container.xml" );

MetadataQuery query = container.createQuery(
new URIQuerySpec( "http://
www.hpmiddleware.com/maiden/1.0/
testresource" ) );
Collection c = container.query( query );
Iterator itor = c.iterator();
MetaObject mo = ( MetaObject ) itor.next();

MaidenAppSample sampleMetadataSet =
( MaidenAppSample )
container.getMetadataSet( mo,
"http://www.hpmiddleware.com/rdf/
maidenapp/sample/1.0#" );
PropertyValueCollection pvc =
sampleMetadataSet.getNameCollection();
out.println( "<br><h3>Values for the name
property?</h3><br>" );
itor = pvc.iterator();
while ( itor.hasNext() )
out.println( ( String ) itor.next() + "<br>" );
%>


  我已经细节说明的存储组件和元数据组件简化了基于标准、多媒体增强的应用程序的创建。这些组件提供了使用你熟悉J2EE开发机制来存储、查询和检索多媒体元素的能力。因此,你不仅可以把现有的和新的J2EE应用程序中的上下文的多媒体内容提供给最终用户,而且可以利用这个平台来创建支持新媒体组件的生成的应用程序。

  例如,你可以通过集合媒体组件到同步多媒体集成语言( Synchronized Multimedia Integration Language,SMIL)来组装一个促进媒体表现得交互创建的基于Web的应用程序。( SMIL是一个提供根据时间线计划音频、视频、文本和图形文件的标注语言。)另一个例子是一个简化提取、审查和最终发布多媒体内容的门户。这两个应用程序可以通过利用标准J2EE组件,例如JSP、servlets和Enterprise Java Beans来在多媒体生命周期中确定关键阶段。


图⒊EMB组件和从属物

  除了利用现有的组件之外,Java开发者团体还在探索如何扩展现有的Java平台的范围来在多媒体资源生成中担任一个更重要的角色。JCP的许多创新能解决现有的Java平台中的一些多媒体处理的不足。
  Enterprise Media Bean

  Enterprise Media Bean ( EMB) Java规范请求解决了多媒体数据的操作和应用。EMB规范工作推出了一个基于组件的体系机构,用于把多媒体数据无缝集成到基于J2EE程序设计模型的应用程序中。(参见图3)。这个规范提出了两个组件类型。第一个组件Media Foundation Bean提供了一些基本的服务(比如头标的提取)来提供一个标准和轻量级方法来在J2EE应用程序中包含基本的多媒体相关特性。相反,第二组件Media Entity Bean依靠EJB实体bean并且因此用于基于EJB体系结构的应用程序。这些Media Entity Bean基本上是持久的、远程的和易变的模型数据的实体bean。因此,它们扩展了标准EJB事务和安全模型到媒体数据中,提供了一个服务器端协议处理程序(例如流服务器)的抽象。

  此外,JCP调查访问内容存储的标准方法。现在,客户应用程序必须使用一个供应商的专有的应用编程接口来与特定的内容容器相互作用。Content Repository for Java Technology应用编程接口将集中于事务读/写访问二进制内容(流操作)、文本内容、全文搜索、过滤、监视、版本控制和处理结构化内容。

  除现有的JCP创新来扩展J2EE平台之外,补充的扩展被要求允许平台在创建多媒体数据的过程中担任更基本的角色。为了J2EE应用程序能够参与图像处理,应该定义辅助容器,允许部署的组件访问当前不可用于EJBs或者servlets的工具。例如,图像过程组件需要大量产生多线程的能力来处理各种并发数据流,并且最终合并结果。一个允许创建这种类型处理组的容器将允许J2EE平台来在多媒体领域中扮演一个更重要的角色。

  开发者现在有与他们现在整合数据库和以前遗留信息所使用的大致相同的方式来寻找和包含多媒体资源的选择。因为这些技术已经成熟并且标准化,用于提高J2EE应用程序使用者的用户经验的潜在能力极大增强。Java团体和中间件供应商必须利用这些潜在能力使J2EE平台成为一个对于更有辨别能力的用户市场来说更可行的选择。

WebDAV(Web-based Distributed Authoring and Versioning)是基于 HTTP 1.1 的一个通信协议。它为 HTTP 1.1 添加了一些扩展(就是在 GET、POST、HEAD 等几个 HTTP 标准方法以外添加了一些新的方法),使得应用程序可以直接将文件写到 Web Server 上,并且在写文件时候可以对文件加锁,写完后对文件解锁,还可以支持对文件所做的版本控制。这个协议的出现极大地增加了 Web 作为一种创作媒体对于我们的价值。基于 WebDAV 可以实现一个功能强大的内容管理系统或者配置管理系统。
我这里不想详细介绍 WebDAV 的协议,感兴趣的可以在这里找到相关的资料:
http://www.webdav.org
其中首先应该看的是这份 WebDAV FAQ:
http://www.webdav.org/other/faq.html

WebDAV 本身是一个类似于 HTTP 的通信协议(IETF RFC 2518)。它与 HTTP 类似,需要实现服务器和客户端两部分软件。目前 WebDAV 已经有了大量相关的软件实现。
在这里是一些与 WebDAV 相关的软件项目:
http://www.webdav.org/projects/

在这些项目中,我们最感兴趣的当然是那些用 Java 实现的开源项目,Slide 是其中最重要的一个项目。Slide 是 Jakarta 项目的一个子项目(又是 Apache 山头的),提供了一套 WebDAV 的服务器端和客户端的开发库和 API,目前已经出到了 2.0 版。
http://jakarta.apache.org/slide/
在这里下载最新的 Slide 2.0 的 Binary 包。
http://jakarta.apache.org/site/binindex.cgi
Slide 分成服务器端和客户端两部分:
服务器端:
http://apache.linuxforum.net/dist/jakarta/slide/binaries/jakarta-slide-server-bin-2.0.zip
客户端:
http://apache.linuxforum.net/dist/jakarta/slide/binaries/jakarta-slide-webdavclient-bin-2.0.zip

我先讲讲服务器端如何配置:
解压缩,假设在 D:\tmp\jakarta-slide-server-2.0 下,你会在
D:\tmp\jakarta-slide-server-2.0\slide\webapp\
下找到两个 war 文件:
slide.war:Slide 服务器端配置,用 Servlet 实现。
slide-doc.war:Slide 文档。

把这两个 war 文件 copy 到你的 Web Container(Tomcat、Jetty、Resin、etc.) 的部署目录(一般是 webapps 目录)下,然后重新启动 Web Container。

在我现在写的这个文档中服务器端的配置就是这么简单。

再讲讲在客户端如何配置。
WebDAV 有非常多的客户端,用 Slide 客户端的库可以非常容易地写出一个 WebDAV 客户端程序。感兴趣的可以看看这篇文档:
http://www.onjava.com/lpt/a/4387

我主要讲讲如何用 Windows 2000/XP 自带的 Web Folder 功能来访问 Web 文件夹。
Windows 2000/XP 安装后已经具备访问基于 WebDAV 协议的 Web 文件夹的功能,而且可以把 Web 文件夹映射为一个本地文件夹,支持拖放、拷贝/粘贴等等功能,使用起来非常方便。
在 Windows 2000/XP 中添加 Web 文件夹的方法是:
打开“网上邻居”,添加网上邻居,在“请键入网上邻居的位置”中输入 Web 文件夹的 URL,例如我刚才用 Slide 配置好的 WebDAV 服务器在:
http://localhost:8000/slide/
然后按照向导的提示继续做就可以了,非常的简单。
配置好了以后你就可以把这个 Web 文件夹当作本地文件夹一样使用了。拖几个文件进去试试吧。关于上述 Web Folder 的配置可以参考这些文档:
http://chapters.marssociety.org/webdav/
(几个闲着没事孜孜不倦地研究人类如何移民火星的酷哥写的文档)
还有 M$ 网站上的相关文档:
http://www.microsoft.com/windowsxp/home/using/productdoc/en/default.asp?url=/windowsxp/home/using/productdoc/en/using_webfolders_for_file_transfer.asp

M$ 的很多产品都内置有 WebDAV 的支持。例如:Office 2000、IE 5/6、Exchange Server、Frontpage。我配置好 WebDAV 服务器后,当我访问这个 URL
http://localhost:8000/slide/files/23.doc
时,Word 2000 可以识别出 Web 服务器支持 WebDAV 协议。于是 Word 2000 可以直接编辑服务器上的这个文档,编辑完后可以直接保存在 Web 服务器上。这个是不是比你习惯的 download->modify->upload 要方便的多?

WebDAV 还有很多话题,比如 WebDAV 完全可以取代 FTP。WebDAV 至少在以下几个方面对 FTP 具有压倒性优势:
1、FTP 需要申请操作系统帐号。WebDAV 不需要申请任何操作系统帐号,它使用一套自己定义的安全完善的身份验证机制。
2、FTP 的所有数据(包括登录信息)全部使用明文传送,加密必须要自己来实现,例如:可以手工用 GPG 来做这件事,但是毕竟还是不方便。用 WebDAV 就可以使用 HTTPS 来传输数据,加密解密的操作完全是在低层自动完成的。
3、FTP 传输数据的传输效率比较低,每传送一个文件需要打开一个新的 TCP 连接,而 WebDAV 传输所有文件只需要一个 TCP 连接。
4、FTP 不象 HTTP 那样容易穿越防火墙,在广域网的应用范围比 HTTP 要小的多。而 WebDAV 因为是基于 HTTP 的,所以具有 HTTP 的所有优点。
5、FTP 客户端工具没有 WebDAV 客户端工具使用方便。你刚才已经看到 WebDAV 服务器配置好后,通过 Windows 2000/XP 的 Web Folder 方式访问 Web 文件夹就和访问本地文件夹没有多少区别。如果应用程序支持 WebDAV 协议(例如 Word 2000),就可以直接打开 Web 文件夹中的文件并且编辑,然后直接保存在原先的 Web 文件夹中。这个用起来简直就和 Samba 完全一样。你知道哪一个 FTP 客户端使用起来有这么方便吗?
 
除了网上邻居以外,在 IE 中也可以直接打开 Web 文件夹。运行 IE,“文件->打开”,选中“以 Web 文件夹方式打开”。然后输入
http://localhost:8000/slide/
这样 IE 就直接打开了这个 Web 文件夹,你可以随便拖几个文件进去试试。如果是 Word 文件可以直接用 Word 打开并编辑,然后可以直接保存在 Web Server 上。这和上面在网上邻居中打开的效果是完全一样的。
你可以写 JS 来直接打开 Web 文件夹中的文件,例如:
<script language="javascript">
<!–
var word = new ActiveXObject("Word.Application");
word.Visible = true;
word.Documents.Open("http://localhost:8000/slide/files/23.doc");
//word.Quit();
–>
</script>
这里只是打开 Word 文件的例子,你当然还可以写出打开其它类型文件的例子。前提是这类文件的应用程序支持 WebDAV 协议并且提供了相应的控件可以被 JS 调用。
Word 打开这个文件后可以直接编辑,然后可以直接保存在 Web Server 上面,省去了你 download->modify->upload 的步骤,用起来是不是更方便?

缺省情况下,WebDAV 服务器在客户端第一次打开一个文件时会为这个文件加一个排他的写锁,以后所有客户端打开这个文件都是只读的。只有在第一个客户端保存文件后才会释放这个锁,然后其他客户端才能修改这个文件。

Slide 可以把文件保存在文件系统中,也可以把文件保存在数据库中。Slide 提供了 API 使你可以写 plugin 将文件保存在其它类型的存储系统中。
Slide 使用基于角色的权限控制,角色的权限可以继承。这些内容聊起来就多了,感兴趣的可以看 Slide 的配置文档。文档中还有与版本控制有关的内容。
http://jakarta.apache.org/slide/config_file.html
设计细颗粒度的持久类并且使用<component>来实现映射。

使用一个Address持久类来封装 street, suburb, state, postcode. 这将有利于代码重用和简化代码重构(refactoring)的工作。

对持久类声明标识符属性。

Hibernate中标识符属性是可选的,不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的(自动生成,不涉及业务含义),并且不是基本类型。为了最大的灵活性,应该使用java.lang.Long or java.lang.String

自然主键标志符

对所有使用自然主键标志符的尸体使用<natural-id>,实现equals()和hashCode()方法来比较树型。

为每个持久类写一个映射文件

不要把所有的持久类映射都写到一个大文件中。把 com.eg.Foo 映射到com/eg/Foo.hbm.xml中, 在团队开发环境中,这一点显得特别有意义。

把映射文件作为资源加载

把映射文件和他们的映射类放在一起进行部署。

考虑把查询字符串放在程序外面

如果你的查询中调用了非ANSI标准的SQL函数,那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。

使用绑定变量

就像在JDBC编程中一样,应该总是用占位符"?"来替换非常量值,不要在查询中用字符串值来构造非常量值!更好的办法是在查询中使用命名参数。

不要自己来管理JDBC connections

Hibernate允许应用程序自己来管理JDBC connections,但是应该作为最后没有办法的办法。如果你不能使用Hibernate内建的connections providers,那么考虑实现自己来实现net.sf.hibernate.connection.ConnectionProvider

考虑使用用户自定义类型(custom type)

net.sf.hibernate.UserType. 假设你有一个Java类型,来自某些类库,需要被持久化,但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现net.sf.hibernate.UserType接口。这种办法使程序代码写起来更加自如,不再需要考虑类与Hibernate type之间的相互转换。

在性能瓶颈的地方使用硬编码的JDBC

在对性能要求很严格的一些系统中,一些操作(例如批量更新和批量删除)也许直接使用JDBC会更好,但是请先搞清楚这是否是一个瓶颈,并且不要想当然认为JDBC一定会更快。如果确实需要直接使用JDBC,那么最好打开一个 Hibernate Session 然后从 Session获得connection,按照这种办法你仍然可以使用同样的transaction策略和底层的connection provider。

理解Session清洗( flushing)

Session会不时的向数据库同步持久化状态,如果这种操作进行的过于频繁,那么性能会受到一定的影响。有时候你可以通过禁止自动flushing尽量最小化非必要的flushing操作,或者更进一步,在一个特殊transaction中改变查询和其它操作的顺序。

在三层架构中,考虑使用Detached对象

当使用servlet / session bean体系结构的时候,需要使用Session读取持久对象,并将值对象传递到servlet / JSP层。为每一个请求使用一个新的Session。使用Session.merge()或者Session.saveOrUpdate()方法来让对象与数据库同步。

在两层架构中,考虑使用长持久上下文

为了提高性能,数据库事务应该尽可能短。然而在实际业务中经常需要实现长时间运行的应用事务,每个用户的每个视图使用一个简单的工作单元。应用程序事务将横跨多个客户请求响应周期。通常情况下,使用Detached对象来实现应用程序事务。在两层体系结构中,通常,比较恰当的方法是在整个应用事务中维护一个单一的打开的持久连接(Session),然后在每个请求结束时简单的关闭掉JDBC连接,然后在并发请求中,重新连接它们。

不要把异常看成可恢复的

这一点甚至比“最佳实践”还要重要,这是“必备常识”。当异常发生的时候,回滚 Transaction ,关闭Session。如果你不这样做的话,Hibernate无法保证内存状态精确的反应持久状态。尤其不要使用Session.load()来判断一个给定标识符的对象实例在数据库中是否存在,应该使用find()。当然,有一些例外情况,比如说StaleObjectStateExceptionObjectNotFoundException

对于关联优先考虑lazy fetching

谨慎的使用主动外连接抓取(eager (outer-join) fetching)。对于大多数没有JVM级别缓存的持久对象的关联,应该使用代理(proxies)或者具有延迟加载属性的集合(lazy collections)。对于被缓存的对象的关联,尤其是缓存的命中率非常高的情况下,应该使用outer-join="false",显式的禁止掉eager fetching。如果那些特殊的确实适合使用outer-join fetch 的场合,请在查询中使用left join

使用Open Session In View模式或者Disciplined Assembly Phase来消除获取数据时产生的问题

Hibernate允许开发冗长的数据传输对象(DTO)。在传统的EJB体系结构中,使用DTOs具有两个目的:第一,他们解决了实体Bean没有实现serializable接口的问题;第二,他们在为表示层提供数据时,DTO对象屏蔽了一些对象间的关联操作。Hibernate排除了第一种情况。然而,仍然需要一个连接阶段(设想业务逻辑严格定义表示层使用的数据对象),否则,在表现层渲染阶段就可能会将持久传递到表现层了。这不是Hibenate的局限性,这是保证事务性数据访问的基础原则。

考虑把Hibernate代码从业务逻辑代码中抽象出来

把Hibernate的数据存取代码隐藏到接口(interface)的后面,组合使用DAOThread Local Session模式。通过Hibernate的UserType,你甚至可以用硬编码的JDBC来持久化那些本该被Hibernate持久化的类。 (该建议更适用于规模足够大应用软件中,对于那些只有5张表的应用程序并不适合。)

不要使用外连接映射 

好的真正的many-to-many连接是非常稀少的。多数时候,需要在链接表中存储附加信息。由于这个原因,使用两个one-to-many连接来代替一个中介的链接类是更明智的选择。事实上,更多的连接情况应该是one-to-many和many-to-one的,应该小心使用其它种类的连接,如果设计中确实出现了其它种类的连接情况,仔细审查一下,以确认他们是必须的。

使用与业务有关的键值来实现equals()hashCode() .

如果你在Session外比较对象,你必须要实现equals()hashCode()。在Session内部,Java的对象识别可以值得信赖。如果你实现了这些方法,不要再使用数据库辨识!瞬时对象不具有标识值,Hibernate会在对象被保存的时候赋予它一个值。如果对象在被保存的时候位于Set内,hash code就会变化,要约就被违背。为了实现用与业务有关的键值编写equals()hashCode(),你应该使用类属性的唯一组合。记住,这个键值只是当对象位于Set内部时才需要保证稳定且唯一,并不是在其整个生命周期中都需要(不需要达到数据库主键这样的稳定性)。绝不要在equals()比较中使用集合(要考虑延迟装载),这些相关联的类可能被代理过。

不要用怪异的连接映射

多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下,用两个指向中介类的一对多的连接比较好。实际上,我们认为绝大多数的连接是一对多和多对一的,你应该谨慎使用其它连接风格,用之前问自己一句,是否真的必须这么做。

2006年01月01日

        除开五大这种要什么有什么,进去做打字也能光耀门楣的不谈。如果是嫁到一个普通软件公司,怎样的环境才能最快的成长呢?
       首先基本的公司项目管理水平是必要的;其次是稳健而不保守的公司技术选型; 还有一班能沟通的同事。

        更重要的,是要有一个严苛的环境,那些古训说的都没错,越是严苛,成长越快。
        然后有些引导与助力,让你在严寒中节省体力,过得好些的,就是最佳的成长环境了。

    一,最复杂多变的业务规则,最难侍候的客户
         大多数人碰上这两样东西都是愁眉苦脸,天天抱怨。其实应该把它视为入门锻炼的:
         第一阶是努力用细密的编码迎合复杂的业务,以金牌服务的态度接受客户的善变和每样需求都要24小时有取的bt。充分打磨过的眼耳口心,才是个合格的IT 人。如果业务规则都清的像水,项目时时都是以我为主的,反是在做toy application,不算入了IT的门。
       打磨之后,如果肯静下心来总结,设计架构如何适应改变,流程如何做到快速更新部署,就是第二阶段的进步了。
       一直做ERP,自问有足够复杂的业务(因为不熟悉业务,有些不复杂的也变复杂了)和麻烦的客户(自己不是大公司,麻烦的客户就会很多),但在第二阶段还是不很静得下心。

    二,最严格的性能要求,准确性要求,错误恢复要求
        最好是那种每天N万人访问,几亿条数据。还有准确性很严格,分分钟几十万上下,如果崩机又要迅速完全恢复状态的项目。
         每天几万人访问的项目没做过,遗憾。
         几千万条数据的项目倒是每天面对,优化学了一点,但没有充分利用严格的环境去追求改进,遗憾。
         ERP的报表倒是经常会搞到几十万上下的差额,但一直重视不足,以后要以历练来对待之。
         崩机恢复是最近的项目要求,更爽是这个项目澳国政府居然有一份规范,我说的助力就是指这些了,不用自己慢慢摸索而且经常摸错地方。

    三,最底层的编码
         不要老是高高在上的搞ERP搞.com,偶然尝试一下用到起码C一级的语言,和硬件打打交道,直接写IO口阿,中断阿,都是有就不要放过的锻炼。
         没做过底层编码,只会纯软件的IT人的感觉是不完整的。
         刚好公司最近的项目有要求,捧一本 〈Linux Device Driver〉看看。

    四,最高层的设计
        设计、框架–技术人一听就眼红的字眼。但不是每个公司都搞很产品化的项目,或者在项目里自写类库重做轮子。所以,除了刚入行的那段疯狂日子,很高阶的设计其实我也没做多少,多数时间都是应用层的开发。

        虽然自己不写,但却经常都要进行框架选型,也算是蒸发智力的一个地方。Promatic Programmer里说的critical的态度很重要,Spring好么?一定要想清楚它好在哪里,不要糊里糊涂的看着quick start就入局了,浑忘了如果不用Spring的世界是怎样的。

       另外,因为有时候要开源软件有bug,或者要增强它来吻合项目需求,或者有错了不明就里时直接读源码会更快找到错误。这样就接触到不少优秀开源项目的源码,比如Spring,无形中也会学到他的设计。
        
       如果说Java盛产框架型的项目,php的项目则多是熟透了的Web应用。最近发现php不是以前的php了,应该算入python,ruby一类的动态OO语言,它的那些著名项目一样有着很好的MVC框架。细看之下学到应用设计方面的不少东西。
       
             
     一阵发呆打了这篇字,其实也是提醒自己,目前的环境可以,应该以更好的心态接受更严苛的历练,那些古训说的都没错。

        今天是未来老丈母娘生日,买了生日蛋糕去祝贺。中午陪老丈母娘一家在外面饭店吃饭,可怎么吃也吃不顺口,就是想吃GF给我做的饭菜,别的什么东西也不想吃。可是,将来万一哪天惹GF生气,她不管我饭吃喽,我不得饿死啊。女人,就是比男人“狠毒”得多,这种“下三赖”的招数也能整出来,郁闷,再不敢惹她了。       

2005年12月31日

窗口
       ImageJ窗口包含一个菜单栏、工具栏、状态栏和一个处理栏。图形、柱状图等显示在一个附加的窗口中。测量结果显示在“结果”窗口中。窗口可以被拖拽并调整大小。

工具栏


        工具栏包含快捷按钮,如放大、浏览图像,改变图像颜色等,点击快捷按钮时,这个按钮所代表的功能描述将显示在状态栏中。

状态栏

        状态栏,在鼠标滑过图像时显示像素位置和像素值,在运行滤镜后,它还会显示耗时和处理速度(像素/秒)等。

进程栏

        进程栏,它显示在状态栏中,它能够显示耗时操作的当前进程,不过,它显示的仅仅是一个近似的结果,并不十分精确。

图形

        ImageJ允许在桌面上一次显示多个图像,当前的活动窗口的标题栏为活动状态。所有操作将被处理与活动窗口中的图像。ImageJ支持8位、16位和32位真彩色图像和8位、16位、32位彩色图像。8位图像以0到255的无符号整型数字表示;16位图像以0到65535的无符号整形数字表示;32位真彩色图像使用浮点数表示。

        16位和32位真彩色图像并不能直接显示在计算机显示器上,通常他们只能显示256灰度,因此,数据需要在窗口上映射为8位位图。窗口定义显示的灰度范围值:在窗口的显示范围以下的灰度值被定义为黑色,在窗口显示范围以上的灰度值被定义为白色。窗口所能显示的最小、最大灰度值可以在Image/Adjust/Brightness/Contrast处定义。

缩放

        ImageJ能够在单一窗口内显示多空间/时间纬度的图像。这些图像由缩放栏调用。这些缩放栏中的图像被命名为切片。所有在缩放栏中的切片一定具有相同的大小和位数。缩放栏提供贯穿所有切片的预览能力,Image必须处理所有在缩放栏中的切片。

        ImageJ使用堆栈打开多个TIFF文件,并将他们保存为多图像TIFF。文件获取处理命令打开其他的多图像,非压缩文件。文件导入命令打开一个文件夹内的所有文件。需要创建一个新的堆栈,只需要选择文件菜单中的新建命令。

颜色表

        对比度图像使用颜色表显示,它每一种描述256种可能的显示像素值。使用图像/调色板菜单可以酸则调色板功能。

窗口
       ImageJ窗口包含一个菜单栏、工具栏、状态栏和一个处理栏。图形、柱状图等显示在一个附加的窗口中。测量结果显示在“结果”窗口中。窗口可以被拖拽并调整大小。

工具栏


        工具栏包含快捷按钮,如放大、浏览图像,改变图像颜色等,点击快捷按钮时,这个按钮所代表的功能描述将显示在状态栏中。

状态栏

        状态栏,在鼠标滑过图像时显示像素位置和像素值,在运行滤镜后,它还会显示耗时和处理速度(像素/秒)等。

进程栏

        进程栏,它显示在状态栏中,它能够显示耗时操作的当前进程,不过,它显示的仅仅是一个近似的结果,并不十分精确。

图形

        ImageJ允许在桌面上一次显示多个图像,当前的活动窗口的标题栏为活动状态。所有操作将被处理与活动窗口中的图像。ImageJ支持8位、16位和32位真彩色图像和8位、16位、32位彩色图像。8位图像以0到255的无符号整型数字表示;16位图像以0到65535的无符号整形数字表示;32位真彩色图像使用浮点数表示。

        16位和32位真彩色图像并不能直接显示在计算机显示器上,通常他们只能显示256灰度,因此,数据需要在窗口上映射为8位位图。窗口定义显示的灰度范围值:在窗口的显示范围以下的灰度值被定义为黑色,在窗口显示范围以上的灰度值被定义为白色。窗口所能显示的最小、最大灰度值可以在Image/Adjust/Brightness/Contrast处定义。

缩放

        ImageJ能够在单一窗口内显示多空间/时间纬度的图像。这些图像由缩放栏调用。这些缩放栏中的图像被命名为切片。所有在缩放栏中的切片一定具有相同的大小和位数。缩放栏提供贯穿所有切片的预览能力,Image必须处理所有在缩放栏中的切片。

        ImageJ使用堆栈打开多个TIFF文件,并将他们保存为多图像TIFF。文件获取处理命令打开其他的多图像,非压缩文件。文件导入命令打开一个文件夹内的所有文件。需要创建一个新的堆栈,只需要选择文件菜单中的新建命令。

颜色表

        对比度图像使用颜色表显示,它每一种描述256种可能的显示像素值。使用图像/调色板菜单可以酸则调色板功能。

        ImageJ是一个开放的Java图像处理程序,它的开发灵感来自Macintosh的NIH Image项目。它能够运行在Applet和应用程序中,需要的运行环境为JDK1.1(或更高版本)。

        它能够显示、编辑、分析、处理保存和打印8位、16位和32位图像。它能够读取多种图像格式,包括:TIFF、GIF、JPEG、BMP、DICOM、FITS和"raw"。它支持堆栈–一系列图像共享一个窗口。它是线程安全的,一些耗时的操作,比如读取大图像的时候可以和其它操作同时完成。

        ImageJ能够进行用户自定义选择的区域计算和像素统计。ImageJ可以测量距离和角度;ImageJ可以创建柱状图和线型分析图,它支持标注图像处理方法,例如对比度处理、锐华、滤镜、钝化等等

        ImageJ还能完成几何换算,例如缩放、旋转和倒转等,Image可以将推行昂放大为32:1的比例,也可以将图像缩小为1:32的比例,在放大任何倍数时,所有分析和处理功能仍可正常使用。程序同时支持任何数量的窗口(图像),只要机器的内存够用就行了。

        最小的实际空间测量精度为毫米,密度和灰度测量精度也是可用的。

        ImageJ通过Java插件被设计为开放体系结构的,可以获取定制的分析、处理插件,这些插件都能够使用ImageJ内置的编辑器和编译器。用户自定义插件可以解决几乎所有图像处理和分析方面的问题。

2005年12月30日
public interface FileObject

表现一个文件,它可以用来访问文件内容和文件的结构

Files are arranged in a hierarchy. Each hierachy forms a file system. A file system represents things like a local OS file system, a windows share, an HTTP server, or the contents of a Zip file.

There are two types of files: Folders, which contain other files, and normal files, which contain data, or content. A folder may not have any content, and a normal file cannot contain other files.

File Naming

TODO – write this.

Reading and Writing a File

Reading and writing a file, and all other operations on the file’s content, is done using the FileContent object returned by getContent().

Creating and Deleting a File

A file is created using either createFolder(), createFile(), or by writing to the file using one of the FileContent methods.

A file is deleted using delete(). Recursive deletion can be done using delete(FileSelector).

Finding Files

Other files in the same file system as this file can be found using:

To find files in another file system, use a FileSystemManager.

Version:
$Revision: 170205 $ $Date: 2005-05-15 10:21:59 +0200 (So, 15 Mai 2005) $
Author:
Adam Murdoch
See Also:
FileSystemManager, FileContent, FileName


Method Summary
 boolean canRenameTo(FileObject newfile)
          Queries the file if it is possible to rename it to newfile.
 void close()
          Closes this file, and its content.
 void copyFrom(FileObject srcFile, FileSelector selector)
          Copies another file, and all its descendents, to this file.
 void createFile()
          Creates this file, if it does not exist.
 void createFolder()
          Creates this folder, if it does not exist.
 boolean delete()
          Deletes this file.
 int delete(FileSelector selector)
          Deletes all descendents of this file that match a selector.
 boolean exists()
          Determines if this file exists.
 FileObject[] findFiles(FileSelector selector)
          Finds the set of matching descendents of this file, in depthwise order.
 void findFiles(FileSelector selector, boolean depthwise, java.util.List selected)
          Finds the set of matching descendents of this file.
 FileObject getChild(java.lang.String name)
          Returns a child of this file.
 FileObject[] getChildren()
          Lists the children of this file.
 FileContent getContent()
          Returns this file’s content.
 FileSystem getFileSystem()
          Returns the file system that contains this file.
 FileName getName()
          Returns the name of this file.
 FileObject getParent()
          Returns the folder that contains this file.
 FileType getType()
          Returns this file’s type.
 java.net.URL getURL()
          Returns a URL representing this file.
 boolean isHidden()
          Determines if this file is hidden.
 boolean isReadable()
          Determines if this file can be read.
 boolean isWriteable()
          Determines if this file can be written to.
 void moveTo(FileObject destFile)
          Move this file.
 FileObject resolveFile(java.lang.String path)
          Finds a file, relative to this file.
 FileObject resolveFile(java.lang.String name, NameScope scope)
          Finds a file, relative to this file.

 


Method Detail

getName

FileName getName()
Returns the name of this file.



getURL

java.net.URL getURL()
                    throws FileSystemException
Returns a URL representing this file.

Throws:
FileSystemException


exists

boolean exists()
               throws FileSystemException
Determines if this file exists.

Returns:
true if this file exists, false if not.
Throws:
FileSystemException – On error determining if this file exists.


isHidden

boolean isHidden()
                 throws FileSystemException
Determines if this file is hidden.

Returns:
true if this file is hidden, false if not.
Throws:
FileSystemException – On error determining if this file exists.


isReadable

boolean isReadable()
                   throws FileSystemException
Determines if this file can be read.

Returns:
true if this file is readable, false if not.
Throws:
FileSystemException – On error determining if this file exists.


isWriteable

boolean isWriteable()
                    throws FileSystemException
Determines if this file can be written to.

Returns:
true if this file is writeable, false if not.
Throws:
FileSystemException – On error determining if this file exists.


getType

FileType getType()
                 throws FileSystemException
Returns this file’s type.

Returns:
One of the FileType constants. Never returns null.
Throws:
FileSystemException – On error determining the file’s type.


getParent

FileObject getParent()
                     throws FileSystemException
Returns the folder that contains this file.

Returns:
The folder that contains this file. Returns null if this file is the root of a file system.
Throws:
FileSystemException – On error finding the file’s parent.


getFileSystem

FileSystem getFileSystem()
Returns the file system that contains this file.

Returns:
The file system.


getChildren

FileObject[] getChildren()
                         throws FileSystemException
Lists the children of this file.

Returns:
An array containing the children of this file. The array is unordered. If the file does not have any children, a zero-length array is returned. This method never returns null.
Throws:
FileSystemException – If this file does not exist, or is not a folder, or on error listing this file’s children.


getChild

FileObject getChild(java.lang.String name)
                    throws FileSystemException
Returns a child of this file. Note that this method returns null when the child does not exist. This differs from resolveFile( String, NameScope) which never returns null.

Parameters:
name – The name of the child.
Returns:
The child, or null if there is no such child.
Throws:
FileSystemException – If this file does not exist, or is not a folder, or on error determining this file’s children.


resolveFile

FileObject resolveFile(java.lang.String name,
                       NameScope scope)
                       throws FileSystemException
Finds a file, relative to this file. Refer to NameScope for a description of how names are resolved in the different scopes.

Parameters:
name – The name to resolve.
Returns:
The file.
Throws:
FileSystemException – On error parsing the path, or on error finding the file.


resolveFile

FileObject resolveFile(java.lang.String path)
                       throws FileSystemException
Finds a file, relative to this file. Equivalent to calling resolveFile( path, NameScope.FILE_SYSTEM ).

Parameters:
path – The path of the file to locate. Can either be a relative path or an absolute path.
Returns:
The file.
Throws:
FileSystemException – On error parsing the path, or on error finding the file.


findFiles

FileObject[] findFiles(FileSelector selector)
                       throws FileSystemException
Finds the set of matching descendents of this file, in depthwise order.

Parameters:
selector – The selector to use to select matching files.
Returns:
The matching files. The files are returned in depthwise order (that is, a child appears in the list before its parent).
Throws:
FileSystemException


findFiles

void findFiles(FileSelector selector,
               boolean depthwise,
               java.util.List selected)
               throws FileSystemException
Finds the set of matching descendents of this file.

Parameters:
selector – the selector used to determine if the file should be selected
depthwise – controls the ordering in the list. e.g. deepest first
selected – container for selected files. list needs not to be empty.
Throws:
FileSystemException


delete

boolean delete()
               throws FileSystemException
Deletes this file. Does nothing if this file does not exist of if it is a folder that has children. Does not delete any descendents of this file, use delete(FileSelector) for that.

Returns:
true if this object has been deleted
Throws:
FileSystemException – If this file is a non-empty folder, or if this file is read-only, or on error deleteing this file.


delete

int delete(FileSelector selector)
           throws FileSystemException
Deletes all descendents of this file that match a selector. Does nothing if this file does not exist.

This method is not transactional. If it fails and throws an exception, this file will potentially only be partially deleted.

Parameters:
selector – The selector to use to select which files to delete.
Returns:
the number of deleted objects
Throws:
FileSystemException – If this file or one of its descendents is read-only, or on error deleting this file or one of its descendents.


createFolder

void createFolder()
                  throws FileSystemException
Creates this folder, if it does not exist. Also creates any ancestor folders which do not exist. This method does nothing if the folder already exists.

Throws:
FileSystemException – If the folder already exists with the wrong type, or the parent folder is read-only, or on error creating this folder or one of its ancestors.


createFile

void createFile()
                throws FileSystemException
Creates this file, if it does not exist. Also creates any ancestor folders which do not exist. This method does nothing if the file already exists and is a file.

Throws:
FileSystemException – If the file already exists with the wrong type, or the parent folder is read-only, or on error creating this file or one of its ancestors.


copyFrom

void copyFrom(FileObject srcFile,
              FileSelector selector)
              throws FileSystemException
Copies another file, and all its descendents, to this file.

If this file does not exist, it is created. Its parent folder is also created, if necessary. If this file does exist, it is deleted first.

This method is not transactional. If it fails and throws an exception, this file will potentially only be partially copied.

Parameters:
srcFile – The source file to copy.
selector – The selector to use to select which files to copy.
Throws:
FileSystemException – If this file is read-only, or if the source file does not exist, or on error copying the file.


moveTo

void moveTo(FileObject destFile)
            throws FileSystemException
Move this file.

Parameters:
destFile – the New filename.
Throws:
FileSystemException – If this file is read-only, or if the source file does not exist, or on error copying the file.


canRenameTo

boolean canRenameTo(FileObject newfile)
Queries the file if it is possible to rename it to newfile.

Parameters:
newfile – the new file(-name)
Returns:
true it this is the case


getContent

FileContent getContent()
                       throws FileSystemException
Returns this file’s content. The FileContent returned by this method can be used to read and write the content of the file.

This method can be called if the file does not exist, and the returned FileContent can be used to create the file by writing its content.

Returns:
This file’s content.
Throws:
FileSystemException – On error getting this file’s content.


close

void close()
           throws FileSystemException
Closes this file, and its content. This method is a hint to the implementation that it can release any resources asociated with the file.

The file object can continue to be used after this method is called.

Throws:
FileSystemException – On error closing the file.
See Also:
FileContent.close()

        清单8是SearchBuddy类的代码片断,它示范了如何为虚拟文件系统装配装配不同的文件类型:
 
清单8装配Zip文件到虚拟文件系统中:
/**
 * Mount a zip file to the searchable virtual
 * file system.
 *
 * @param pathToFolder Absolute or relative path to the zip file.
 * @throws SearchException Error while adding the zip file to the virtual file system.
 */
public void addSearchableZip(String pathToZip) throws SearchException {
 File zipFile = new File(pathToZip);
 if(!zipFile.exists()) {
  throw new SearchException("Invalid zip file path");
 }
 try {
  FileObject zipFileObject = defFileSysMgr.toFileObject(zipFile);
  searchableVfs.addJunction("/" + zipFile,
                      defFileSysMgr.resolveFile("zip:" + zipFileObject + "!/"));
 } catch (FileSystemException e) {
  throw new SearchException("Unable to add zip file to the virtual file system", e);
 }
}
 
        zip文件通过调用VirtualFileSystem类中的addJunction方法进行装配操作。连接 (或装配点)缓存在装饰类SearchableVirtualFileSystem中,它将被使用在整个查询操作中。

清单9提供的代码段提供查询已经装配好的文件系统的功能。
 
清单9,查询操作
Class SearchBuddy
/**
 * Delegate the search call to the Searchable virtual file
 * system decorator.
 *
 * @param fileNamePart Name of the file.
 * @param extension Extension to search
 * @throws SearchException Error from the VirtualFileSystem when searching for file.
 */
public void search(String fileNamePart, String extension) throws SearchException {
 searchableVfs.search(fileNamePart, extension);
}
 
Class SearchableVirtualFileSystem
/**
 * Iterate the junctions to search for the given file name.
 *
 * @param fileNamePart File name to search
 * @param extension Extension to search
 * @throws SearchException
 */
public void search(String fileNamePart, String extension) throws SearchException {
 try {
  Iterator<String> searchPoints = junctions.iterator();
  FileObject matchingFiles[];
  while(searchPoints.hasNext()) {
   String searchPoint = searchPoints.next();
   FileObject searchRoot = vfs.resolveFile(searchPoint);
   filter.setExtension(extension);
   filter.setFileNamePart(fileNamePart);
   matchingFiles = searchRoot.findFiles(filter);
   for(FileObject file : matchingFiles) {
    System.out.println("Result:" + file);
   }
  }
 } catch (FileSystemException e) {
  throw new SearchException("Search failed", e);
 }
}
 
        查询方法迭代已装配好的文件系统,确定根路径,调用FileObject类中的findFiles方法进行查询过滤。查询结果将打印在控制台输出重。装饰类SearchableVirtualFileSystem的首要职责为记忆已装配的那些文件系统。其他方法调用均委托给了VirtualFileSystem实例包装器进行。

       范例应用程序的源代码文件包含了足够数量的文档来解释代码。

        在桌面和服务器应用程序中使用Common VFS框架能够完整地隔离目标应用和文件类型细节之间的代码,可以增强应用程序的模块性和可维护可扩展性。