2006年5月23日

Mondrian是一个开放源代码的Rolap服务器,使用java开发的。它实现了xmla和jolap规范,而且自定义了一种使用mdx语言的客户端接口。Mondrian是olap服务器,而不是数据仓库服务器,因此Mondrian的元数据主要包括olap建模的元数据,不包括从外部数据源到数据库转换的元数据。也就是说Mondria的元数据仅仅包括了多维逻辑模型,从关系型数据库到多维逻辑模型的映射,存取权限等信息。在功能上,Mondrian支持共享维和成员计算,支持星型模型和雪花模型的功能。
Mondrian中使用物理的xml文件存储元数据,它的设计者规定了xml文件的格式。下面简单介绍一下它是如何存储元数据的。

Element Description
根元素
<Schema> Collection of Cubes, Virtual cubes, Shared dimensions, and Roles.
逻辑元素
<Cube> A collection of dimensions and measures, all centered on a fact table.
<VirtualCube> A cube defined by combining the dimensions and measures of one or more cubes.
<Dimension>
<DimensionUsage> Usage of a shared dimension by a cube.
<Hierarchy>
<Level>
<Property>
<Measure>
物理元素
<Table> Fact- or dimension table.
<View> Defines a 'table' using a SQL query, which can have different variants for different underlying databases.
<Join> Defines a 'table' by joining a set of queries.
存取控制
<Role> An access-control profile.
<SchemaGrant> A set of rights to a schema.
<CubeGrant> A set of rights to a cube.
<HierarchyGrant> A set of rights to a hierarchy and levels within that hierarchy.
<MemberGrant> A set of rights to a member and its children.
其他
<Parameter>
<Table>
<Table>



一个模式定义一个多维数据库,它包括一个逻辑模型,由立方体,层次,成员和逻辑模型到物理模型的映射构成。一个逻辑模型由可以用MDX语言来查询。Mondrain的模型由xml文件来描述。现在创建模式的唯一途径是用文本编辑a器编辑xml文件。Xml的语法不是太复杂,因此没有想象中的那么难。目前正在开发一个图形界面的程序来创建和修改模式。
一个模式最重要的组成部分是立方体,度量和维:在一个主题域中立方体是维和度量的集合。一个度量是一个可测量的数值,比如产品销售的数量或者详细清单的价格
一个维是一个属性或者是属性的集合, 通过维你可以将度量划分到字类中。比如:你希望将销售产品按颜色,顾客性别,售出的商店分为八个部分,那么颜色,性别,商店都是维。

下面是一个简单的模型定义的例子:
<Schema>
<Cube name="Sales">
<Table name="sales_fact_1997"/>
<Dimension name="Gender" foreignKey="customer_id">
<Hierarchy hasAll="true" allMemberName="All Genders" primaryKey="customer_id">
<Table name="customer"/>
<Level name="Gender" column="gender" uniqueMembers="true"/>
</Hierarchy>
</Dimension>
<Dimension name="Time" foreignKey="time_id">
<Hierarchy hasAll="false" primaryKey="time_id">
<Table name="time_by_day"/>
<Level name="Year" column="the_year" type="Numeric"
uniqueMembers="true"/>
<Level name="Quarter" column="quarter"
uniqueMembers="false"/>
<Level name="Month" column="month_of_year" type="Numeric"
uniqueMembers="false"/>
</Hierarchy>
</Dimension>
<Measure name="Unit Sales" column="unit_sales"
aggregator="sum" formatString="#,###"/>
<Measure name="Store Sales" column="store_sales"
aggregator="sum" formatString="#,###.##"/>
</Cube>
</Schema>

这个模型包含了一个销售cube,这个cube有两个维,时间和性别维;两个度量,销售数量和销售总额。
我们可以在这个模型上写一个 MDX 查询:
select {[Measures].[Unit Sales], [Measures].[Store Sales]} on columns,
{[Time].[1997].[Q1].descendants} on rows
from [Sales]
where [Gender].[F]
这 个查询涉及到了销售立方体, 每一个维 [Measures], [Time], [Gender], 这些维的多个成员. 结果如下:
[Time] [Measures].[Unit Sales] [Measures].[Store Sales]
[1997].[Q1] 0 0
[1997].[Q1].[Jan] 0 0
[1997].[Q1].[Feb] 0 0
[1997].[Q1].[Mar] 0 0

下面详细地介绍一下模式定义:
一个立方体是一个或者多个维和度量的集合,通常是一个事实表,这里是 ‘sales_fact_1997". 事实表保存了需要计算的列和包含维的参考表.
<Cube name="Sales">
<Table name="sales_fact_1997"/>
...
</Cube>
这里用 <Table> 元素定义事实表. 如果事实表 不在默认的模式中, 你可以用"schema"属性指定一个明确地模式,例如:
<Table schema="foodmart" name="sales_fact_1997"/>
你也可以利用 <View> 和 <Join> 结构来创建更复杂的sql .
度量
销售立方体定义了两个维 "Unit Sales" 和 "Store Sales".
<Measure name="Unit Sales" column="unit_sales"
aggregator="sum" formatString="#,###"/>
<Measure name="Store Sales" column="store_sales"
aggregator="sum" formatString="#,###.00"/>
每个度量有一个名字,对应事实表中的一列, 采用一个聚集函数 (usually "sum").
一个可选的格式字符串指定了值如何被打印. 这里我们选择销售数量不带小数的输出(因为销售数量是整数) ,销售总额带2位小数 . 符号',' 和 '.' 是对地区敏感的, 因此如果是在意大利运行, 销售总额可能会出现 "48.123,45". 你可以用 advanced format strings来实现更严格的效果.度量值不是从列中来的,而是从立方体的单元中来的

性别维由单一的层次组成,仅有一层。
<Dimension name="Gender" foreignKey="customer_id">
<Hierarchy hasAll="true" primaryKey="customer_id">
<Table name="customer"/>
<Level name="Gender" column="gender" uniqueMembers="true"/>
</Hierarchy>
</Dimension>
对于任意给定的销售, 性别维是指购买改产品的客户的性别. 它通过连接事实表"sales_fact_1997.customer_id"和维表"customer.customer_id"
来表示 。"gender" 包括两个值, 'F' 和 'M', 因此性别维包含的成员: [Gender].[F] and [Gender].[M]. 因为 hasAll="true", 系统产生一个特别的 'all' 层, 仅包括一个成员 [All Genders].
一个维可以包含多个层次:
<Dimension name="Time" foreignKey="time_id">
<Hierarchy hasAll="false" primaryKey="time_id">
<Table name="time_by_day"/>
<Level name="Year" column="the_year" type="Numeric"
uniqueMembers="true"/>
<Level name="Quarter" column="quarter"
uniqueMembers="false"/>
<Level name="Month" column="month_of_year" type="Numeric"
uniqueMembers="false"/>
</Hierarchy>
<Hierarchy name="Time Weekly" hasAll="false" primaryKey="time_id">
<Table name="time_by_week"/>
<Level name="Year" column="the_year" type="Numeric"
uniqueMembers="true"/>
<Level name="Week" column="week"
uniqueMembers="false"/>
<Level name="Day" column="day_of_week" type="String"
uniqueMembers="false"/>
</Hierarchy>
</Dimension>
第一个层次没有指定名称.缺省的情况下,一个层次拥有和它的维相同的名称。,因此第一个层次成为"Time".这些层次没有太多的共同之处,他们甚至没有相同的表,除非它们连接了实施表中的同一列"time_id"。在一个维上存在两个层次的原因是这样对最终用户是有用的. 如果一个维上存在两个层次, MDX会强制不允许在一个查询中同时用到他们.
A dimension can live in the fact table:
<Cube name="Sales">
<Table name="sales_fact_1997"/>
...
<Dimension name="Payment method">
<Hierarchy hasAll="true">
<Level name="Payment method" column="payment_method" uniqueMembers="true"/>
</Hierarchy>
</Dimension>
</Cube>
每个维包含有多层组成的一个层次,

大多数维都是仅有一个层次,但有时候一个维有多个层次。比如:你可能希望在时间维上从天聚集到月,季度和年;或者从天聚集到周和年。这两种层次都是从天到年,但是聚集的路径不同。大多数层次有全成员,全成员包括层次的所有成员,因此能够代表他们的总合。它通常命名为'All something',比如:'All stores'.




星型模式和雪花模式
mondrian支持星型模式和雪花模式。下面介绍一下雪花模式的建模,它需要用到操作符 <Join>.比如:
<Cube name="Sales">
...
<Dimension name="Product" foreignKey="product_id">
<Hierarchy hasAll="true" primaryKey="product_id" primaryKeyTable="product">
<Join leftKey="product_class_id" rightAlias="product_class" rightKey="product_class_id">
<Table name="product"/>
<Join leftKey="product_type_id" rightKey="product_type_id">
<Table name="product_class"/>
<Table name="product_type"/>
</Join>
</Join>
...
</Hierarchy>
</Dimension>
</Cube>
这里定义一个 "Product" 维 由三个表构成. 事实表连接 表"product" (通过外键 "product_id"),表"product"连接表"product_class" (通过外键 "product_class_id"),表"product_class"连接表 "product_type" (通过外键 "product_type_id"). 我们利用 <Join> 元素的循环嵌套, <Join>带有两个操作对象; 操作对象可能是表,连接或者查询 。
按照操作对象行的数目来安排次序,表 "product" 的行数最大, 因此它首先出现连接事实表;然后是表 "product_class"和 "product_type",在雪花的末端拥有的行数最小.
注意外部元素 <Join>有一个属性 rightAlias. 这是必要的,因为join 的右边(是内部元素 <Join> ) 有可能是许多表组成的.这种情况下不需要属性leftAlias,因为列 leftKey 很明确的来自表 "product".

共享维
当为一个连接生成SQL的时候, mondrian 需要知道连接哪一个列. 如果一正在连接一个多表连接, 你需要告诉它连接这些表里的哪一个表,哪一个列.
因为共享维不属于一个cube,你必须给它们一个明确的表 (或者数据源). 当你在一个特别的cube里用他们的时候, 你要指定外键 foreign key. 下面的例子显示了 Store Type 维被 连接到 Sales cube ,用了外键 sales_fact_1997.store_id, 并且被连接到Warehouse cube ,用了外键 warehouse.warehouse_store_id :
<Dimension name="Store Type">
<Hierarchy hasAll="true" primaryKey="store_id">
<Table name="store"/>
<Level name="Store Type" column="store_type" uniqueMembers="true"/>
</Hierarchy>
</Dimension>

<Cube name="Sales">
<Table name="sales_fact_1997"/>
...
<DimensionUsage name="Store Type" source="Store Type" foreignKey="store_id"/>
</Cube>

<Cube name="Warehouse">
<Table name="warehouse"/>
...
<DimensionUsage name="Store Type" source="Store Type" foreignKey="warehouse_store_id"/>
</Cube>




虚拟 cubes
父子层次
一个使用方便的层次 有一个严格的层的集合, 成员与层紧密的联系.比如,在 Product 层次中, 任何产品名称层的成员在商标层上都有一个父亲 ,商标层上的成员在产品子目录层也都有一个父亲. 这种结构对于现实世界中的数据有时候太严格了.
一个父子层次只有一层 (不计算 'all' 层), 但是任何成员可以在同一层上有父亲成员. 一个典型的例子是Employees 层次:
<Dimension name="Employees" foreignKey="employee_id">
<Hierarchy hasAll="true" allMemberName="All Employees" primaryKey="employee_id">
<Table name="employee"/>
<Level name="Employee Id" uniqueMembers="true" type="Numeric"
column="employee_id" nameColumn="full_name"
parentColumn="supervisor_id" nullParentValue="0">
<Property name="Marital Status" column="marital_status"/>
<Property name="Position Title" column="position_title"/>
<Property name="Gender" column="gender"/>
<Property name="Salary" column="salary"/>
<Property name="Education Level" column="education_level"/>
<Property name="Management Role" column="management_role"/>
</Level>
</Hierarchy>
</Dimension>
这里parentColumn 和nullParentValue是重要的属性:
属性parentColumn 是一个成员连接到它父亲成员的列名。在这种情况下, 它是指向雇员经理的外键。元素<Level>的子元素 <ParentExpression> 是与属性 parentColumn 有相同作用的,但是元素允许定义任意的SQL表达式, 就像元素 <Expression>. 属性 parentColumn (或者 元素<ParentExpression>) 是维一向Mondrian指出 层次有父子结构的。
属性 nullParentValue 是指明成员没有父成员的值 。 缺省情况下 nullParentValue="null", 但是因为许多数据库不支持null, 建模时 用其他值来代替空值,0和-1.

物理结构
member reade
member reader 是访问成员的方法. 层次通常以维表为基础建立的 , 因此要用sql来构造.但是甚至你的数据没有存在于 RDBMS, 你可以通过一个 Java 类来访问层次。(自定义 member reader)
Here are a couple of examples:
DateSource (to be written)生成一个时间层次. 按常规,数据仓库工具生成一个表 ,每天包含一行。但是问题是这个表需要装载,并且随着时间的变化能够添加更多的行。 DateSource 在内存中按照要求生成日期成员.
FileSystemSource (to be written) 按照目录和文件的层次描述文件系统。 Like the time hierarchy created by DateSource, this is a virtual hierarchy: the member for a particular file is only created when, and if, that file's parent directory is expanded.
ExpressionMemberReader (to be written) 创建了一个基于表达式的层次。
自定义member reader 必须实现接口 mondrian.rolap.MemberSource. 如果你需要实现一个更大的成员操作集合, 需要实现接口 interface mondrian.rolap.MemberReader; 否则, Mondrian在 mondrian.rolap.CacheMemberReader中封装 你的 reader类.你的 member reader 必须有一个公共的构造函数,这个构造函数拥有参数(Hierarchy,Properties),抛出未检查的错误.
Member readers 用 元素<Hierarchy> 的属性memberReaderClass来声明; 任何 <Parameter> 子元素通过属性构造函数来传递.
这是一个例子:
<Dimension name="Has bought dairy">
<Hierarchy hasAll="true" memberReaderClass="mondrian.rolap.HasBoughtDairySource">
<Level name="Has bought dairy" uniqueMembers="true"/>
<Parameter name="expression" value="not used"/>
</Hierarchy>
</Dimension>
Cell readers
<Measure name="name" cellReaderClass="com.foo.MyCellReader">
类 "com.foo.MyCellReader" 实现了接口interface mondrian.olap.CellReader.


存取控制
可以定义存取控制的属性(角色), 作为模式的一部分, 并且可以在建立连接的时候设置角色。
定义角色
角色可以通过 元素<Role>来设置 , 它是元素<Schema> 的直接的子元素.
下面是一个关于角色的例子:
<Role name="California manager">
<SchemaGrant access="none">
<CubeGrant cube="Sales" access="all">
<HierarchyGrant hierarchy="[Store]" access="custom" topLevel="[Store].[Store Country]">
<MemberGrant member="[Store].[USA].[CA]" access="all"/>
<MemberGrant member="[Store].[USA].[CA].[Los Angeles]" access="none"/>
</HierarchyGrant>
<HierarchyGrant hierarchy="[Customers]" access="custom" topLevel="[Customers].[State Province]" bottomLevel="[Customers].[City]">
<MemberGrant member="[Customers].[USA].[CA]" access="all"/>
<MemberGrant member="[Customers].[USA].[CA].[Los Angeles]" access="none"/>
</HierarchyGrant>
<HierarchyGrant hierarchy="[Gender]" access="none"/>
</CubeGrant>
</SchemaGrant>
</Role>
元素 <SchemaGrant> 定义了模式中缺省的对象方问权限. 访问属性可以是 "all" 或者 "none"; 这个属性可以被具体的权限对象继承. 在这个例子中, 因为 access="none", 用户只能浏览"Sales" 立方体, 这里明确的赋予了这个权限.
元素 <CubeGrant> 定义了立方体的访问权限. 就像 <SchemaGrant>, 属性access 可以是"all" 或者 "none", 并且能够被cube中具体的子对象继承.
元素 <HierarchyGrant>定义了层次的访问权限. 属性access 可以是"all", 意思是所有的members都是可见的; "none",意思是 hierarchy的存在对用户是隐藏的; "custom", 你可以利用属性 topLevel 定义可见的最高层 (阻止用户 进行上卷操作, 比如浏览税收 上卷到 Store Country 层); 或者用属性 bottomLevel 定义可见的最底层 (这里阻止用户查看顾客个人的细节数据);或者控制用户查看哪一个成员集合,通过嵌套定义元素 <MemberGrant>.
你也可以只定义元素 <MemberGrant> ,如果模式的<HierarchyGrant> 有属性access="custom". Member grants 赋予 (或者取消) 访问给定的成员, 以及它的所有子成员.
posted @ 2008-03-26 22:30 edsonjava 阅读(902) | 评论 (0)编辑 收藏
 
改了不少JPivot/mondrian代码,还修正了jpivot一个bug。

对JPivot的jfreechart和drillthrough显示做了增强,终于可以拿出去给人用了。

先说说性能问题: 先是找了一台闲置的IBM X445 PC Server,4×2GHZ CPU,8G内存,2×146G硬盘,操作系统 windows 2000 , 开启AWE 3G参数。然后装Oracle 10g,数据仓库模式,使用了4G AWE内存共约4.5GB内存。再建成一张1600万用户数据宽表,宽表一律使用bitmap索引,还有其他20个左右维表。 然后就简单了,写mondrian Cube,配JPivot。 最后搞下来的结果是:基本上mondrian 每次做group by 操作最长不超过30秒,一般在20秒左右。用户基本可以接受。问了使用NCR的朋友,说NCR使用自己的数据库,也基本是这样的一个性能。 PS:偷偷问一声,在这基础上,性能还能改进否?

 

再说说方向问题: 我们现在使用2个OLAP,一个是jpivot + mondrian ,属于ROLAP;另一个是BO intelligence + essbase,属于MOLAP。目前的感觉是,由于DB性能强悍,导致ROLAP和MOLAP在性能上相差不大。同时ROLAP可以直接和报表系统共用同一张表。而MOLAP则需要使用工具来打CUBE做数据转换,这样在开发和维护工作量上,MOLAP比ROLAP大。 另外往往业务部门分析到最后,就是要看明细数据了,这个时候MOLAP的前端工具往往不能做好支持。而jpivot则无此问题。 综上所述,我目前好像还没看到必须用MOLAP的理由,听说华为原来用M$ 的OLAP,后来好像支持不住了,就直接用回了BO 报表,呵呵。

 

JPivot的问题: 操作太复杂,必须对OLAP的概念有清晰的了解,普通用户无法使用。与mondrian 集成不够紧密。mondrian不提供数据钻取功能,该功能是jpivot自己做的,所以会导致数据类型格式丢失。钻取详细数据量无限制,导致内存溢出。界面比较难看,操作方式非主流使用jpivot自己的mvc框架,不易其他框架集成 总体来说,jpivot目前已经不是一个玩具了,完全可以用于企业级的操作,而且定位在高端业务分析人员。


拿出来开源比较困难,一方面jpivot在不停升级,另一方面我在修改的时候不顾一切,在jpivot中乱引用了mondrian代码,还把mondrian部分无用代码全删了。这样,我就在这个帖里把能共享部分都在这里帖出来。 首先是我优化后的界面。 1.图标用了pentaho里面的图标。 2.jpivot里面其实支持3D饼图,只是选项未开,我先将jfreechart升级成1.0.2,又对饼图、线图等做了美观。 3.drillthrough是jpivot相对其他olap产品的杀手级功能,但是有不少细节未完善。我基本都一一补上。 在界面上可以看出,我添加了一个CSV导出功能(改了WCF库),同时限制最大导出20万行记录(改了jpivot)。界面上显示的“访问次数”是measure的名字,实际上应该显示“访问时间”,该问题暂时无解。另外修正了一下numberformat、dateformat不正确的一些问题。 4.excel导出时,格式很难看,但是由于excel本身只支持256色,无法显示web上的底色,所以我修改了只显示蓝色的border,底色一律为白。 附件中rar里面是web的CSS文件、Excel的生成文件和jpivot的图表生成部分代码,感兴趣的朋友各取所需吧


另外还把jpviot完全整合到我自己的系统中去了,呵呵。 可以在系统web界面上编写Cube和MDX定义,Cube和MDX为一对多关系。Cube通过xsd来做校验。开发Cube和MDX的时候可以随时做预览。 然后再把一个MDX在界面发布成一个单独的OLAP分析。 下一步的目标是将数据权限与jpivot做整合,由于Cube的xml是由系统自动生成的,所以mondrian的role配置也可以由系统根据配置自动生成。 这部分代码涉及我的系统和框架比较深,所以不帖代码了哈,大家自己搞搞2天也就出来了


还做了个及其变态的功能,就是把界面上所有显示的jpivot cell,一个个的去取出钻取数据的measure,然后生成csv文件,打成zip包给用户下载或发到其他接口。 当时我化了整整一个礼拜钻研mondrian代码,希望可以不用那么傻傻个一个个去钻,结果失败...

我在用Jpivot的时候,发现用mondrian是影响取数性能其中的一个瓶颈........ 经研究.....我们自己修改了jpivot和wcf的一些代码来适应我们自己的项目.........以下是我做的一些修改.....想听听大家的意见 1.脱离mondrian.直接写dll的方式取数,然后生成XML数据 .我发现脱离mondrian自己写了一个DLL去调用MSSQL 2000 的OLAP,数度很快........... 2 .修改界面的显示方式 上面也说道.Jpivot的界面一个不好看,二是用起来很不方便.比如取维度等的时候....一层一层的进去实在很麻烦.... a.修改取维度的方式 我们参照ms的做法 做成一个了一个树的取数,研究jpivot里面的代码.如果直接用jpivot的代码取数据十分慢.这样我自己通过AJAX和Jpivot结合,动态生成树的结构,然后在树上取维度的时候,直接通过鼠标托到选择维度textbox上.........依照条件生成相应MDX....显示数据..... b.修改数据显示的样式.和取维度,生成MDX分开了. 显示数据我用了另外一种方式显示.就是用Frame分为上下两层.....上下两层可以通过按钮扩大整个页面........ 3. 集成在自己的框架中 集成在自己的框架中,我个人觉得是比较麻烦的一件事情.一点小事没有搞好就很麻烦...因为我们是用JSF开发的.所以依照Jpivot....自己写了一些组件来辅助开发,我自己开发主要改成比较像ms 2000 的olap分析方法... 还未完成的需求 JFreeChar的功能还需要加强. 个人感觉:jpivot是很不错.可是不能一拿来就用..我发现好多人用jpivot都要修改好多东西....但是修改起来又比较麻烦....java,j2ee,xml ,xslt,javascript,taglib.....好多东西都要懂.....
posted @ 2008-03-26 22:28 edsonjava 阅读(2214) | 评论 (1)编辑 收藏
 

This documentation is related to the displaytag 1.1.x releases.

The latest available release is 1.1.1

Displaytag 1.1 offers several enhancements over 1.0: the most notable news are support for partial lists and enhanced decorator APIs, but there is also a lot more. Be sure to read the migration guide for upgrading an existing application from displaytag 1.0. A full changelog is also available.

Overview

The display tag library is an open source suite of custom tags that provide high-level web presentation patterns which will work in an MVC model. The library provides a significant amount of functionality while still being easy to use.

What can I do with it?

Actually the display tag library can just... display tables! Give it a list of objects and it will handle column display, sorting, paging, cropping, grouping, exporting, smart linking and decoration of a table in a customizable XHTML style.

The tables in the sample images below were generated from lists using the <display:table> tag:

sample tables produced with the display:table tag
posted @ 2008-02-23 23:47 edsonjava 阅读(501) | 评论 (0)编辑 收藏
 
近在论证java领域的开源BI前端框架,把随手记得东西和大家分享下.
因为只看了几天,有没时间整理所以看起来比较乱,也不是很深入。

目前在java领域较常见的BI前端框架(商业智能项目)主要有以下几个Pentaho,spagoBi, OpenI, JASPER intelligence等开源框架。

 他们都有自己的强项和不足,下面简要介绍下:

轻量级的:

OpenI使用Mondrian和Jpivot框架,报表引擎是jasper report,数据挖掘接口是R-Project,

相对来说开发和学习比较简单,而且OpenI支持使用MS的数据仓库(xmla),但是其国际化比较失败(中文乱码),要深入改造。

 JASPER intelligence也是个轻型项目,对jasper report的支持最好,所以报表部分比较好。

 重量级的:

PentahospagoBi是两个比较大的框架了,集成了相当多的开源项目,JfreeReport、Mondrian、Kettle、Weka基本都使用了。特别适合大型复杂项目的开发。

      Pentaho在中国使用的比较多,文档什么的也多一点。尤其值得一提的是网络上对他的中文支持做的相当好,很多志愿者翻译了它的文档。这给我们开发带来很大便利。

 

      Pentaho的模块工作流引擎、中心资源库、审计组件、报表设计工具、ETL工具、OLAP Server、多维展示、数据挖掘组件各种组建都有。

而且Pentaho得到了很大的投资,开发后劲很大,而且会有付费的官方发售版本。 

http://blog.csdn.net/dust_bug/archive/2006/09/18/1240753.aspx

这个是Pentaho源代码阅读报告》,介绍Pentaho构架相当的全面。 

Pentaho的中文论坛在http://www.bipub.org/ 

Pentaho相对spagoBi来说功能较强,尤其是工作流一块做的相当不错。

官方站的demos在http://www.pentaho.com/products/demos/

 spagoBi功能也很强,尤其是最近发布的1。9版本,在http://spagobi.eng.it:8080/sbiportal/faces/public/exo(或http://spagobi.eng.it:8080/sbiportal

的demos里展现了spagoBi很多功能。

 后记
这几款BI框架因为都是开源的前端框架,所以核心部分使用的还是一些开源项目,

Mondrian,Jpivot,JfreeReport,所以在使用的时候搭建合适的框架会占用项目很大一部分时间,但是一旦框架搭建好了,基本就可以象流水线一样出报表了。

但是期望在原始功能上添加性能功能是比较麻烦的,为了一个新加的功能可能需要相当长的时间来实现。

另外这些开源框架的权限管理都不怎么强,可能需要改造。

另外,全球话的问题也是问题。象OpenI完全不支持中文,必须改造。

posted @ 2008-02-23 23:38 edsonjava 阅读(933) | 评论 (0)编辑 收藏
 

我们都知道“瞎子摸象”的故事。不同的瞎子对大象的认识不同,因为他们只认识了自己摸到的地方。而企业如果要避免重犯这样的错误,那就离不开商务智能(BI)。专家认为,BI对于企业的重要性就像聪明才智对于个人的重要性。欧美企业的经验也证明,企业避免无知和一知半解危险的有效手段就是商务智能。商务智能旨在充分利用企业在日常经营过程中收集的大量数据和资料,并将它们转化为信息和知识来免除各种无知状态和瞎猜行为。   

支持BI的开源工具数量众多,但是大多数的工具都是偏重某方面的。例如,CloverETL偏重ETL,JPivot偏重多维分析展现,Mondrian是OLAP服务器。而Bee、Pentaho和SpagoBI等项目则针对商务智能问题提供了完整的解决方案。

ETL 工具

ETL开源工具主要包括CloverETL和Octupus等。

(1)CloverETL是一个Java的ETL框架,用来转换结构化的数据,支持多种字符集之间的转换(如ASCII、UTF-8和ISO-8859-1等);支持JDBC,同时支持dBase和FoxPro数据文件;支持基于XML的转换描述。

(2)Octupus是一个基于Java的ETL工具,它也支持JDBC数据源和基于XML的转换定义。Octupus提供通用的方法进行数据转换,用户可以通过实现转换接口或者使用Jscript代码来定义转换流程。

OLAP服务器

(1)Lemur主要面向HOLAP,虽然采用C++编写,但是可以被其他语言的程序所调用。Lemur支持基本的操作,如切片、切块和旋转等基本操作。

(2)Mondrian面向ROLAP包含4层:表示层、计算层、聚集层、存储层。

● 表示层:指最终呈现在用户显示器上的以及与用户之间的交互,有许多方法来展现多维数据,包括数据透视表、饼、柱、线状图。

● 计算层:分析、验证、执行MDX查询。

● 聚集层:一个聚集指内存中一组计算值(cell),这些值通过维列来限制。计算层发送单元请求,如果请求不在缓存中,或者不能通过旋转聚集导出的话,那么聚集层向存储层发送请求。聚合层是一个数据缓冲层,从数据库来的单元数据,聚合后提供给计算层。聚合层的主要作用是提高系统的性能。

● 存储层:提供聚集单元数据和维表的成员。包括三种需要存储的数据,分别是事实数据、聚集和维。

OLAP客户端

JPivot是JSP风格的标签库,用来支持OLAP表,使用户可以执行典型的OLAP操作,如切片、切块、上钻、下钻等。JPivot使用Mondrian服务器,分析结果可以导出为Excel或PDF文件格式。

数据库管理系统

主要的开源工具包括MonetDB、MySQL、MaxDB和PostgreSQL等。这些数据库都被设计用来支持BI环境。MySQL、MaxDB和PostgreSQL均支持单向的数据复制。BizGres项目的目的在于使PostgreSQL成为数据仓库和 BI的开源标准。BizGres为BI环境构建专用的完整数据库平台。

完整的BI开源解决方案

1.Pentaho 公司的Pentaho BI 平台

它是一个以流程为中心的、面向解决方案的框架,具有商务智能组件。BI 平台是以流程为中心的,其中枢控制器是一个工作流引擎。工作流引擎使用流程定义来定义在 BI 平台上执行的商务智能流程。流程可以很容易被定制,也可以添加新的流程。BI 平台包含组件和报表,用以分析这些流程的性能。BI 平台是面向解决方案的,平台的操作是定义在流程定义和指定每个活动的 action 文档里。这些流程和操作共同定义了一个商务智能问题的解决方案。这个 BI 解决方案可以很容易地集成到平台外部的商业流程。一个解决方案的定义可以包含任意数量的流程和操作。

BI平台包括一个 BI 框架、BI 组件、一个 BI 工作台和桌面收件箱。BI 工作台是一套设计和管理工具,集成到Eclipse环境。这些工具允许商业分析人员或开发人员创建报表、仪表盘、分析模型、商业规则和 BI 流程。Pentaho BI 平台构建于服务器、引擎和组件的基础之上,包括J2EE 服务器、安全与权限控制、portal、工作流、规则引擎、图表、协作、内容管理、数据集成、多维分析和系统建模等功能。这些组件的大部分是基于标准的,可使用其他产品替换之。

2.ObjectWeb

该项目近日发布了SpagoBi 1.8版本。SpagoBi 是一款基于Mondrain+JProvit的BI方案,能够通过OpenLaszlo产生实时报表,为商务智能项目提供了一个完整开源的解决方案,它涵盖了一个BI系统所有方面的功能,包括:数据挖掘、查询、分析、报告、Dashboard仪表板等等。SpagoBI使用核心系统与功能模块集成的架构,这样在确保平台稳定性与协调性的基础上又保证了系统具有很强的扩展能力。用户无需使用SpagoBI的所有模块,而是可以只利用其中的一些模块。

SpagoBI使用了许多已有的开源软件,如Spago和Spagosi等。因此,SpagoBI集成了 Spago的特征和技术特点,使用它们管理商务智能对象,如报表、OLAP分析、仪表盘、记分卡以及数据挖掘模型等。SpagoBI支持BI系统的监控管理,包括商务智能对象的控制、校验、认证和分配流程。SpagoBI采用Portalet技术将所有的BI对象发布到终端用户,因此BI对象就可以集成到为特定的企业需求而已经选择好的Portal系统中去。

3.Bee项目

该项目是一套支持商务智能项目实施的工具套件,包括ETL工具和OLAP 服务器。Bee的ETL工具使用基于Perl的BEI,通过界面描述流程,以XML形式进行存储。用户必须对转换过程进行编码。Bee的ROLAP 服务器保证多通SQL 生成和强有力的高速缓存管理(使用MySQL数据库管理系统)。ROLAP服务器通过SOAP应用接口提供丰富的客户应用。Web Portal作为主要的用户接口,通过Web浏览器进行报表设计、展示和管理控制,分析结果可以以Excel、PDF、PNG、PowerPoint、 text和XML等多种形式导出。

Bee项目的特点在于:

● 简单快捷的数据访问;

● 支持预先定义报表和实时查询;

● 通过拖拽方式轻松实现报表定制;

● 完整报表的轻松控制;

● 以表和图进行高质量的数据展示。

posted @ 2008-02-23 23:29 edsonjava 阅读(510) | 评论 (0)编辑 收藏
 
java /zongfeng 
mondrian是一个olap工具,jpviot是一个显示它处理结果的taglib,使用这2个工具可以做复杂的统计汇总并显示

OLAP:Mondrian&JPviot


olap:online analytical processing(联机分析处理),实时的分析大量数据,其操作通常是 只读的.online意味着即使是大量的数据,系统对查询的响应也要足够快.

olap使用一种技术叫做multimensional analysis(多维分析),关系数据库将数据存成行和列的形式,多维数据表包含轴和单元.

mondrian包含4层:表示层,计算层,聚集层,存储层.

表示层:指最终呈现在用户显示器上的,以及与用户之间的交互,有许多方法来展现多维数据,包括数据透视表,饼,柱,线状图.

计算层:分析,验证,执行MDX查询.

聚集层:一个聚集指内存中一组计算值(cell),这些值通过维列来限制.计算层发送单元请求,如果请求不在缓存中,或者不能通过旋转聚集导出的话,聚集层向存储层发送请求.

聚合层是一个数据缓冲层,从数据库来的单元数据,聚合后提供给计算层。聚合层的主要作用是提高系统的性能。

存储层:提供聚集单元数据和维表的成员,这些层可以不在同一机子上,但是计算和聚集层必须在同一台机子上.

三种需要存储的数据:1:事实数据2:聚集3:维

配置文件中的特定含义:
1:cube(立方体):是维和量的集合

2:measure(量):一个具体的测量量

3:dimension(维):一个属性或者一系列属性,通过维可以将量分类

下面是我关于jpviot的修改:jpviot是显示mondrian的一个taglib

问题1:让行和列的标题显示为中文,此问题非常简单,只需要在你的schema中设置一下编码即可,例如在FoodMart中设置如下

<?xml version="1.0" encoding="gb2312"?>

然后可以这样描述Measure:

<Measure name="库存消耗" column="store_cost" aggregator="sum" formatString="#,###.00"/>

所有带name属性的都可以替换成中文,jpviot会自动显示这些中文.

问题2:关于去掉Measure标题的问题:

默认生成的报表中会有这么一行
<tr>
<th rowspan="1" colspan="2" class="corner-heading" nowrap="nowrap">&nbsp;</th><th rowspan="1" colspan="3" class="heading-heading" nowrap="nowrap"><img height="9" width="9" border="0" src="/jpivot/jpivot/table/drill-position-other.gif">Measures</th>
</tr>

这一行有个默认的标题是Measure,如果你不想删除这一行,而仅仅想修改这个标题的话,可以修改
WEB-INFclassescomtonbellerjpivotmondrianresources.properties.但是注意这个文件中内容写成英文没问题,如写成中文的话应该写成unicode,例如023这样的形式.

如果你要去掉这一行的话,修改配置文件和xsl恐怕做不到,我分析了其代码,最终在代码层次上做了修改:
修改的代码为com.tonbeller.jpivot.table.ColumnAxisBuilderImpl:

将其构造函数中的setHierarchyHeader的参数修改为setHierarchyHeader(NO_HEADER);这个函数支持3个参数,我们修改后就不会显示那个标题行了.

问题3:生成图表后自动生成chart表的问题:

我测试生成图表中的中文问题都解决了,但是每次生成chart图时会报UTF编码错误,从错误判断应该是某个文件的编码错误,起初根据错误判断是filter的问题,可是filter那点代码中根本不涉及编码的问题.我将很多配置文件的编码都改了也不行.因为那个英文例子没问题,我查看了JFreechart的一个servlet(org.jfree.chart.servlet.DisplayChart),因为jpviot就是调用这个servlet实现绘图的,分析这个servlet我知道它会在一个临时目录生成png文件,然后交给servlet写到浏览器的响应中去,我找到那个临时目录(tomcattemp),发现里面已经生成了正确的中文图形.从而判断图形生成正确,但是写到浏览器中时出了问题.最后我查看能生成英文图表的那个例子,发觉不仅仅在html中生成图形,而且生成map.而这个map的生成全是在程序中做的,程序生成一个xml文件,通过chart.xsl解析生成map的最终html代码.但是在程序中生成时并没有加入编码设置,因此问题出在生成map这儿.

最终修改代码如下:

com.tonbeller.jpivot.chart.ChartComponent:

在render函数中修改如下:

String desc="<?xml version="1.0" encoding="gb2312"?>";
String xchart =desc+"n"+ "<xchart>" + writeImageMap(filename, info, false) + "</xchart>";
这样就为xchart设置了编码.

问题4:修改jfreechart中的默认字体:

com.tonbeller.jpivot.chart.ChartComponent中定义了几种字体,但是这几种字体都是英文字体,我将其修改为宋体:
把所有的字体定义都改为"SimSun"
注意到这儿并没有玩,如果你仅仅修改程序,仍旧会出现问题,报错说没有适合"SimSun"的item
同时要修改一个配置文件:WEB-INFjpivotchartchartpropertiesform.xml
在这个配置文件中将SimSun加入其中,形式如下:

<listBox1 type="string" modelReference="fontName" label="Title font">
<listItem value="SansSerif" label="SansSerif"/>
<listItem value="Serif" label="Serif"/>
<listItem value="SimSun" label="SimSun"/>
<listItem value="Monospaced" label="Monospaced"/>
</listBox1>

以上为我最近的一点心得,我会完善这篇文档,将包含mondrian中schema的书写方法和MDX查询语言,欢迎大家交流
link1:微软的MDX中文文档

posted @ 2008-02-23 23:20 edsonjava 阅读(863) | 评论 (0)编辑 收藏
 

在xml应用中,经常将一些URL信息作为xml数据存储,其中URL参数有可能包含有中文字符。
当使用dom对xml数据进行解析时,可以对中文字符进行编码。
但如果只使用xslt来显示xml数据时(data.xml+data.xsl),发现此时的URL会出现编码错误.
即使指定编码类型(encoding="gb2312"),依然会出现同样的问题.
测试发现:是IE的缓存机制问题,IE仍会把新的页面(所链接的URL)的MIME内容类型默认为text/xml

解决方法:
1.指定输出文档类型为xml文档  (example:data.xsl)
 <xsl:output method="xml"  encoding="gb2312" media-type="text/xml" />
2.在新的窗口打开,给联接增加属性,指明目标窗口为其他窗口  (example:data2.xsl)
 <xsl:attribute name="target">_blank</xsl:attribute>


examples:


/*** data.xml ***/

<?xml version="1.0" encoding="gb2312"?>
<?xml-stylesheet type="text/xsl" href="data.xsl"?>
<root>
 <search>
  <url>http://www.google.com/search?q=</url>
  <word>xml数据</word>
 </search>
 <search>
  <url>http://www1.baidu.com/baidu?word=</url>
  <word>xml数据</word>
 </search>
 <search>
  <url>http://www.google.com/search?q=</url>
  <word>极限编程(xp)</word>
 </search>
 <search>
  <url>http://www1.baidu.com/baidu?word=</url>
  <word>极限编程(xp)</word>
 </search>
</root>


/*** data.xsl ***/

<?xml version="1.0" encoding="gb2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- 去掉下面一句,将出现错误 -->
<xsl:output method="xml"  encoding="gb2312" media-type="text/xml" />

<xsl:template match="/">
 <xsl:apply-templates /> 
</xsl:template>

<xsl:template match="search">
 <xsl:element name="a">
  <xsl:attribute name="href"><xsl:value-of select="url" /><xsl:value-of select="word" /></xsl:attribute>
  <xsl:value-of select="word" />
 </xsl:element>
 <br />
</xsl:template>

</xsl:stylesheet>


/*** data2.xsl ***/

<?xml version="1.0" encoding="gb2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
 <xsl:apply-templates /> 
</xsl:template>

<xsl:template match="search">
 <xsl:element name="a">
  <xsl:attribute name="href"><xsl:value-of select="url" /><xsl:value-of select="word" /></xsl:attribute>
  <!-- 去掉下面一句,将出现错误 -->
  <xsl:attribute name="target">_blank</xsl:attribute>
  <xsl:value-of select="word" />
 </xsl:element>
 <br />
</xsl:template>

</xsl:stylesheet>

posted @ 2008-02-23 23:08 edsonjava 阅读(510) | 评论 (0)编辑 收藏
 
软件包:javax.servlet.http 
      所包含的接口:HttpServletRequest;HttpServletResponse;HttpSession;HttpSessionBindingListener;HttpSessionContext。
      所包含的类:Cookie;HttpServlet;HttpSessionBindingEvent;HttpUtils。

      一、HttpServletRequest接口
      定义\
      public interface HttpServletRequest extends ServletRequest;
      用来处理一个对Servlet的HTTP格式的请求信息。
      方法
      1、getAuthType
      public String getAuthType();
      返回这个请求的身份验证模式。
      2、getCookies
      public Cookie[] getCookies();
      返回一个数组,该数组包含这个请求中当前的所有cookie。如果这个请求中没有cookie,返回一个空数组。
      3、getDateHeader
      public long getDateHeader(String name);
      返回指定的请求头域的值,这个值被转换成一个反映自1970-1-1日(GMT)以来的精确到毫秒的长整数。
      如果头域不能转换,抛出一个IllegalArgumentException。如果这个请求头域不存在,这个方法返回-1。
      4、getHeader
      public String getHeader(String name);
      返回一个请求头域的值。(译者注:与上一个方法不同的是,该方法返回一个字符串)
      如果这个请求头域不存在,这个方法返回-1。
      5、getHeaderNames
      public Enumeration getHeaderNames();
      该方法返回一个String对象的列表,该列表反映请求的所有头域名。
      有的引擎可能不允许通过这种方法访问头域,在这种情况下,这个方法返回一个空的列表。
      6、getIntHeader
      public int getIntHeader(String name);
      返回指定的请求头域的值,这个值被转换成一个整数。
      如果头域不能转换,抛出一个IllegalArgumentException。如果这个请求头域不存在,这个方法返回-1。
      7、getMethod
      public String getMethod();
      返回这个请求使用的HTTP方法(例如:GET、POST、PUT)
      8、getPathInfo
      public String getPathInfo();
      这个方法返回在这个请求的URL的Servlet路径之后的请求URL的额外的路径信息。如果这个请求URL包括一个查询字符串,在返回值内将不包括这个查询字符串。这个路径在返回之前必须经过URL解码。如果在这个请求的URL的Servlet路径之后没有路径信息。这个方法返回空值。
      9、getPathTranslated
      public String getPathTranslated();
      这个方法获得这个请求的URL的Servlet路径之后的额外的路径信息,并将它转换成一个真实的路径。在进行转换前,这个请求的URL必须经过URL解码。如果在这个URL的Servlet路径之后没有附加路径信息。这个方法返回空值。
      10、getQueryString
      public String getQueryString();
      返回这个请求URL所包含的查询字符串。一个查询字串符在一个URL中由一个“?”引出。如果没有查询字符串,这个方法返回空值。
      11、getRemoteUser
      public String getRemoteUser
      返回作了请求的用户名,这个信息用来作HTTP用户论证。
      如果在请求中没有用户名信息,这个方法返回空值。
      12、getRequestedSessionId
      public String getRequestedSessionId();
      返回这个请求相应的session id。如果由于某种原因客户端提供的session id是无效的,这个session id将与在当前session中的session id不同,与此同时,将建立一个新的session。
      如果这个请求没与一个session关联,这个方法返回空值。
      13、getRequestURI
      public String getRequestURI();
      从HTTP请求的第一行返回请求的URL中定义被请求的资源的部分。如果有一个查询字符串存在,这个查询字符串将不包括在返回值当中。例如,一个请求通过/catalog/books?id=1这样的URL路径访问,这个方法将返回/catalog/books。这个方法的返回值包括了Servlet路径和路径信息。
      如果这个URL路径中的的一部分经过了URL编码,这个方法的返回值在返回之前必须经过解码。
      14、getServletPath
      public String getServletPath();
      这个方法返回请求URL反映调用Servlet的部分。例如,一个Servlet被映射到/catalog/summer这个URL路径,而一个请求使用了/catalog/summer/casual这样的路径。所谓的反映调用Servlet的部分就是指/catalog/summer。
      如果这个Servlet不是通过路径匹配来调用。这个方法将返回一个空值。
      15、getSession
      public HttpSession getSession();
      public HttpSession getSession(boolean create);
      返回与这个请求关联的当前的有效的session。如果调用这个方法时没带参数,那么在没有session与这个请求关联的情况下,将会新建一个session。如果调用这个方法时带入了一个布尔型的参数,只有当这个参数为真时,session才会被建立。
      为了确保session能够被完全维持。Servlet开发者必须在响应被提交之前调用该方法。
      如果带入的参数为假,而且没有session与这个请求关联。这个方法会返回空值。
      16、isRequestedSessionIdValid
      public boolean isRequestedSessionIdValid();
      这个方法检查与此请求关联的session当前是不是有效。如果当前请求中使用的session无效,它将不能通过getSession方法返回。
      17、isRequestedSessionIdFromCookie
      public boolean isRequestedSessionIdFromCookie();
      如果这个请求的session id是通过客户端的一个cookie提供的,该方法返回真,否则返回假。
      18、isRequestedSessionIdFromURL
      public boolean isRequestedSessionIdFromURL();
      如果这个请求的session id是通过客户端的URL的一部分提供的,该方法返回真,否则返回假。请注意此方法与isRequestedSessionIdFromUrl在URL的拼写上不同。
      以下方法将被取消\

      19、isRequestedSessionIdFromUrl
      public boolean isRequestedSessionIdFromUrl();
      该方法被isRequestedSessionIdFromURL代替。

      二、HttpServletResponse接口
      定义\

      public interface HttpServletResponse extends ServletResponse
      描述一个返回到客户端的HTTP回应。这个接口允许Servlet程序员利用HTTP协议规定的头信息。
      成员变量
      public static final int SC_CONTINUE = 100;
      public static final int SC_SWITCHING_PROTOCOLS = 101;
      public static final int SC_OK = 200;
      public static final int SC_CREATED = 201;
      public static final int SC_ACCEPTED = 202;
      public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
      public static final int SC_NO_CONTENT = 204;
      public static final int SC_RESET_CONTENT = 205;
      public static final int SC_PARTIAL_CONTENT = 206;
      public static final int SC_MULTIPLE_CHOICES = 300;
      public static final int SC_MOVED_PERMANENTLY = 301;
      public static final int SC_MOVED_TEMPORARILY = 302;
      public static final int SC_SEE_OTHER = 303;
      public static final int SC_NOT_MODIFIED = 304;
      public static final int SC_USE_PROXY = 305;
      public static final int SC_BAD_REQUEST = 400;
      public static final int SC_UNAUTHORIZED = 401;
      public static final int SC_PAYMENT_REQUIRED = 402;
      public static final int SC_FORBIDDEN = 403;
      public static final int SC_NOT_FOUND = 404;
      public static final int SC_METHOD_NOT_ALLOWED = 405;
      public static final int SC_NOT_ACCEPTABLE = 406;
      public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
      public static final int SC_REQUEST_TIMEOUT = 408;
      public static final int SC_CONFLICT = 409;
      public static final int SC_GONE = 410;
      public static final int SC_LENGTH_REQUIRED = 411;
      public static final int SC_PRECONDITION_FAILED = 412;
      public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
      public static final int SC_REQUEST_URI_TOO_LONG = 414;
      public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
      public static final int SC_INTERNAL_SERVER_ERROR = 500;
      public static final int SC_NOT_IMPLEMENTED = 501;
      public static final int SC_BAD_GATEWAY = 502;
      public static final int SC_SERVICE_UNAVAILABLE = 503;
      public static final int SC_GATEWAY_TIMEOUT = 504;
      public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
      以上HTTP产状态码是由HTTP/1.1定义的。
      方法
      1、addCookie
      public void addCookie(Cookie cookie);
      在响应中增加一个指定的cookie。可多次调用该方法以定义多个cookie。为了设置适当的头域,该方法应该在响应被提交之前调用。
      2、containsHeader
      public boolean containsHeader(String name);
      检查是否设置了指定的响应头。
      3、encodeRedirectURL
      public String encodeRedirectURL(String url);
      对sendRedirect方法使用的指定URL进行编码。如果不需要编码,就直接返回这个URL。之所以提供这个附加的编码方法,是因为在redirect的情况下,决定是否对URL进行编码的规则和一般情况有所不同。所给的URL必须是一个绝对URL。相对URL不能被接收,会抛出一个IllegalArgumentException。
      所有提供给sendRedirect方法的URL都应通过这个方法运行,这样才能确保会话跟踪能够在所有浏览器中正常运行。
      4、encodeURL
      public String encodeURL(String url);
      对包含session ID的URL进行编码。如果不需要编码,就直接返回这个URL。Servlet引擎必须提供URL编码方法,因为在有些情况下,我们将不得不重写URL,例如,在响应对应的请求中包含一个有效的session,但是这个session不能被非URL的(例如cookie)的手段来维持。
      所有提供给Servlet的URL都应通过这个方法运行,这样才能确保会话跟踪能够在所有浏览器中正常运行。
      5、sendError
      public void sendError(int statusCode) throws IOException;
      public void sendError(int statusCode, String message) throws
         IOException;
      用给定的状态码发给客户端一个错误响应。如果提供了一个message参数,这将作为响应体的一部分被发出,否则,服务器会返回错误代码所对应的标准信息。
      调用这个方法后,响应立即被提交。在调用这个方法后,Servlet不会再有更多的输出。
      6、sendRedirect
      public void sendRedirect(String location) throws IOException;
      使用给定的路径,给客户端发出一个临时转向的响应(SC_MOVED_TEMPORARILY)。给定的路径必须是绝对URL。相对URL将不能被接收,会抛出一个IllegalArgumentException。
      这个方法必须在响应被提交之前调用。调用这个方法后,响应立即被提交。在调用这个方法后,Servlet不会再有更多的输出。
      7、setDateHeader
      public void setDateHeader(String name, long date);
      用一个给定的名称和日期值设置响应头,这里的日期值应该是反映自1970-1-1日(GMT)以来的精确到毫秒的长整数。如果响应头已经被设置,新的值将覆盖当前的值。
      8、setHeader
      public void setHeader(String name, String value);
      用一个给定的名称和域设置响应头。如果响应头已经被设置,新的值将覆盖当前的值。
      9、setIntHeader
      public void setIntHeader(String name, int value);
      用一个给定的名称和整形值设置响应头。如果响应头已经被设置,新的值将覆盖当前的值。
      10、setStatus
      public void setStatus(int statusCode);
      这个方法设置了响应的状态码,如果状态码已经被设置,新的值将覆盖当前的值。
      以下的几个方法将被取消\
      11、encodeRedirectUrl
      public String encodeRedirectUrl(String url);
      该方法被encodeRedirectURL取代。 
      12、encodeUrl
      public String encodeUrl(String url);
      该方法被encodeURL取代。 
      13、setStatus
      public void setStatus(int statusCode, String message);
      这个方法设置了响应的状态码,如果状态码已经被设置,新的值将覆盖当前的值。如果提供了一个message,它也将会被作为响应体的一部分被发送。

      三、HttpSession接口
      定义\
      public interface HttpSession
      这个接口被Servlet引擎用来实现在HTTP客户端和HTTP会话两者的关联。这种关联可能在多外连接和请求中持续一段给定的时间。session用来在无状态的HTTP协议下越过多个请求页面来维持状态和识别用户。
      一个session可以通过cookie或重写URL来维持。
      方法
      1、getCreationTime
      public long getCreationTime();
      返回建立session的时间,这个时间表示为自1970-1-1日(GMT)以来的毫秒数。 
      2、getId
      public String getId();
      返回分配给这个session的标识符。一个HTTP session的标识符是一个由服务器来建立和维持的唯一的字符串。
      3、getLastAccessedTime
      public long getLastAccessedTime();
      返回客户端最后一次发出与这个session有关的请求的时间,如果这个session是新建立的,返回-1。这个时间表示为自1970-1-1日(GMT)以来的毫秒数。 
      4、getMaxInactiveInterval
      public int getMaxInactiveInterval();
      返加一个秒数,这个秒数表示客户端在不发出请求时,session被Servlet引擎维持的最长时间。在这个时间之后,Servlet引擎可能被Servlet引擎终止。如果这个session不会被终止,这个方法返回-1。
      当session无效后再调用这个方法会抛出一个IllegalStateException。
      5、getValue
      public Object getValue(String name);
      返回一个以给定的名字绑定到session上的对象。如果不存在这样的绑定,返回空值。
      当session无效后再调用这个方法会抛出一个IllegalStateException。
      6、getValueNames
      public String[] getValueNames();
      以一个数组返回绑定到session上的所有数据的名称。
      当session无效后再调用这个方法会抛出一个IllegalStateException。
      7、invalidate
      public void invalidate();
      这个方法会终止这个session。所有绑定在这个session上的数据都会被清除。并通过HttpSessionBindingListener接口的valueUnbound方法发出通告。
      8、isNew
      public boolean isNew();
      返回一个布尔值以判断这个session是不是新的。如果一个session已经被服务器建立但是还没有收到相应的客户端的请求,这个session将被认为是新的。这意味着,这个客户端还没有加入会话或没有被会话公认。在他发出下一个请求时还不能返回适当的session认证信息。
      当session无效后再调用这个方法会抛出一个IllegalStateException。
      9、putValue
      public void putValue(String name, Object value);
      以给定的名字,绑定给定的对象到session中。已存在的同名的绑定会被重置。这时会调用HttpSessionBindingListener接口的valueBound方法。
      当session无效后再调用这个方法会抛出一个IllegalStateException。
      10、removeValue
      public void removeValue(String name);
      取消给定名字的对象在session上的绑定。如果未找到给定名字的绑定的对象,这个方法什么出不做。 这时会调用HttpSessionBindingListener接口的valueUnbound方法。
      当session无效后再调用这个方法会抛出一个IllegalStateException。
      11、setMaxInactiveInterval
      public int setMaxInactiveInterval(int interval);
      设置一个秒数,这个秒数表示客户端在不发出请求时,session被Servlet引擎维持的最长时间。
      以下这个方法将被取消\
      12、getSessionContext
      public HttpSessionContext getSessionContext();
      返回session在其中得以保持的环境变量。这个方法和其他所有HttpSessionContext的方法一样被取消了。

      四、HttpSessionBindingListener接口
      定义\
      public interface HttpSessionBindingListener
      这个对象被加入到HTTP的session中,执行这个接口会通告有没有什么对象被绑定到这个HTTP session中或被从这个HTTP session中取消绑定。
      方法
      1、valueBound
      public void valueBound(HttpSessionBindingEvent event);
      当一个对象被绑定到session中,调用此方法。HttpSession.putValue方法被调用时,Servlet引擎应该调用此方法。
      2、valueUnbound
      public void valueUnbound(HttpSessionBindingEvent event);
      当一个对象被从session中取消绑定,调用此方法。HttpSession.removeValue方法被调用时,Servlet引擎应该调用此方法。

      五、HttpSessionContext接口
      定义\
      此接口将被取消\
      public interface HttpSessionContext
      这个对象是与一组HTTP session关联的单一的实体。
      这个接口由于安全的原因被取消,它出现在目前的版本中仅仅是为了兼容性的原因。这个接口的方法将模拟以前的版本的定义返回相应的值。
      方法
      1、getSession
      public HttpSession getSession(String sessionId);
      当初用来返回与这个session id相关的session。现在返回空值。
      2、getIds
      public Enumeration getIds();
      当初用来返回这个环境下所有session id的列表。现在返回空的列表。

      六、Cookie类\
      定义\
      public class Cookie implements Cloneable
      这个类描述了一个cookie,有关cookie的定义你可以参照Netscape Communications Corporation的说明,也可以参照RFC 2109。
      构造函数
      public Cookie(String name, String value);
      用一个name-value对定义一个cookie。这个name必须能被HTTP/1.1所接受。
      以字符$开头的name被RFC 2109保留。
      给定的name如果不能被HTTP/1.1所接受,该方法抛出一个IllegalArgumentException。
      方法
      1、getComment
      public String getComment();
      返回描述这个cookie目的的说明,如果未定义这个说明,返回空值。
      2、getDomain
      public String getDomain();
      返回这个cookie可以出现的区域,如果未定义区域,返回空值。
      3、getMaxAge
      public int getMaxAge();
      这个方法返回这个cookie指定的最长存活时期。如果未定义这个最长存活时期,该方法返回-1。
      4、getName
      public String getName();
      该方法返回cookie名。
      5、getPath
      public String getPath();
      返回这个cookie有效的所有URL路径的前缀,如果未定义,返回空值。
      6、getSecure
      public boolean getSecure();
      如果这个cookie只通过安全通道传输返回真,否则返回假。
      7、getValue
      public String getValue();
      该方法返回cookie的值。
      8、getVersion
      public int getVersion();
      返回cookie的版本。版本1由RFC 2109解释。版本0由Netscape Communications Corporation的说明解释。新构造的cookie默认使用版本0。
      9、setComment
      public void setComment(String purpose);
      如果一个用户将这个cookie提交给另一个用户,必须通过这个说明描述这个cookie的目的。版本0不支持这个属性。
      10、setDomain
      public void setDomain(String pattern);
      这个方法设置cookie的有效域的属性。这个属性指定了cookie可以出现的区域。一个有效域以一个点开头(.foo.com),这意味着在指定的域名解析系统的区域中(可能是www.foo.com但不是a.b.foo.com)的主机可以看到这个cookie。默认情况是,cookie只能返回保存它的主机。
      11、setMaxAge
      public void setMaxAge(int expiry);
      这个方法设定这个cookie的最长存活时期。在该存活时期之后,cookie会被终目。负数表示这个cookie不会生效,0将从客户端删除这个cookie。
         12、setPath
      public void setPath(String uri);
      这个方法设置cookie的路径属性。客户端只能向以这个给定的路径String开头的路径返回cookie。
      13、setSecure
      public void setSecure(boolean flag);
      指出这个cookie只能通过安全通道(例如HTTPS)发送。只有当产生这个cookie的服务器使用安全协议发送这个cookie值时才能这样设置。
      14、setValue
      public void setValue(String newValue);
      设置这个cookie的值,对于二进制数据采用BASE64编码。
      版本0不能使用空格、{}、()、=、,、“”、/、?、@、:以及;。
      15、setVersion
      public void setVersion(int v);
      设置cookie的版本号

      七、HttpServlet类\
      定义\
      public class HttpServlet extends GenericServlet implements 
         Serializable
      这是一个抽象类,用来简化HTTP Servlet写作的过程。它是GenericServlet类的扩充,提供了一个处理HTTP协议的框架。
      在这个类中的service方法支持例如GET、POST这样的标准的HTTP方法。这一支持过程是通过分配他们到适当的方法(例如doGet、doPost)来实现的。
      方法
      1、doDelete
      protected void doDelete(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP DELETE操作。这个操作允许客户端请求从服务器上删除URL。这一操作可能有负面影响,对此用户就负起责任。
      这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理DELETE请求时,你必须重载这一方法。
      2、doGet
      protected void doGet(HttpServletRequest request, 
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP GET操作。这个操作允许客户端简单地从一个HTTP服务器“获得”资源。对这个方法的重载将自动地支持HEAD方法。
      GET操作应该是安全而且没有负面影响的。这个操作也应该可以安全地重复。
      这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。
      3、doHead
      protected void doHead(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP HEAD操作。默认的情况是,这个操作会按照一个无条件的GET方法来执行,该操作不向客户端返回任何数据,而仅仅是返回包含内容长度的头信息。
      与GET操作一样,这个操作应该是安全而且没有负面影响的。这个操作也应该可以安全地重复。
      这个方法的默认执行结果是自动处理HTTP HEAD操作,这个方法不需要被一个子类执行。 
      4、doOptions
      protected void doOptions(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP OPTION操作。这个操作自动地决定支持哪一种HTTP方法。例如,一个Servlet写了一个HttpServlet的子类并重载了doGet方法,doOption会返回下面的头:
      Allow: GET,HEAD,TRACE,OPTIONS
      你一般不需要重载这个方法。
      5、doPost
      protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP POST操作。这个操作包含请求体的数据,Servlet应该按照他行事。
      这个操作可能有负面影响。例如更新存储的数据或在线购物。
      这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理POST操作时,你必须在HttpServlet的子类中重载这一方法。
      6、doPut
      protected void doPut(HttpServletRequest request, 
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP PUT操作。这个操作类似于通过FTP发送文件。
      这个操作可能有负面影响。例如更新存储的数据或在线购物。
      这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理PUT操作时,你必须在HttpServlet的子类中重载这一方法。
      7、doTrace
      protected void doTrace(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException;
      被这个类的service方法调用,用来处理一个HTTP TRACE操作。这个操作的默认执行结果是产生一个响应,这个响应包含一个反映trace请求中发送的所有头域的信息。
      当你开发Servlet时,在多数情况下你需要重载这个方法。
      8、getLastModified
      protected long getLastModified(HttpServletRequest request);
      返回这个请求实体的最后修改时间。为了支持GET操作,你必须重载这一方法,以精确地反映最后修改的时间。这将有助于浏览器和代理服务器减少装载服务器和网络资源,从而更加有效地工作。返回的数值是自1970-1-1日(GMT)以来的毫秒数。 
默认的执行结果是返回一个负数,这标志着最后修改时间未知,它也不能被一个有条件的GET操作使用。
      9、service
      protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException;
      public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException;
      这是一个Servlet的HTTP-specific方案,它分配请求到这个类的支持这个请求的其他方法。
      当你开发Servlet时,在多数情况下你不必重载这个方法。

      八、HttpSessionBindingEvent类\
      定义\
      public class HttpSessionBindingEvent extends EventObject
      这个事件是在监听到HttpSession发生绑定和取消绑定的情况时连通HttpSessionBindingListener的。这可能是一个session被终止或被认定无效的结果。
      事件源是HttpSession.putValue或HttpSession.removeValue。
      构造函数
      public HttpSessionBindingEvent(HttpSession session, String name);
      通过引起这个事件的Session和发生绑定或取消绑定的对象名构造一个新的HttpSessionBindingEvent。
      方法
      1、getName
      public String getName();
      返回发生绑定和取消绑定的对象的名字。
      2、getSession
      public HttpSession getSession();
      返回发生绑定和取消绑定的session的名字。

         九、HttpUtils类\
      定义\
      public class HttpUtils
      收集HTTP Servlet使用的静态的有效的方法。
      方法
      1、getRequestURL
      public static StringBuffer getRequestURL(HttpServletRequest
            request);
      在服务器上重建客户端用来建立请求的URL。这个方法反映了不同的协议(例如http和https)和端口,但不包含查询字符串。
      这个方法返回一个StringBuffer而不是一个String,这样URL可以被Servlet开发者有效地修改。
      2、parsePostData
      public static Hashtable parsePostData(int len, 
            ServletInputstream in);
      解析一个包含MIME类型application/x-www-form-urlencoded的数据的流,并创建一个具有关键值-数据对的hash table。这里的关键值是字符串,数据是该字符串所对应的值的列表。一个关键值可以在POST的数据中出现一次或多次。这个关键值每出现一次,它的相应的值就被加入到hash table中的字符串所对应的值的列表中。
      从POST数据读出的数据将经过URL解码,+将被转换为空格以十六进制传送的数据(例如%xx)将被转换成字符。
      当POST数据无效时,该方法抛出一个IllegalArgumentException。
      3、parseQueryString
      public static Hashtable parseQueryString(String s);
      解析一个查询字符串,并创建一个具有关键值-数据对的hash table。这里的数据是该字符串所对应的值的列表。一个关键值可以出现一次或多次。这个关键值每出现一次,它的相应的值就被加入到hash table中的字符串所对应的值的列表中。
      从查询字符串读出的数据将经过URL解码,+将被转换为空格以十六进制传送的数据(例如%xx)将被转换成字符。
      当查询字符串无效时,该方法抛出一个IllegalArgumentException。
posted @ 2007-07-26 16:34 edsonjava 阅读(432) | 评论 (0)编辑 收藏
 

CVSNT 2.5.03 Installation on Windows 2003

Author: Bo Berglund
Notice:
This guide is written as an installation help for CVSNT 2.5.03 and higher on Windows 2003 server.
Most of the discussion is also valid for installation on Windows XP-Pro (see below for an important setting).
NOTE! You cannot use XP-Home for CVSNT!
The guide uses the Innosetup based installer that I maintain but similar results can probably be obtained by using the Innosetup installer published by Oliver Giesen as well.
I am not using the MSI installer from the official CVSNT website since I cannot accept non-opensource software if anything else is available.

Table of contents
CVSNT Installation
Configuring the server
Adding CVS users
Adding CVS administrators
Disabling pserver as security measure
The cvs passwd command for adding users
Managing pserver and sserver users
Using the SSPI protocol
Fine-tuning user access of CVS
Using spaces with CVSNT

Links:
CVSNT Auditing Configuration Tutorial
Innosetup CVSNT Installer download
CVSMailer homepage, Automatic email on commits and other events
ViewCvs Installer download
CVSNT command reference
CVSNT download (where you can download the latest CVSNT versions)

Karl Fogel's book 'Open Source development with CVS'
The free part of Karl Fogel's book in HTML format
DevGuy's CVS information pages
CVS-Gui (WinCvs) homepage
WinCvs Dialy use guide
WinCvs 1.3 manual (PDF format)

WinCvs download (on SourceForge)

Installation of the CVSNT server

File system type
Make sure your system is only using the NTFS file system!
Also make sure you are logged on as an administrator of the PC (using an account with administrative priviliges).
And most important: Use the local disk on the CVSNT server!

IMPORTANT for XP-Pro users:
You MUST switch off Simple File Sharing, which is the default for XP (as recommended by Microsoft to make XP somewhat compatible with Win95-98-ME)!
You do this by opening a Windows Explorer and then use the menu command Tools/Folder Options. Select the View tab and scroll down to the bottom where you find this item. Uncheck it now!
Simple File Sharing

Now for the actual installation and configuration:

1. Get the latest release of CVSNT
Get the latest CVSNT Innosetup installation from Innosetup CVSNT Installer download

2. Create CVS directories
Create two directories on the target machine, c:\cvsrepos and c:\cvsrepos\cvstemp. If you have a separate disk partition to spare for CVS then use that instead. The important point here is that the disk where the repository is located on is NTFS.

3. Directory security and permissions
Give c:\cvsrepos\cvstemp security settings that allows full control for all accounts including SYSTEM.
Important:
The cvstemp directory must NOT be located in either c:\WINNT\Temp or anywhere in the "C:\Documents and Settings" tree because these locations have imposed restrictions on user access!
Notice that on XP-Pro out of the box from Microsoft the permissions cannot be set like this until "Simple File Sharing" is switched off (see above). So you must do this if you use XP-Pro. XP-Home is totally unsuitable for CVSNT!

4. Install CVSNT
Run the downloaded CVSNT setup file and make sure to change the installation path to c:\programs\cvsnt (I am paranoid about removing any spaces in paths used by cvs!)
Start screen:
Install screen #1

License agreement:
Install screen #2

Install directory selection:
Note:
I strongly recommend that you install CVSNT to a path that does NOT contain any embedded spaces, for example like this:
Install screen #3

Installation component selection screen:
Install screen #4

Start menu selection:
Install screen #5

Task selection screen:
Install screen #6

Ready to install!
Install screen #7

Install in progress
Install screen #8

Release notes
Install screen #9

Installation done!
Install screen #10


Configuring the CVSNT server and repository


1. CVSNT Control Panel configuration
CVSNT is configured from the CVSNT Control Panel, which can be reached via the shortcut link placed under the Start menu during installation.
Control Panel

Now open the CVSNT control panel applet and do the following:

2. Shut down the CVSNT service
Check that the CVSNT Service is not running (Start button is enabled). This is the initial screen showing that both services are running:

Configuration screen #1
If it is started then stop it. You can leave the Lock Service running.

3. Repository creation
The tab will initially look like this:

Configuration screen #2

4. Add repository
Now you will add a repository to the server. This is done using the "Add" button. When you click this a dialogue shows up where you will define your repository.

Empty repo

5. Repository folder
Click the ellipsis button for Location to bring up the folder browser.
Now you can browse to the location you want for your repository and add a new folder here.
NOTE:
I strongly advice NOT to use paths with embedded spaces for CVS!

Browse for folder

6. Name repository
Now fill in the description and the name of the repository as well.
NOTE:
Do NOT
accept the suggested name, which is the same as the folder path!
Instead only use the bare folder name with a leading / like this:

CVSNT AddRepository

7. Initializing the repository
When you click the OK button there will be a dialog where CVSNT offers to initialize the new repository.
When you click Yes then the new folder will be converted to a real repository:

CVSNT AddRepository


8. First repository added!
Now the list of repositories has been populated with the first repository:

CVSNT Repository


You can add as many as you like (almost) but please do not fall for the temptation to use one repository for each and every project! There are a lot of possibilities to streamline the development process using CVSNT, but many of these use the virtual modules concept and this is only possible within a single repository.


9. Server Settings
Now go on to the Server Settings tab.
Here the default settings are all right for now, except the Temporary Directory setting.

Serversettings

NOTICE about Domains:
You can set the Default domain entry to either the CVSNT server PC name (as in the example above) or the domain name to which the CVSNT server belongs. CVSNT will strip the domain part from all accounts that log on using the default domain before processing. All other logons will be processed using their complete names (DOMAIN\username). The result of this is that all users that "belong" to the domain specified in this box will be logged using only the account name, likewise these usernames will be supplied to the administrative scripts without the domain name. All others will have a domain name added. This must be accounted for in any admin script used.
The CVSROOT/users file is one such admin file that needs to be handled with care concerning domain and non-domain entries.

Temp dir: Use the ellipsis button to browse for the folder prepared for this purpose above:

Tempdir

10. Compatibility
On the next tab (Compatibility Options) there is nothing you need to change for now:

Serversettings screen #1


11. Plugins and protocols
The Plugins tab define a lot of the extra features of CVSNT including some aspects of the connection protocols. The sceen list the available plugins and when you select a line you will be able to configure this plugin by clicking the configure button:

Serversettings screen #1


12. Sserver configuration
Here is the configuration window for the SSERVER protocol plugin. Please set it like this:

SSPI config screen


13. Advanced settings
The final tab on the Control Panel deals with advanced configuration settings and you need not change anything here.

Configuration screen #1


14. Apply configuration changes
Now click the Apply button! This is really important, nothing will happen unless you do this! Note that after you have done this the Apply button is disabled.

15. Start the CVSNT service
Go back to the first tab and click the Start button. After a few moments the Stop button will be highlighted.
Now CVSNT runs (success!)

16. Restart the server
In order for you to be able to use the command line cvs you need to have the path variable set to include the location of the cvs.exe just installed (c:\programs\cvsnt). Since the installer will have put this into the system path variable it will work if you restart the server.
You can check this by going to a command window and typing the command:
cvs --ver
If this results in an eror message then you should restart the server PC before continuing.

Adding and managing CVS users for pserver and sserver access

This is a step that is only needed if you plan on using the sserver or pserver protocols with this CVS server. If your users are all on Windows PC:s pserver is not recommended since it has inherent security flaws. Instead use SSPI because that protocols integrate much better with Windows. If you decide to go with sspi (recommended) then you can skip the discussion on how to add and manage users in this section.

1. Creating CVS accounts on the server
In order for pserver and sserver to work you have to define CVS users, but before you can do this you need to create two real accounts on the server. These accounts will be used by the CVS users as the working accounts.
You need one account which will be a CVS administrative account and one which will be a normal user account. Note that the CVS administrator need not be a server administrator!

Usermanager

The two accounts are added through the Users dialog in Computer Management.
I have used the account names cvsadmin and cvsuser as shown above.

2. Adding CVS users
Open a command window and do the following (replace items <text> with the real values from your system).

set cvsroot=:sspi:<computername>:/TEST
cvs passwd -a <account name>

You will now be asked to enter a password for this user. This password is only for CVS use so it should not be the real system password! Enter the password twice.
Now the CVSROOT/passwd file will be created and the user you entered will be added to the list in this file.
This step is necessary if you are going to use the pserver or sserver protocol in the future since there is no way to log in with pserver/sserver unless there is a passwd file present with the user listed.

Important note:
Any user entered like this MUST be an NT user on the local system! CVS will not accept any user login that is not connected to a "real" account.

3. Aliasing CVS users to real accounts
In order to have many CVS user logins you don't need to create masses of system accounts! Instead you can "alias" a CVS login to a "real" account using this command:

cvs passwd -r <real accountname> -a <cvs login name>

What will happen now is that to CVS the user will be known and registered as the CVS login given in the command, but for file operations that will encounter permission issues the commands will be executed in the context of the real system account that was aliased. This makes it possible to use NTFS file system permissions to limit access to certain parts of the repository to some users. You simply create a system account for which you set limited permissions and then you alias the CVS login to this user.

Note that this command will fail if there is a space embedded in the real account name! DON'T ever use spaces in these contexts!!!!! (But using quotes may solve the problem like this:
cvs passwd -r "system admin" -a "new user"
Since I don't have a valid user with embedded space I could not check the quotes trick with the valid user name parameter, but adding a CVS login with space embedded *can* be done with quotes.)

Examples:
cvs passwd -r cvsuser -a charlie

or if you want the new user to be a CVS administrator:

cvs passwd -r cvsadmin -a rogerh

Note about Domain users:
You can add domain users with the following command:
cvs passwd -r <real accountname> -D <domain name> -a <cvs login name>
This command is reported by a user to have worked for him. I cannot check it because I don't have a domain. But based on information from the mail list I think that it will only work if there is a trust between the CVSNT server PC and the domain controller. If the CVSNT server PC is a member of the domain then this is the case.

The server is now ready to be used and you can check the pserver functionality by doing this:

4. Testing the CVS connection with sserver
Open another command window and type:
set cvsroot=:sserver:<user>@<computername>:/TEST
Replace <user> and <computername> with valid entries like:
set cvsroot=:sserver:charlie@cvsserver:/TEST

Then:
cvs login (enter password on prompt)
cvs ls -l -R
(this should give you a list of the files in TEST/CVSROOT)

5. Testing the CVS connection with pserver
Open another command window and type:
set cvsroot=:pserver:<user>@<computername>:/TEST
Replace <user> and <computername> with valid entries like:
set cvsroot=:pserver:charlie@cvsserver:/TEST

Then:
cvs login (enter password on prompt)
cvs ls -l -R
(this should give you a list of the files in TEST/CVSROOT)

6. Testing the CVS connection from another PC
Open a command window on another PC where you have installed the CVSNT in client only mode and type:
set cvsroot=:sserver:<user>@<computername>:/TEST
Replace <user> and <computername> with valid entries like:
set cvsroot=:pserver:charlie@cvsserver:/TEST

Then:
cvs login (enter password on prompt)
cvs ls -l -R
(this should give you a list of the files in TEST/CVSROOT)

If you cannot get this far, for example if the login fails, then you should check the Windows Firewall settings on the CVSNT server:

7. Modifying Windows Firewall to allow CVS calls

  • Go to Control Panel
  • Open the Windows Firewall item.
  • Select the Exceptions tab
  • Click the "Add port" button
  • Enter the name CVSNT and port number 2401 as a TCP port
  • Accept back to the main screen
  • Make sure Windows Firewall is set to ON

Configuration screen #1

Configuration screen #1

Configuration screen #1

 

Administrating the repository, users with admin rights

There have been a number of reports that people have not been able to add users or execute the cvs admin command even though they were members of the Administartors group or even of Domain Admins. In order to avoid this there is a simple way to manage who will have admin rights on the CVSNT server. It is done through the CVSROOT/admin file.
Here is how to:

  • Create a text file called admin (no extension) inside the CVSROOT directory of the repository.
  • Edit this file by adding on separate lines the login names of the users you want to give administrative priviliges on the CVS server.
The file could look like this:
cvsadmin
charlie
jennifer
john

Now each of these users are able to add new users, change their passwords and use the cvs admin command.

 

Disabling the pserver protocol

If you are exposing your CVSNT server to the Internet you should disable the :pserver: protocol because it uses too low security levels. Only the password for login is 'encrypted' and this is only barely so. All other traffic is in cleartext...
To protect your data you should use the :sspi: protocol instead (and set its encryption flag of course).
As an alternative with the same basic functionality as pserver you can use sserver instead. This uses encrypted connections by default and is probably better if you want to add cvs logins that do not correspond to real accounts (see above).
Disabling any protocol on the CVSNT server is done through the CVSNT Control Panel Plugins tab.
Select the :pserver: protocol line and click Configure. This will bring up a dialogue where you can just uncheck the checkbox to disable the protocol:

Configuration screen #1


Adding new pserver users using the cvs passwd command

As soon as you have logged on using pserver or sserver with a cvs login name that is the same as a local system admin or is aliased to an admin account or is listed in the CVSROOT/admin file then you can add and delete CVS user logins with the passwd command. Here is the full syntax for this command:

Usage:
cvs passwd [-a] [-x] [-X] [-r real_user] [-R] [-D domain] [username]
-a Add user
-x Disable user
-X Delete user
-r Alias username to real system user
-R Remove alias to real system user
-D Use domain password

Example:
cvs passwd -r charlie -a john
This adds a CVS login john with a system alias to account charlie. When the command is executed there will be a password dialogue that asks for the password of john twice for confirmation. Note that this is NOT the actual system password of account john, it is the CVS login password only used by CVSNT.
After the command completes there will be a new line in the CVSROOT/passwd file looking somewhat like this:
john:KacIT8t1F/SKU:charlie
The part between the :: is the DES encrypted password you typed in and will be used by the CVSNT service during login to validate john. Once accepted the account charlie will instead be used so the password is no longer used. The CVSNT service has full priviliges to act on charlie's behalf and this is what it does too.

Managing pserver and sserver users

If you plan on using pserver or sserver with a fairly large number of different user logins then you might want to do as follows (also described above):

  • Create a local user on the CVSNT server by the name of "cvsuser".
  • Login to the cvs server using an admin account.
  • Add the logins with the following command to alias to the cvsuser:
    cvs passwd -r cvsuser -a <login user name>
    You will be asked twice for the login password.
You may add as many pserver users this way as you like. They will all be individually identified by the login name even though the operations on the repository will be done in the cvsuser account context. Mail systems will recognize these user names as well (see below).

 

Using the SSPI protocol for CVSNT access

A few years ago the SSPI protocol was added to CVSNT. It works over TCP/IP so it can more easily traverse firewalls. Like :ntserver:, which is now depreciated, the :sspi: protocol does not need a login, instead the login you did when you started your workstation is used with this protocol.

Limiting user access with sspi
When used normally sspi will accept connections from all system users that authenticate against the system (local or domain). Often this is not really what we want, instead we want to use the same mechansism as is used with :pserver:. Here the CVSROOT/passwd file limits the logins accepted by CVSNT to those mentioned in the file.
With :sspi: this is quite possible, you only have to list the account login names that you want to give CVS access in the passwd file. You also have to set the parameter
SystemAuth = No
in the CVSROOT/config file.
Note that in this case there is no need for entering passwords into the passwd file, sspi uses the system login and the passwd file is only used as a list of accepted users. So simply issuing this command when logged in as a CVS administrator will work:
cvs passwd -a newuser
(press enter twice to tell CVSNT that no password is used)

Fine-tuning user access of CVS

The NTFS file system permissions can be used to tune the access to the CVS repository with more granularity than the passwd file allows. Here is how it is done:

  1. Create a number of NT user groups where members can be added and removed easily.
  2. Don't use aliases in the login scheme, let each user login as himself, for example using :sspi:.
  3. Set permissions (read/write, read only, no access) on the module level in the repository using the CVS groups as tokens.
  4. Give membership to the CVS user groups as needed to individual NT accounts

 

Using spaces with CVSNT

CVSNT tries its best to handle spaces embedded in file and directory names. But there still are instances where the use of spaces breaks the CVS functionality badly. So my recommendations are:

  1. Install CVSNT to a path that does not contain spaces.
  2. Place the repository on a path not containing spaces.
  3. If you install additional software like PERL or RCS, don't use spaces!
  4. Instruct your users not to use spaces in directory names that are to be handled by CVS.

People may argue that CVSNT handles these issues for them, but in my experience this is only partly true. For example the loginfo and notify script parsing breaks fully when handling files with embedded spaces. There are other places as well...
Also by allowing spaces you will make it impossible to later move the repository to a *nix system (Unix, Linux etc).
So you are much better off prohibiting spaces up front!

Afterwords

This tutorial is written 2005-11-16 and is based on CVSNT version 2.5.03.2148.
The test system is Windows Enterprise Server 2003 with SP1 installed running in Virtual PC 2004 SP1 on my development PC. The server is not member of a domain.

Comments? Send me a message: Bo Berglund

posted @ 2007-07-16 14:22 edsonjava 阅读(2310) | 评论 (0)编辑 收藏
 

2  Jive与设计模式

Jive论坛系统使用大量设计模式巧妙地实现了一系列功能。因为设计模式的通用性和可理解性,将帮助更多人很快地理解 Jive论坛源码,从而可以依据一种“协定”来动态地扩展它。那么使用设计模式还有哪些好处?

2.1  设计模式

设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的。设计模式使代码编制真正工程化,设计模式是软件工程的基石。

GOF(设计模式作者简称)《设计模式》这本书第一次将设计模式提升到理论高度,并将之规范化,该书提出了23种基本设计模式。自此,在可复用面向对象软件的发展过程中,新的大量的设计模式不断出现。

很多人都知道Java是完全面向对象的设计和编程语言,但是由于接受教育以及经验的原因,大多数程序员或设计人员都是从传统的过程语言转变而来,因此在思维习惯上要完全转变为面向对象的设计和开发方式是困难的,而学习设计模式可以更好地帮助和坚固这种转变。

凡是学习完成设计模式的人都有一种类似重生的感觉,这种重生可以从很多方面去解释。换一种新的角度来看待和解决问题应该是一种比较贴切的解释,而这种新的思维角度培养属于基础培训,因此,设计模式是学习Java的必读基础课程之一。

由于设计模式概念比较抽象,对于初学者学习有一定的难度,因此结合Jive论坛系统学习设计模式将是一种很好的选择。

掌握了设计模式,将会帮助程序员或设计人员以更加可重用性、可伸缩性的眼光来开发应用系统,甚至开发通用的框架系统。框架系统是构成一类特定软件可复用设计的一组相互协作的类,主要是对应用系统中反复重用部分的提炼,类似一种模板,这是一种结构性的模板。

框架通常定义了应用体系的整体结构、类和对象的关系等设计参数,以便于具体应用实现者能集中精力于应用本身的特定细节。框架强调设计复用,而设计模式最小的可重用单位,因此框架不可避免地会反复使用到设计模式。关于通用框架系统的设计开发将在以后章节中讨论。

其实Jive论坛本身也形成了一个基于Web结构的通用框架系统,因为它很多设计思想是可以重用的,例如设定 一个总体入口,通过入口检查用户的访问控制权限,当然还有其他各方面的功能实现方式都是值得在其他系统中借鉴的,也正因为它以模式的形式表现出来,这种可 重用性和可借鉴性就更强。

2.2  ForumFactory与工厂模式

工厂模式是GOF设计模式的主要常用模式,它主要是为创建对象提供了一种接口,工厂模式主要是封装了创建对象的细节过程,从而使得外界调用一个对象时,根本无需关心这个对象是如何产生的。

在GOF设计模式中,工厂模式分为工厂方法模式和抽象工厂模式。两者主要区别是,工厂方法是创建一种产品接口下的产品对象,而抽象工厂模式是创建多种产品接口下的产品对象,非常类似Builder生成器模式。在平时实践中,使用较多的基本是工厂方法模式。

以类SampleOne为例,要创建SampleOne的对象实例:

SampleOne sampleOne = new SampleOne();

如果Sample类有几个相近的类:SampleTwo或SampleThree,那么创建它们的实例分别是:

SampleTwo sampleTwo = new SampleTwo();

SampleThree sampleThree = new SampleThree();

其实这3个类都有一些共同的特征,如网上商店中销售书籍、玩具或者化妆品。虽然它们是不同的具体产品,但是它 们有一个共同特征,可以抽象为“商品”。日常生活中很多东西都可以这样高度抽象成一种接口形式。上面这3个类如果可以抽象为一个统一接口 SampleIF,那么上面语句就可以成为:

SampleIF sampleOne = new SampleOne();

SampleIF sampleTwo = new SampleTwo();

SampleIF sampleThree = new SampleThree();

在实际情况中,有时并不需要同时生成3种对象,而是根据情况在3者之中选一个。在这种情况下,需要使用工厂方法来完成了,创建一个叫SampleFactory的抽象类:

public class SampleFactory{

   public abstract SampleIF creator();

}

在这个抽象工厂类中有一个抽象方法creator,但是没有具体实现,而是延迟到它的子类中实现,创建子类SampleFactoryImp:

public class SampleFactoryImp extends SampleFactory{

   public SampleIF creator(){

    //根据其他因素综合判断返回具体产品

    //假设应该返回SampleOne对象

       return new SampleOne();

}

}

在SampleFactoryImp中根据具体情况来选择返回SampleOne、SampleTwo或SampleThree。所谓具体情况有很多种:上下文其他过程计算结果;直接根据配置文件中配置。

上述工厂方法模式中涉及到一个抽象产品接口Sample,如果还有其他完全不同的产品接口,如Product 等,一个子类SampleFactoryImp只能实现一套系列产品方案的生产,如果还需要另外一套系统产品方案,就可能需要另外一个子类 SampleFactoryImpTwo来实现。这样,多个产品系列、多个工厂方法就形成了抽象工厂模式。

前面已经讨论在Jive中设置了论坛统一入口,这个统一入口就是ForumFactory,以下是ForumFactory的主要代码:

public abstract class ForumFactory {

  private static Object initLock = new Object();

  private static String className = " com.Yasna.forum.database.DbForumFactory";

  private static ForumFactory factory = null;

  

  public static ForumFactory getInstance(Authorization authorization) {

    if (authorization == null) {

      return null;

    }

    //以下使用了Singleton 单态模式,将在2.3节讨论

    if (factory == null) {

      synchronized(initLock) {

        if (factory == null) {

            ... //从配置文件中获得当前className

          try {

              //动态装载类

              Class c = Class.forName(className);

              factory = (ForumFactory)c.newInstance();

          }

          catch (Exception e) {

              return null;

          }

        }

      }

    }

    //返回 proxy.用来限制授权对forum的访问

    return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));

  }

  //创键产品接口Forum的具体对象实例

  public abstract Forum createForum(String name, String description)

  throws UnauthorizedException, ForumAlreadyExistsException;

   //创键产品接口ForumThread的具体对象实例

public abstract ForumThread createThread(ForumMessage rootMessage)

throws UnauthorizedException;

//创键产品接口ForumMessage的具体对象实例

 

 

    public abstract ForumMessage createMessage();

  ....

}

ForumFactory中提供了很多抽象方法如createForum、createThread和 createMessage()等,它们是创建各自产品接口下的具体对象,这3个接口就是前面分析的基本业务对象Forum、ForumThread和 ForumMessage,这些创建方法在ForumFactory中却不立即执行,而是推迟到ForumFactory子类中实现。

ForumFactory的子类实现是 com.Yasna.forum.database.DbForumFactory,这是一种数据库实现方式。即在DbForumFactory中分别实 现了在数据库中createForum、createThread和createMessage()等3种方法,当然也提供了动态扩展到另外一套系列产品 的生产方案的可能。如果使用XML来实现,那么可以编制一个XmlForumFactory的具体工厂子类来分别实现3种创建方法。

因此,Jive论坛在统一入口处使用了抽象工厂模式来动态地创建论坛中所需要的各种产品,如图3-4所示。

图3-4  ForumFactory抽象工厂模式图

图3-4中,XmlForumFactory和DbForumFactory作为抽象工厂 ForumFactory的两个具体实现,而Forum、ForumThread和ForumMessage分别作为3个系列抽象产品接口,依靠不同的工 厂实现方式,会产生不同的产品对象。

从抽象工厂模式去理解Jive论坛统一入口处,可以一步到位掌握了几个类之间的大概关系。因为使用了抽象工厂模式这种通用的设计模式,可以方便源码阅读者快速地掌握整个系统的结构和来龙去脉,图3-4这张图已经初步展示了Jive的主要框架结构。

细心的读者也许会发现,在上面ForumFactory有一个getInstance比较令人费解,这将在2.3节进行讨论。


2.3  统一入口与单态模式

在上面ForumFactory的getInstance方法使用单态(SingleTon)模式。单态模式是保证一个类有且仅有一个对象实例,并提供一个访问它的全局访问点。

前面曾提到ForumFactory是Jive提供客户端访问数据库系统的统一入口。为了保证所有的客户端请求都要经过这个ForumFactory,如果不使用单态模式,客户端下列调用语句表示生成了ForumFactory实例:

ForumFactory factory = new DbForumFactory();

客户端每发生一次请求都调用这条语句,这就会发生每次都生成不同factory对象实例,这显然不符合设计要求,因此必须使用单态模式。

一般在Java实现单态模式有几种选择,最常用而且安全的用法如下:

public class Singleton {

  private Singleton(){}

  //在自己内部定义自己一个实例,是不是很奇怪

  //注意这是private,只供内部调用

  private static Singleton instance = new Singleton();

  //这里提供了一个供外部访问本class的静态方法,可以直接访问

  public static Singleton getInstance() {

    return instance;

  }

}

单态模式一共使用了两条语句实现:第一条直接生成自己的对象,第二条提供一个方法供外部调用这个对象,同时最好将构造函数设置为private,以防止其他程序员直接使用new Singleton生成实例。

还有一种Java单态模式实现:

public class Singleton {

  private Singleton(){}

  private static Singleton instance = null;

  public static synchronized Singleton getInstance() {

    if (instance==null)

      instance=new Singleton()

    return instance;

  }

在上面代码中,使用了判断语句。如果instance为空,再进行实例化,这成为lazy initialization。注意getInstance()方法的synchronized,这个synchronized很重要。如果没有 synchronized,那么使用getInstance()在第一次被访问时有可能得到多个Singleton实例。

关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论,有兴趣者可以进一步研究。一般认为第一种形式要更加安全些;但是后者可以用在类初始化时需要参数输入的情况下。

在Jive的ForumFactory中采取了后者lazy initialization形式,这是为了能够动态配置指定ForumFactory的具体子类。在getInstance中,从配置文件中获得当前工 厂的具体实现,如果需要启动XmlForumFactory,就不必修改ForumFactory代码,直接在配置文件中指定className的名字为 XmlForumFactory。这样通过下列动态装载机制生成ForumFactory具体对象:

Class c = Class.forName(className);

factory = (ForumFactory)c.newInstance();

这是利用Java的反射机制,可以通过动态指定className的数值而达到生成对象的方式。

使用单态模式的目标是为了控制对象的创建,单态模式经常使用在控制资源的访问上。例如数据库连接或 Socket连接等。单态模式可以控制在某个时刻只有一个线程访问资源。由于Java中没有全局变量的概念,因此使用单态模式有时可以起到这种作用,当然 需要注意是在一个JVM中。

2.4  访问控制与代理模式

仔细研究会发现,在ForumFactory的getInstance方法中最后的返回值有些奇怪。按照单态 模式的概念应该直接返回factory这个对象实例,但是却返回了ForumFactoryProxy的一个实例,这实际上改变了单态模式的初衷。这样客 户端每次通过调用ForumFactory的getInstance返回的就不是ForumFactory的惟一实例,而是新的对象。之所以这样做是为了 访问权限的控制,姑且不论这样做的优劣,先看看什么是代理模式。

代理模式是属于设计模式结构型模式中一种,它是实际访问对象的代理对象,或者影子对象,主要达到控制实际对象的访问。这种控制的目的很多,例如提高性能等。即远程代理模式,这种模式将在以后章节讨论。

其中一个主要的控制目的是控制客户端对实际对象的访问权限。在Jive系统中,因为有角色权限的分别,对于Forum、ForumThread和FroumMessage的访问操作必须经过权限机制验证后才能进行。

以ForumFactoryProxy中的createForum方法为例,其实ForumFactoryProxy也是FroumFactory的一种工厂实现,它的createForum具体实现如下:

public Forum createForum(String name, String description)

            throws UnauthorizedException, ForumAlreadyExistsException

    {

        if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {

            Forum newForum = factory.createForum(name, description);

            return new ForumProxy(newForum, authorization, permissions);

        }

        else {

            throw new UnauthorizedException();

        }

}

在这个方法中进行了权限验证,判断是否属于系统管理员。如果是,将直接从DbForumFactory对象 factory的方法createForum中获得一个新的Forum对象,然后再返回Forum的子类代理对象ForumProxy。因为在Forum 中也还有很多属性和操作方法,这些也需要进行权限验证。ForumProxy和ForumFactoryProxy起到类似的作用。

Jive中有下列几个代理类:

·          ForumFactoryProxy:客户端和DbForumFactory之间的代理。客户端访问DbForumFactory的任何方法都要先经过ForumFactoryProxy相应方法代理一次。以下意思相同。

·          ForumProxy:客户端和DbForum之间的代理,研究Forum对象的每个方法,必须先看ForumProxy对象的方法。

·          ForumMessageProxy:客户端和DbForumMessage之间的代理。

·          ForumThreadProxy:客户端和DbForumThread之间的代理。

User和Group也有相应的代理类。

由以上分析看出,每个数据对象都有一个代理。如果系统中数据对象非常多,依据这种一对一的代理关系,会有很多代理类,将使系统变得不是非常干净,因此可以使用动态代理来代替这所有的代理类,具体实现将在以后章节讨论。

2.5  批量分页查询与迭代模式

迭代(Iterator)模式是提供一种顺序访问某个集合各个元素的方法,确保不暴露该集合的内部表现。迭代模式应用于对大量数据的访问,Java Collection API中Iterator就是迭代模式的一种实现。

在前面章节已经讨论过,用户查询大量数据,从数据库不应该直接返回ResultSet,应该是 Collection。但是有一个问题,如果这个数据很大,需要分页面显示。如果一下子将所有页面要显示的数据都查询出来放在Collection,会影 响性能。而使用迭代模式则不必将全部集合都展现出来,只有遍历到某个元素时才会查询数据库获得这个元素的数据。

以论坛中显示帖子主题为例,在一个页面中不可能显示所有主题,只有分页面显示,如图3-5所示。

图3-5中一共分15页来显示所有论坛帖子,可以从显示Forum.jsp中发现下列语句可以完成上述结果:

ResultFilter filter = new ResultFilter();  //设置结果过滤器

   filter.setStartIndex(start);             //设置开始点

   filter.setNumResults(range);          //设置范围

   ForumThreadIterator threads = forum.threads(filter);  //获得迭代器

   while(threads.hasNext){

       //逐个显示threads中帖子主题,输出图3-5中的每一行

   }

图3-5  分页显示所有帖子

上述代码中主要是从Forum的threads方法获得迭代器ForumThreadIterator的实 例,依据前面代理模式中分析、研究Forum对象的方法,首先是看ForumProxy中对应方法,然后再看DbForum中对应方法的具体实现。在 ForumProxy中,threads方法如下:

public ForumThreadIterator threads(ResultFilter resultFilter) {

     ForumThreadIterator iterator = forum.threads(resultFilter);

      return new ForumThreadIteratorProxy(iterator, authorization, permissions);

}

首先是调用了DbForum中具体的threads方法,再追踪到DbForum中看看,它的threads方法代码如下:

public ForumThreadIterator threads(ResultFilter resultFilter) {

//按resultFilter设置范围要求获得SQL查询语句

   String query = getThreadListSQL(resultFilter, false); 

   //获得resultFilter设置范围内的所有ThreadID集合

   long [] threadBlock = getThreadBlock(query.toString(), resultFilter.getStartIndex());

   //以下是计算查询区域的开始点和终点

   int startIndex = resultFilter.getStartIndex();

   int endIndex;

   // If number of results is set to inifinite, set endIndex to the total

  // number of threads in the forum.

   if (resultFilter.getNumResults() == ResultFilter.NULL_INT) {

     endIndex = (int)getThreadCount(resultFilter);

   }else {

     endIndex = resultFilter.getNumResults() + startIndex;

   }

   return new ForumThreadBlockIterator(threadBlock, query.toString(),

               startIndex, endIndex, this.id, factory);

}

ResultFilter是一个查询结果类,可以对论坛主题Thread和帖子内容Message进行过滤或 排序,这样就可以根据用户要求定制特殊的查询范围。如查询某个用户去年在这个论坛发表的所有帖子,那只要创建一个ResultFilter对象就可以代表 这个查询要求。

在上面threads方法代码中,第一步是先定制出相应的动态SQL查询语句,然后使用这个查询语句查询数据库,获得查询范围内所有的ForumThread的ID集合,然后在这个ID集合中获得当前页面的ID子集合,这是非常关键的一步。

在这关键的一步中,有两个重要的方法getThreadListSQL和getThreadBlock:

·          GetThreadListSQL:获得SQL查询语句query的值,这个方法Jive实现起来显得非常地琐碎。

·          GetThreadBlock:获得当前页面的ID子集合,那么如何确定ID子集合的开始位置呢?查看getThreadBlock方法代码,可以发现,它是使用最普遍的ResultSet next()方法来逐个跳跃到开始位置。

上面代码的Threads方法中最后返回的是ForumThreadBlockIterator,它是抽象类 ForumThreadIterator的子类,而ForumThreadIterator继承了Collection的Iterator,以此声明自己 是一个迭代器,ForumMessageBlockIterator实现的具体方法如下:

public boolean hasNext();     //判断是否有下一个元素

public boolean hasPrevious()  //判断是否有前一个元素

public Object next() throws java.util.NoSuchElementException  //获得下一个元素实例

ForumThreadBlockIterator中的Block是“页”的意思,它的一个主要类变量 threadBlock包含的是一个页面中所有ForumThread的ID,next()方法实际是对threadBlock中ForumThread 进行遍历,如果这个页面全部遍历完成,将再获取下一页(Block)数据。

在ForumThreadBlockIterator重要方法getElement中实现了两个功能:

·          如果当前遍历指针超过当前页面,将使用getThreadBlock获得下一个页面的ID子集合;

·          如果当前遍历指针在当前页面之内,根据ID获得完整的数据对象,实现输出;

ForumThreadBlockIterator的getElement方法代码如下:

private Object getElement(int index) {

   if (index < 0) {        return null;        }

   // 检查所要获得的 element 是否在本查询范围内(当前页面内)

   if (index < blockStart ||

index >= blockStart + DbForum.THREAD_BLOCK_SIZE) {  

      try {

          //从缓冲中获得Forum实例

          DbForum forum = factory.cacheManager.forumCache.get(forumID);

          //获得下一页的内容

          this.threadBlock = forum.getThreadBlock(query, index);

          this.blockID = index / DbForum.THREAD_BLOCK_SIZE;

          this.blockStart = blockID * DbForum.THREAD_BLOCK_SIZE;

      } catch (ForumNotFoundException fnfe) {

               return null;

       }

     }

     Object element = null;

     // 计算这个元素在当前查询范围内的相对位置

     int relativeIndex = index % DbForum.THREAD_BLOCK_SIZE;

     // Make sure index isn't too large

     if (relativeIndex < threadBlock.length) {

        try {

            // 从缓冲中获得实际thread 对象

            element = factory.cacheManager.threadCache.get(

                        threadBlock[relativeIndex]);

        } catch (ForumThreadNotFoundException tnfe) { }

     }

     return element;

}

ForumThreadBlockIterator是真正实现分页查询的核心功能, ForumThreadBlockIterator对象返回到客户端的过程中,遭遇ForumThreadIteratorProxy的截获,可以回头看 看ForumProxy中的threads方法,它最终返回给调用客户端Forum.jsp的是ForumThreadIteratorProxy实例。

ForumThreadIteratorProxy也是迭代器ForumThreadIterator的一个子类,它的一个具体方法中:

public Object next() {

  return new ForumThreadProxy((ForumThread)iterator.next(), authorization,

            permissions);

}

这一句是返回一个ForumThreadProxy实例,返回就是一个ForumThread实例的代理。这里,Jive使用代理模式实现访问控制实现得不是很巧妙,似乎有代理到处“飞”的感觉,这是可以对之进行改造的。

从以上可以看出,Jive在输出如图3-5所示的多页查询结果时,采取了下列步骤:

(1)先查询出符合查询条件的所有对象元素的ID集合,注意不是所有对象元素,只是其ID的集合,这样节约了大量内存。

(2)每个页面视为一个Block,每当进入下一页时,获得下一个页面的所有对象的ID集合。

(3)输出当前页面的所有对象时,首先从缓冲中获取,如果缓冲中没有,再根据ID从数据库中获取完整的对象数据。

上述实现方法完全基于即查即显,相比于一般批量查询做法:一次性获得所有数据,然后遍历数据结果集ResultSet,Jive这种批量查询方式是一种比较理想的选择。

以上是ForumThread的批量显示,有关帖子内容ForumMessage也是采取类似做法。在每个 ForumThread中可能有很多帖子内容(ForumMessage对象集合),也不能在一个页面中全部显示,所以也是使用迭代模式来实现的。显示一 个Forum主题下所有帖子内容的功能由ForumThread的messages()方法完成,检查它的代理类FroumThreadProxy如何具 体完成:

public Iterator messages(ResultFilter resultFilter) {

   Iterator iterator = thread.messages(resultFilter);

   return new IteratorProxy(JiveGlobals.MESSAGE, iterator, authorization, permissions);

}

实现的原理基本相同,返回的都是一个Iterator代理类,在这些代理类中都是进行用户权限检验的。

Jive中也有关于一次性获得所有数据,然后遍历ResultSet的做法。这种做法主要适合一次性查询数据库的所有数据,例如查询当前所有论坛Forum,首先实现SQL语句:

SELECT forumID FROM jiveForum

获得所有Forum的forumID,这段代码位于DbForumFactory.java的forums方法中,如下:

  public Iterator forums() {

    if (forums == null) {

      LongList forumList = new LongList();

      Connection con = null;

      PreparedStatement pstmt = null;

      try {

        con = ConnectionManager.getConnection();

        // GET_FORUMS值是SELECT forumID FROM jiveForum

        pstmt = con.prepareStatement(GET_FORUMS);

        ResultSet rs = pstmt.executeQuery();

        while (rs.next()) {

          forumList.add(rs.getLong(1));                 //将所有查询ID结果放入forumList中

        }

      }catch (SQLException sqle) {

        sqle.printStackTrace();

      } finally {

        …

    }

    return new DatabaseObjectIterator(JiveGlobals.FORUM, forums, this);

  }

forums方法是返回一个DatabaseObjectIterator,这个 DatabaseObjectIterator也是一个迭代器,但是实现原理要比ForumThreadBlockIterator简单。它只提供了一个 遍历指针,在所有ID结果集中遍历,然后也是通过ID获得完整的数据对象。

总之,Jive中关于批量查询有两种实现方式:以ForumThreadBlockIterator为代表的实现方式适合在数据量巨大、需要多页查询时使用;而DatabaseObjectIterator则是推荐在一个页面中显示少量数据时使用。

2.6  过滤器与装饰模式

装饰(Decorator)模式是动态给一个对象添加一些额外的职责,或者说改变这个对象的一些行为。这就类似于使用油漆为某个东西刷上油漆,在原来的对象表面增加了一层外衣。

在装饰模式中,有两个主要角色:一个是被刷油漆的对象(decoratee);另外一个是给decoratee刷油漆的对象(decorator)。这两个对象都继承同一个接口。

首先举一个简单例子来说明什么是装饰模式。

先创建一个接口:

public interface Work

{

  public void insert();

}

这是一种打桩工作的抽象接口,动作insert表示插入,那么插入什么?下面这个实现表示方形木桩的插入:

public class SquarePeg implements Work{

  public void insert(){

    System.out.println("方形桩插入");

  }

}

本来这样也许就可以满足打桩的工作需要,但是有可能土质很硬,在插入方形桩之前先要打一个洞,那么又将如何实现?可以编制一个Decorator类,同样继承Work接口,但是在实现insert方法时有些特别:

public class Decorator implements Work{

  private Work work;

  //额外增加的功能被打包在这个List中

  private ArrayList others = new ArrayList();

  public Decorator(Work work)

  {

    this.work=work;

    others.add("打洞");   //准备好额外的功能

  }

  public void insert(){

    otherMethod();

    work.insert();

  }

  public void otherMethod()

  {

    ListIterator listIterator = others.listIterator();

    while (listIterator.hasNext())

    {

      System.out.println(((String)(listIterator.next())) + " 正在进行");

    }

}

}

在Decorator的方法insert中先执行otherMethod()方法,然后才实现SquarePeg的insert方法。油漆工Decorator给被油漆者SquarePeg添加了新的行为——打洞。具体客户端调用如下:

Work squarePeg = new SquarePeg();

Work decorator = new Decorator(squarePeg);

decorator.insert();

本例中只添加了一个新的行为(打洞),如果还有很多类似的行为,那么使用装饰模式的优点就体现出来了。因为可以通过另外一个角度(如组织新的油漆工实现子类)来对这些行为进行混合和匹配,这样就不必为每个行为创建一个类,从而减少了系统的复杂性。

使用装饰模式可以避免在被油漆对象decoratee中包装很多动态的,可能需要也可能不需要的功能,只要在系统真正运行时,通过油漆工decorator来检查那些需要加载的功能,实行动态加载。

Jive论坛实现了信息过滤功能。例如可以将帖子内容中的HTML语句过滤掉;可以将帖子内容中Java代码 以特别格式显示等。这些过滤功能有很多,在实际使用时不一定都需要,是由实际情况选择的。例如有的论坛就不需要将帖子内容的HTML语句过滤掉,选择哪些 过滤功能是由论坛管理者具体动态决定的。而且新的过滤功能可能随时可以定制开发出来,如果试图强行建立一种接口包含所有过滤行为,那么到时有新过滤功能加 入时,还需要改变接口代码,真是一种危险的行为。

装饰模式可以解决这种运行时需要动态增加功能的问题,且看看Jive是如何实现的。

前面讨论过,在Jive中,有主要几个对象ForumFactory、Forum以及ForumThread 和ForumMessage,它们之间的关系如图3-2所示。因此帖子内容ForumMessage对象的获得是从其上级FroumThread的方法 getMessage中获取,但是在实际代码中,ForumThread的方法getMessage委托ForumFactory来获取 ForumMessage对象。看看ForumThread的子类DbForumThread的getMessage代码:

public ForumMessage getMessage(long messageID)

         throws ForumMessageNotFoundException

{

     return factory.getMessage(messageID, this.id, forumID);

}

这是一种奇怪的委托,大概是因为需要考虑到过滤器功能有意为之吧。那就看看ForumFactory的具体实 现子类DbForumFactory的getMessage功能,getMessage是将数据库中的ForumMessage对象经由过滤器过滤一遍后 输出(注:因为原来的Jive的getMessage代码考虑到可缓存或不可缓存的过滤,比较复杂,实际过滤功能都是可以缓存的,因此精简如下)。

protected ForumMessage getMessage(long messageID, long threadID, long forumID)

            throws ForumMessageNotFoundException

{

        DbForumMessage message = cacheManager.messageCache.get(messageID);

        // Do a security check to make sure the message comes from the thread.

        if (message.threadID != threadID) {

            throw new ForumMessageNotFoundException();

        }

        ForumMessage filterMessage = null;

            try {

  // 应用全局过滤器

     filterMessage = filterManager.applyFilters(message);

                Forum forum = getForum(forumID);               

                //应用本论坛过滤器

                filterMessage = forum.getFilterManager().applyFilters(filterMessage);

            }

            catch (Exception e) { }

        return filterMessage;

}

上面代码实际是装饰模式的客户端调用代码,DbForumMessage 的实例message是被油漆者decoratee。通过filterManager 或forum.getFilterManager()的applyFilter方法,将message实行了所有的过滤功能。这就类似前面示例的下列语 句:

Work decorator = new Decorator(squarePeg);

forum.getFilterManager()是从数据库中获取当前配置的所有过滤器类。每个Forum都有一套自己的过滤器类,这是通过下列语句实现的:

FilterManager filterManager =  new DbFilterManager();

在DbFilterManager 的类变量ForumMessageFilter [] filters中保存着所有的过滤器,applyFilters方法实行过滤如下:

public ForumMessage applyFilters(ForumMessage message) {

    for (int i=0; i < filters.length; i++) {

         if (filters[i] != null) {

            message = filters[i].clone(message);

         }

     }

     return message;

}

而ForumMessageFilter是ForumMessage的另外一个子类,被油漆者DbForumMessage通过油漆工ForumMessageFilter增加了一些新的行为和功能(过滤),如图3-6所示。

图3-6  装饰模式

这就组成了一个稍微复杂一点的装饰模式。HTMLFilter实现了HTML代码过滤功能,而JavaCodeHighLighter实现了Java代码过滤功能,HTMLFilter代码如下:

public class HTMLFilter extends ForumMessageFilter {

    public ForumMessageFilter clone(ForumMessage message){

        HTMLFilter filter = new HTMLFilter();

        filter.message = message;

        return filter;

    }

    public boolean isCacheable() {

        return true;

    }

    public String getSubject() {

        return StringUtils.escapeHTMLTags(message.getSubject());

    }

    public String getBody() {

        return StringUtils.escapeHTMLTags(message.getBody());

    }

}

HTMLFilter中重载了ForumMessage的getSubject()、getBody()方法,实际是改变了这两个原来的行为,这类似前面举例的方法:

public void insert(){

    otherMethod();

    work.insert();

}

这两者都改变了被油漆者的行为。

在HTMLFilter中还使用了原型(Prototype)模式,原型模式定义是:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。按照这种定义,Java的clone技术应该是原型模式的一个实现。

HTMLFilter的clone方法实际就是在当前HTMLFilter实例中再生成一个同样的实例。这样 在处理多个并发请求时,不用通过同一个过滤器实例进行处理,提高了性能。但是HTMLFilter的clone方法是采取new方法来实现,不如直接使用 Object的native方法速度快。

因为在DbFilterManager中是根据配置使用类反射机制动态分别生成包括HTMLFilter在内的过滤器实例。但是每种过滤器实例只有一个,为了使得大量用户不必争夺一个过滤器实例来实现过滤,就采取了克隆方式,这种实战手法可以借鉴在自己的应用系统中。

2.7  主题监测与观察者模式

观察者(Observer)模式是定义对象之间一对多的依赖关系,当一个被观察的对象发生改变时,所有依赖于它的对象都会得到通知并采取相应行为。

使用观察者模式的优点是将被观察者和观察者解耦,从而可以不影响被观察者继续自己的行为动作。观察者模式适合应用于一些“事件触发”场合。

在Jive中,用户也许会对某个主题感兴趣,希望关于此主题发生的任何新的讨论能通过电子邮件通知他,因此他订阅监视了这个主题。因为这个功能的实现会引入电子邮件的发送。在前面章节已经讨论了电子邮件发送有可能因为网络原因延迟,如果在有人回复这个主题时,立即进行电子邮件发送,通知所有订阅该主题的用户。那么该用户可能等待很长时间得不到正常回应。

使用观察者模式,可以通过触发一个观察者,由观察者通过另外线程来实施邮件发送,而被观察者发出触发通知后,可以继续自己原来的逻辑行为。

看看Jive的WatchManager类:

public interface WatchManager {

    //正常监察类型,用户在这个主题更新后再次访问时,会明显地发现

    public static final int NORMAL_WATCH = 0;

     // 当主题变化时,通过电子邮件通知用户

    public static final int EMAIL_NOTIFY_WATCH = 1;

    //设置一个主题被观察的时间,默认为30天

    public void setDeleteDays(int deleteDays) throws UnauthorizedException;

    public int getDeleteDays();

    //是否激活了E-mail提醒

    public boolean isEmailNotifyEnabled() throws UnauthorizedException;

    public void setEmailNotifyEnabled(boolean enabled) throws UnauthorizedException;

    //保存E-mail的内容

    public String getEmailBody() throws UnauthorizedException;

    public void setEmailBody(String body) throws UnauthorizedException;

    //保存E-mail的主题

    public String getEmailSubject() throws UnauthorizedException;

public void setEmailSubject(String subject) throws UnauthorizedException;

    …

 

    //为某个主题创建一个观察者

    public void createWatch(User user, ForumThread thread, int watchType)

            throws UnauthorizedException;

    //删除某个主题的观察者

    public void deleteWatch(User user, ForumThread thread, int watchType)

    //得到一个主题的所有观察者

    public Iterator getWatchedForumThreads(User user, int watchType)

            throws UnauthorizedException;

    //判断一个用户是否在观察监视该主题

    public boolean isWatchedThread(User user, ForumThread thread, int watchType)

            throws UnauthorizedException;

    …

}

DbWatchManager是WatchManager的一个子类,通过数据库保存着有关某个主题被哪些用户监视等数据资料。WatchManager对象是随同DbForumFactory()一起生成的。

在DbWatchManager中有一个WatchManager没有的很重要的方法——通知方法:

protected void notifyWatches(ForumThread thread) {

     //If watches are turned on.

    if (!emailNotifyEnabled) {

            return;

     }

     //通知所有观察这个主题的用户

     EmailWatchUpdateTask task = new EmailWatchUpdateTask(this, factory, thread);

     TaskEngine.addTask(task);

 }

这个方法用来触发所有有关这个主题的监视或订阅用户,以E-mail发送提醒他们。那么这个通知方法本身又是如何被触发的?从功能上分析,应该是在发表新帖子时触发。

在DbForumThread的addMessage的最后一行有一句:

factory.watchManager.notifyWatches(this);

这其实是调用了DbWatchManager的notifyWatches方法,因此确实是在增加新帖子时触发了该帖子的所有观察者。

notifyWatches方法中在执行E-mail通知用户时,使用了TaskEngine来执行E- mail发送。E-mailWatchUpdateTask是一个线程类,而TaskEngine是线程任务管理器,专门按要求启动如E- mailWatchUpdateTask这样的任务线程。其实TaskEngine是一个简单的线程池,它不断通过查询Queue是否有可运行的线程,如 果有就直接运行线程。

public class TaskEngine {

    //任务列表

    private static LinkedList taskList = null;

    //工作数组

    private static Thread[] workers = null;

    private static Timer taskTimer = null;

    private static Object lock = new Object();

 

    static {

        //根据配置文件初始化任务启动时间

        taskTimer = new Timer(true);

        // 默认使用7个线程来装载启动任务

        workers = new Thread[7];

        taskList = new LinkedList();

        for (int i=0; i<workers.length; i++) {

             // TaskEngineWorker是个简单的线程类

            TaskEngineWorker worker = new TaskEngineWorker();

            workers[i] = new Thread(worker);

            workers[i].setDaemon(true);

            workers[i].start();         //启动TaskEngineWorker这个线程

        }

    }

    //TaskEngineWorker内部类

    private static class TaskEngineWorker implements Runnable {

        private boolean done = false;

        public void run() {

            while (!done) {

                //运行nextTask方法

                nextTask().run();

            }

        }

    }

    // nextTask()返回的是一个可运行线程,是任务列表Queue的一个读取者

    private static Runnable nextTask() {

        synchronized(lock) {

            // 如果没有任务,就锁定在这里

            while (taskList.isEmpty()) {

                try {

                    lock.wait();        //等待解锁

                } catch (InterruptedException ie) { }

            }

            //从任务列表中取出第一个任务线程

            return (Runnable)taskList.removeLast();

        }

    }

    public static void addTask(Runnable r) {

        addTask(r, Thread.NORM_PRIORITY);

    }

    //这是任务列表Queue的生产者

    public static void addTask(Runnable task, int priority) {

        synchronized(lock) {

            taskList.addFirst(task);

            //提醒所有锁在lock这里的线程可以运行了

            //这是线程的互相通知机制,可参考线程参考资料

            lock.notifyAll();

        }

    }

    …

}

在TaskEngine中启动设置了一个消息管道Queue和两个线程。一个线程是负责向Queue里放入 Object,可谓是消息的生产者;而另外一个线程负责从Queue中取出Object,如果Queue中没有Object,那它就锁定(Block)在 那里,直到Queue中有Object,因为这些Object本身也是线程,因此它取出后就直接运行它们。

这个TaskEngine建立的模型非常类似JMS(Java消息系统),虽然它们功能类似,但不同的是: JMS是一个分布式消息发布机制,可以在多台服务器上运行,处理能力要强大得多。而TaskEngine由于基于线程基础,因此不能跨JVM实现。可以说 TaskEngine是一个微观组件,而JMS则是一个宏观架构系统。JMS相关讨论将在后面章节进行。

以上讨论了Jive系统中观察者模式的实现,Jive使用线程比较基础的概念实现了观察者模式,当然有助于了解J2EE很多底层的基础知识,整个Web容器的技术实现就是基于线程池原理建立的。

Java的JDK则提供了比较方便的观察者模式API——java.util.Observable和java.util.Observer,它们的用户非常简单,只要被观察者继承Observable,然后使用下列语句设置观察点:

setChanged();

notifyObservers(name); //一旦执行本代码,就触发观察者了

而观察者只要实现Observer接口,并实现update方法,在update方法中将被观察者触发后传来的object进行处理。举例如下:

网上商店中商品价格可能发生变化,如果需要在价格变化时,首页能够自动显示这些降价产品,那么使用观察者模式将方便得多。首先,商品是一个被观察者:

public class product extends Observable{

  private float price;

  public float getPrice(){ return price;}

  public void setPrice(){

   this.price=price;

 //商品价格发生变化,触发观察者

   setChanged();

   notifyObservers(new Float(price));

  }

  ...

}

价格观察者实现observer接口:

public class PriceObserver implements Observer{

  private float price=0;

  public void update(Observable obj,Object arg){

    if (arg instanceof Float){

     price=((Float)arg).floatValue();

     System.out.println("PriceObserver :price changet to "+price);

    }

  }

}

这样,一个简单的观察者模式就很容易地实现了。

posted @ 2007-05-18 18:46 edsonjava 阅读(693) | 评论 (0)编辑 收藏
 

前言

Jive是一个开放的Java源代码项目。其目标是建设一个开放结构的,强壮的,易于扩展的基于JSP的论坛。在其设计目标的指导下,其结构设计得非常得好,融合了很多新的观念,比如Design Pattern,可更换的Skin,可插入Plug等等。详细解读其源代码对于理解这些新的设计上的概念是很有裨益的。如果你对Design Pattern和Java语言有一定的了解,但是还是会时常迷惑于其中的话,不妨研究研究Jive源代码,一定会对其中的很多概念有更深入的理解。这篇文章源于我的Jive源代码研究笔记,希望能够提纲挈领,带领大家进入到这个美好的世界。当然,如果没有时间仔细地看源代码的话,看看这篇文章,我想也是会有一些帮助的。

再开始之前,需要指出的是,Jive中对Design Pattern的应用,并没有拘礼与GOF书中所给出的实现方法,而是有许多变通的地方。一方面,我想是由于具体的实际需要,另一方面,我想这也是设计观念进化的结果吧。因而,这些变通的地方,将是我讲解的重点。





回页首


整体结构概叙

基于一个OO的设计原则:面向接口编程,而不是针对实现编程。Jive在设计的时候,把其大部分的基本对象都设计为接口或者抽象类。在Jive中,基本的接口有Forum,ForumMessage,ForumThread,Group,User,Authorization和Query。我们可以很容易的从这些接口的名字来知道他们的功用,下面的类图给出了这些类之间的一些静态关系:



图1:Jive整体关系
图1:Jive整体关系

你可能会有疑问,为什么会都是接口呢?这是基于扩展性考虑的。在Jive给出的实现中,所有的这些接口,Forum,ForumMessage,User等等,都使用数据库来实现的,一条消息,或者一个用户对应于数据库中的一条消息Jive使用了DbForum,DbForumMessage,DbUser等类来实现这些接口,通过JDBC来操作数据库,使之作为论坛的底层支撑。

然而,有时候,或许我们并不想使用数据库,比如我们想只是使用文件系统来作为论坛的底层支撑,这时候,我们需要做的只是编码实现了Forum等等接口的诸如FileFroum,FileForumMessage等对象,然后嵌入Jive中即可,原有的任何代码都可以不用改变!!!这就是面向接口编程的威力了!

下面来看看具体的设计和编码。






AbstractFactory模式和可扩展性

如果要实现较好的可扩展性,AbstractFactory模式确实是一件利器。如上面所说,如果要创建的Forum接口的不同实现,而又不想更改代码的话,就需要用到抽象工厂了。再Jive中,AuthorizationFactory类是一个抽象类,用来创建Authorization对象。这是一个抽象工厂,可以通过不同的子类来创建不同的Authorization对象。这个工厂的实现方法是:

在AuthorizationFactory中使用一个private static变量factory,用来引用具体的抽象工厂的实例:
private static AuthorizationFactory factory = null;

用一个private static的String,来指明具体的抽象工厂的子类类名:
private static String className ="com.coolservlets.forum.database.DbAuthorizationFactory";

然后是用一个private static的loadAuthorizationFactory方法来给这个factory变量赋值,生成具体的抽象工厂类:

   private static void loadAuthorizationFactory() {
            if (factory == null) {
            synchronized(className) {
            if (factory == null) {
            String classNameProp = PropertyManager.getProperty(
            "AuthorizationFactory.className"
            );
            if (classNameProp != null) {
            className = classNameProp;
            }
            try {
            Class c = Class.forName(className);
            factory = (AuthorizationFactory)c.newInstance();
            }
            catch (Exception e) {
            System.err.println("Exception loading class: " + e);
            e.printStackTrace();
            }
            }
            }
            }
            }

在static的getAuthorization方法返回一个Authorization的过程中,先初始化工厂类factory变量,然后用factory的createAuthorization方法来创建:

   public static Authorization getAuthorization(String username,
            String password) throws UnauthorizedException
            {
            loadAuthorizationFactory();
            return factory.createAuthorization(username, password);
            }

不同的子类有不同的createAuthorization方法的实现。比如在DbAuthorizationFactory这个AuthorizationFactory的数据库实现子类中,createAuthorization方法是这样实现的:

   public Authorization createAuthorization(String username, String password)
            throws UnauthorizedException
            {
            if (username == null || password == null) {
            throw new UnauthorizedException();
            }
            password = StringUtils.hash(password);
            int userID = 0;
            Connection con = null;
            PreparedStatement pstmt = null;
            try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(AUTHORIZE);
            pstmt.setString(1, username);
            pstmt.setString(2, password);
            ResultSet rs = pstmt.executeQuery();
            if (!rs.next()) {
            throw new UnauthorizedException();
            }
            userID = rs.getInt(1);
            }
            catch( SQLException sqle ) {
            System.err.println("Exception in DbAuthorizationFactory:" + sqle);
            sqle.printStackTrace();
            throw new UnauthorizedException();
            }
            finally {
            try {  pstmt.close(); }
            catch (Exception e) { e.printStackTrace(); }
            try {  con.close();   }
            catch (Exception e) { e.printStackTrace(); }
            }
            return new DbAuthorization(userID);
            }

在这个类中,可以看到抽象类和具体的子类之间的关系,它们是如何协作的,又是如何划分抽象方法和非抽象方法的,这都是值得注意的地方。一般的,抽象方法需要子类来实现,而抽象类中的非抽象方法应该所有子类所能够共享的,或者可是说,是定义在抽象方法之上的较高层的方法。这确实是一个抽象工厂的好例子!虽然实现的方法已经和GOF中给出的实现相差较远了,但思想没变,这儿的实现,也确实是要巧妙的些。

还有就是静态方法的使用,使得这个类看起来有些Singleton的意味。这使得对于AbstractFactory的创建变得简单。

下面的类图给出了这个AbstractFactory的实现的总体情况:



图2:AbstractFactory模式的实现类图
图2:AbstractFactory模式的实现类图

在AuthorizationFactory中定义的其它方法,涉及到具体的如何创建Authorization,都是作为abstract方法出现,具体实现留给子类来完成。

这样,在需要生成一个Authorization的时候,只需要调用AuthorizationFactory的静态方法getAuthorization就可以了,由子类实现了具体的细节。

其它的,如同上面讲到的,在创建Forum的时候用的ForumFactory,具有同上面一样的实现,这就是模式之所以称为模式的所在了。





回页首


Proxy模式和权限控制

Proxy模式的功能有很多,比如远程代理,用来给远程对象提供一个本地代表;虚代理,用来为创建开大开销的对象提供缓冲,等等。在Jive中使用的是保护代理,为被保护的对象提供权限控制。

我们都知道在一个论坛中,权限的控制是必须的,否则论坛就很可能会被搞得一团糟。Jive中引入Proxy对象,Authorization接口以及权限描叙属类来提供对论坛的保护。

以ForumFactory为例,一个额外的ForumFactoryProxy来处理权限认证的工作,它为某一个ForumFactory提供了一个代理,保证只有授权的用户才能够存取ForumFactory的某些操作。实际上ForumFactory在这儿不仅仅只是一个生成Forum的类的,它更像是一个Forum的管理类。提供了添加,删除,枚举等等一系列的功能,而有些功能不是什么样的人都可以使用的,因而引入了另外的一个代理类来处理权限的问题。

当然,代理类需要继承ForumFactory,以使方法签名一致: ForumFactoryProxy extends ForumFactory

在它的构造方法中,就提供了一个ForumFactory对象,这是需要被代理的对象;一个Authorization对象,提供用户信息;还有一个ForumPermissions,提供认证信息:

   public ForumFactoryProxy(ForumFactory factory, Authorization authorization,
            ForumPermissions permissions)
            {
            this.factory = factory;
            this.authorization = authorization;
            this.permissions = permissions;
            }

一般的代理过程都是这样的,在访问某个方法之前,必须接受权限的检查,以createForum为例:

   public Forum createForum(String name, String description)
            throws UnauthorizedException, ForumAlreadyExistsException
            {
            if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {
            Forum newForum = factory.createForum(name, description);
            return new ForumProxy(newForum, authorization, permissions);
            }
            else {
            throw new UnauthorizedException();
            }
            }

下面给出这个模式的类图:



图3:Proxy模式的类图
图3:Proxy模式的类图

这个模式的实现基本上和GOF中所给出的实现一致。在Jive中,几乎所有的接口,Forum,ForumMessage,ForumThread等等,都会有一个相应的Proxy对象来进行权限控制。而在创建具体的对象的时候,都是用相应的Proxy对象来代替原有的对象返回的。例如在ForumFactory的getInstance()方法中需要返回一个Forum的时候,Jive是这样做的:

public static ForumFactory getInstance(Authorization authorization) {
            ......
            ForumFactoryProxy proxy = new ForumFactoryProxy(factory,authorization, factory.getPermissions(authorization));
            return proxy;
            }

因而,所有被创建的对象实际上都是Proxy对象,抽象工厂保证了没有权限验证的对象根本不会客户所得到,它们只会在Proxy的内部扮演角色,而永远不会被外部对象所存取,这样,就从根本上保证了论坛的安全。





回页首


Decorator模式和过滤器

一般的在OO设计中,而外功能的添加是通过继承来实现的,但是继承有的时候不够灵活,而且当功能的组合很多的时候,继承的子类就会成几何级数增长,使得类多的难以控制。正是基于这样的考虑,Decorator模式得以诞生。

Decorator模式相当于封装了某个特定的操作,当某个对象需要这个操作的时候,加上这个Decorator即可。并且,多个Decorator还可以组合,以提供更多的功能。

在Jive中,Decorator模式应用在一些过滤器(Filter)中。Filter提供对ForumMessage对象内容的重新构造。比如,当一个ForumMessage对象流过一个名为FilterCodeHighlight的过滤器后,存在于消息中的所有Java源代码文本,会被重新构造为具有语法高亮显示的消息。在比如,当经过了语法高亮修饰的消息再流过一个名为FilterHtml的过滤器后,消息中的HTML片断会被注释可以在HTML内部显示文本,这样就防止了用户输入了HTML控制标签后,使得页面显示不正常的问题。

Jive中,所有的过滤器继承于一个抽象类ForumMessageFilter,而ForumMessageFilter又实现了ForumMessage接口。也就是说,每一个过滤器实际上也是一个ForumMessage对象。

ForumMessageFilter中还封装一个ForumMessage对象。进行过滤的方法很简单,使用的是getBody(),比如在FilterCodeHighlight这个类中:

   public String getBody() {
            return highlightCode(message.getBody());
            }

highlightCode是一个private方法,实施具体的过滤的细节。getBody()方法实际上是定义在ForumMessage接口中的,当调用过滤器的getBody()方法时,就能够得到结构重整后的ForumMessage对象了。这个对象可以被其他客户引用,也可以在传递给另外的过滤器,实施进一步的操作。

在实现一个具体的消息的过滤的时候,在Forum中有addForumMessageFilter(),applyFilters()方法,用来实现对过滤器的应用。

对一个Forum,使用addForumMessageFilter()方法添加一个Filter的时候,并没有指定一个具体的Message,而只是一个规则(Filter中封装了过滤规则),然后applyFilter()方法中,实施这些规则:

   public ForumMessage applyFilters(ForumMessage message) {
            //Loop through filters and apply them
            for (int i=0; i < filters.length; i++) {
            message = filters[i].clone(message);
            }
            return message;
            }

过滤器的clone()方法,为过滤器复制消息体。这个方法的使用,分离了在过滤器中对于消息体和过滤规则的初始化过程,这也是一个值得借鉴的技巧!

下面给出Decorator模式的类图:



图4:Decorator模式的类图
图4:Decorator模式的类图

我们可以看到Decorator模式实际上和Proxy模式是很相近的,但是它们代表两个不同的功能含义。Proxy模式提供一个对象的控制,而Decorator模式则是为对象提供额外的功能。





回页首


Iterator模式和论坛的浏览

Iterator模式用来分离数据结构和遍历算法,降低两者之间的耦合度,以使得同一个数据结构用不同的算法遍历时,仍能够具有相同的接口,另一方面,Iterator模式使得当改换遍历算法后,不需要更改程序的代码。

在Java的JDK中本身就定义有一个Iterator接口,在Iterator接口中仅仅定义了三个方法,hasNext()判断是否遍历完最后一个元素,next()方法返回要遍历的数据结构中一个对象,remove()则删除当前对象。Jive中使用IteratorProxy抽象类继承了这一接口。这儿Proxy的含义和上面一样,也就是说,这个IteratorProxy出了会实现Iterator的遍历功能外,还会有代理权限控制的功能。

对于论坛中的基本对象Forum,ForumThread,ForumMessage,Group,User都有相应的遍历器。比如对应于Forum接口有ForumIteratorProxy对象。这个ForumIteratorProxy遍历器就相当于一个封装了一系列Forum对象的集合类,通过定义好的接口hasNext()和next()可以方便的遍历这个集合,而并不需要知道是如何遍历这个集合的。遍历的算法可能很简单,也可能很复杂,但是对于外部的客户而言,这并没有任何的区别。

而对于论坛中具体的遍历方法,这取决于具体的实现,在Jive中给出的是数据库的实现。

我们就以MessageIteratorProxy为例,来讲解Iterator模式的用法。

DbThreadIterator对象实现了Iterator接口,是对于一个Thread中所有Message的遍历器,我们来看看它是如何实现的。

hasNext()判断在这个Thread中是不是还有下一条Message:

public boolean hasNext() {
            if (currentIndex+1 >= messages.length) {
            return false;
            }
            return true;
            }

next()方法从数据库中取出与在这个Thread中的下一条Message:

   public Object next() throws java.util.NoSuchElementException {
            ForumMessage message = null;
            if (nextMessage != null) {
            message = nextMessage;
            nextMessage = null;
            }
            else {
            message = getNextMessage();
            if (message == null) {
            throw new java.util.NoSuchElementException();
            }
            }
            return message;
            }

这样,通过对数据库的操作,DbThreadIterator实现了对一个Thread中所有Message遍历的方法。

再ForumThread接口中有messages()方法,返回在这个Thread中的所有Message的一个遍历器(Iterator),实际上也就是返回了一个Message的集合:
public Iterator messages();

在DbForumThread中实现了这个方法:
public Iterator messages() {return new DbThreadIterator(this);}

从DbForumThread的messages()方法中所返回的就是这个Thread中所有Message的一个遍历器,通过这个遍历器,我们就可以访问Thread中的所有的Message了。当然,事情还没有完,由于权限的问题,我们还需要构造这个遍历器的Proxy对象,然后通过这个Proxy对象来访问遍历器。

下面的类图给出了在Jive中Iterator模式的实现方法:



图5:Jive中Iterator模式的实现
图5:Jive中Iterator模式的实现

在Jive中,因为在一个Thread之下,Message是按树形结构组织的,因而,当需要层级表示一个Thread中的Message之间的关系的时候,仅仅用上面讲到的线性的Iterator是不够的。这时候,对Iterator的概念进行推广,就引入了TreeWalker接口。

顾名思义,TreeWalker提供了遍历一个树和存取树上节点的方法:

public interface TreeWalker {
            public ForumMessage getRoot();
            public ForumMessage getChild(ForumMessage parent, int index);
            public int getChildCount(ForumMessage parent);
            public int getRecursiveChildCount(ForumMessage parent);
            public int getIndexOfChild(ForumMessage parent, ForumMessage child);
            public boolean isLeaf(ForumMessage node);

TreeWalker只是Iterator的简单推广,并没有Iterator应用的那么广泛,而且,也可以很容易的在TreeWalker上面在套一层Iterator的借口,让它在某些情况下行使Iterator的职责。这儿就不再多讨论了。

再此,Jive设计中所有涉及到的设计模式的地方,基本上都讲完了,看完了之后,是不是对设计模式有了更进一步的了解了呢?

下一部分的内容,将会涉及到具体的编码,深入到JSP的内部,我们将会看到Jive中是如何实现可更换的Skin的,还会涉及Tag Library的一些内容。好了,这次就到这儿了。下次再见。

posted @ 2007-05-18 15:09 edsonjava 阅读(242) | 评论 (0)编辑 收藏
 
现在越来越多的项目是基于Linux或Unix下的,而在Linux上给客户上安装一个项目,需要进行许多的安装设置过程,比如数据库的,WebLogic Server的。现写下基于Red hat Linux7.1 +jdk1.3+WebLogic Server7.0 +oracle9.2 的安装配置过程。

一.安装好linux ,安装过程比较简单,不在此叙述.

二.JDK的安装配置.
一般以root用户安装。
先从SUN网站上下载一个jdk.比如:j2sdk-1_3_1_06-linux-i586.bin,放到/usr/local
下,
  chmod a+x j2sdk-1_3_0-linux.bin(添加执行权限)
  ./j2sdk-1_3_0-linux.bin

安装RPM文件格式:
  chmod a+x j2sdk-1_3_0-linux-rpm.bin
  ./j2sdk-1_3_0-linux-rpm.bin
  rpm -iv j2sdk-1_3_0-linux.rpm
  rpm -iv --force j2sdk-1_3_0-linux.rpm
  ./j2sdk-1_3_1_06-linux-i586.bin

设置环境变量:
  # vi /etc/profile
里面添加:
  export JAVA_HOME=/usr/local/jdk1.3.1_06/
  export CLASSPATH=.:/usr/local/ jdk1.3.1_06/lib
PATH=$PATH:$JAVA_HOME/bin

三、Oracle 9i的安装配置
1.从oracle网站下载oracle9i.
2.解压oracle文件
gunzip Linux9i_Disk1.cpio.gz Linux9i_Disk2.cpio.gz Linux9i_Disk3.cpio.gz

cpio -idmv < Linux9i_Disk1.cpio
cpio -idmv < Linux9i_Disk2.cpio
cpio -idmv < Linux9i_Disk3.cpio

3.以root用户登陆,创建oracle用户,目录,设置oracle环境变量.
  Create Oracle User Accounts
  # groupadd dba
  # groupadd oinstall
  # useradd -g oinstall -G dba oracle
  # passwd ********
  Create Oracle Directories
  # mkdir /opt/oracle
  # mkdir /opt/oracle/product
  # mkdir /opt/oracle/product/9.2.0
  # chown -R oracle.oinstall /opt/oracle
  # mkdir /var/opt/oracle
  # chown oracle.dba /var/opt/oracle
  # chmod 755 /var/opt/oracle
  Set Oracle Environments
  As the oracle user execute the following commands:
  # vi /home/oracle/.bash_profile添加

  export ORACLE_BASE=/usr/local/oracle
  export ORACLE_HOME=$ORACLE_BASE/product/9.2.0
  export ORACLE_SID=orcl
  export ORACLE_TERM=xterm
  export ORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/data
  LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
  LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
  export LD_LIBRARY_PATH
  CLASSPATH=$ORACLE_HOME/JRE:$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib
  CLASSPATH=$CLASSPATH:$ORACLE_HOME/network/jlib
  export CLASSPATH
  PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin

调整内存
  Shared Memory
  su root
  # vi/etc/sysctl.conf里添加
  kernel.shmmax=1073741824
4.安装oracle
  进入Disk1目录
  cd Disk1
  在控制台窗口敲入
  ./runInstaller
安装完了以后,启动数据库
oracle$ sqlplus /nolog
SQL> connect / as sysdba
SQL> startup

oracle 的安装过程比较复杂,而且如果你的开发包安装的不够全的话,会出现一些错误,具体请参考: http://www.puschitz.com/InstallingOracle9i.shtml

5.设置oracle 自启动

(1)vi /etc/oratab
  orcl:/usr/local/oracle/product/9.2.0:Y
(2)vi /home/oracle/.bash_profile
  ORACLE_SID=orcl
  ORAENV_ASK=NO
  export ORACLE_SID ORAENV_ASK
  . oraenv
(3)在 /etc/rc.d/init.d 下创建一个dbora文件。
内容如下:

  #!/bin/sh
  ORA_HOME=/usr/local/oracle/product/9.2.0
  ORA_OWNER=oracle
  if [ ! -f $ORA_HOME/bin/dbstart ]
  then
  echo "Oracle startup: cannot start"
  exit
  fi
  case "$1" in
  'start') #start oracle database and listeners
  su - $ORA_OWNER -c "$ORA_HOME/bin/dbstart"
  su - $ORA_OWNER -c "$ORA_HOME/bin/lsnrctl start"
  ;;
  'stop') #stop oracle databse and listeners
  su - $ORA_OWNER -c "$ORA_HOME/bin/lsnrctl stop"
  su - $ORA_OWNER -c "$ORA_HOME/bin/dbshut"
  ;;
  esac

把这个文件与下列文件联接:
ln -s /etc/rc.d/init.d/dbora /etc/rc.d/rc3.d/S99dbora
ln -s /etc/rc.d/init.d/dbora /etc/rc.d/rc5.d/S99dbora
ln -s /etc/rc.d/init.d/dbora /etc/rc.d/rc0.d/K10dbora

(4)编辑dbstart文件
以oracle用户登陆
vi /usr/local/oracle/product/9.2.0/bin/dbstart
- add the following line:
SPFILE=${ORACLE_HOME}/dbs/spfile${ORACLE_SID}.ora
after this line: PFILE=${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora

- change:
if [ -f $PFILE ] ; then
to:
if [ -f $PFILE -o -f $SPFILE ] ; then
重启动服务器,检查数据库是否已经起来。

四、WebLogic 配置。
从bea网站上下载一个WebLogic Server.开始安装。
[test@linux2 download]$ chmod a+x server701_linux.bin
[test@linux2 download]$ ./server701_linux.bin
按照提示安装即可。

五.设置WebLogic Server 自启动.
以root用户登陆。
vi /etc/rc.d/rc.local
把su - test -c "/home/test/bea/user_projects/mydomain/startWebLogic.sh 2>&1> /dev/null &"
(这里的 test是你安装WebLogic Server时候的用户名.)
放到/etc/rc.d/rc.local里就行了,不过这样有一个缺点,你WebLogic Server启动后一直在后台运行,你不能看到上面的提示信息和出错信息.

六.调整你的WebLogic Server,便于用户的使用。
设置默认 Web Application,从
  mydomain> Servers> myserver>connection>http>Default Web Application
选中你的web application即可。
设置你的首页,在你web application里面的web.xml里面添加一句
  
  你得页面
  


以上安装过程在Red hat Linux7.1 +jdk1.3+WebLogic Server7.0 +oracle9.2下安装测试通过。
posted @ 2007-05-17 18:21 edsonjava 阅读(301) | 评论 (0)编辑 收藏
 

1.安装JDK
首先,到http://java.sun.com/j2se/1.5.0/download.jsp
下载最新版本的Linux 平台的JDK,建议下载RPM自解压格式的例如本文所用jdk-1_5_0_06-linux-i586-rpm.bin,先下载文件到/tmp,打开终端,输入:
cd /tmp
su
输入root密码
直接执行文件:
./jdk-1_5_0_06-linux-i586-rpm.bin
然后会出现sun的协议(Sun Microsystems, Inc. Binary Code License Agreement),如果运行jdk-1_5_0_06-linux-i586-rpm.bin无效请给予其相应的运行权限。
想查看完整协议,不断点击more就可以了。如果看完了,或者像我一样看不懂^__^就直接按q吧。
出现提示:Do you agree to the above license terms? [yes or no]
如果同意协议,请输入yes
然后自动解压出jdk-1_5_0_06-linux-i586.rpm,并且自动安装,如果不是root用户,可能会出现类似
error: cannot get exclusive lock on /var/lib/rpm/Packages
error: cannot open Packages index using db3 -
不允许的操作 (1)
error: cannot open Packages database in /var/lib/rpm
的错误。
最后显示Done.安装结束

2.配置环境变量
就像在windows下一样,装好JDK需要配置环境变量,否则系统找不到相应的程序。先查看当前系统环境变量中jdk的路径:
echo $JAVA_HOME
如果安装SuSE Linux时选择了相应的java的包,则显示/usr/lib/jvm/java。再看当前JDK版本:
java -version
我的机器上显示如下信息:
java version "1.4.2_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_06-b03)
Java HotSpot(TM) Client VM (build 1.4.2_06-b03, mixed mode)
1.4.2_06版本,不是我们刚才安装的版本(因为没有修改环境变量嘛)。
我们刚才安装的版本默认在/usr/java/jdk1.5.0_06,我们把它加到环境变量。
最简单的办法就是编辑/ect/profile,在文件最后的
#
# End of /etc/profile
#
上面添加如下文本:
export JAVA_HOME=/usr/java/jdk1.5.0_06
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
export JRE_HOME=$JAVA_HOME/jre
export PATH=$JAVA_HOME/bin:$PATH
作用分别是设置 JAVA_HOME , CLASSPATH , JRE_HOME , PATH 所指向的路径。跟windows下的意义一样。如果不懂可以查阅相关文档或者直接把以上文本复制粘贴到你的/ect/profile 中即可。
注销一下,使更改生效。
再查看一下当前的环境变量:
echo $JAVA_HOME
输出:
/usr/java/jdk1.5.0_06
可以看到我们刚才装的JDK生效了。
然后输入:
java -version
查看当前JDK版本。
输出:
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)
说明我们环境变量配置成功了。

posted @ 2007-05-17 16:16 edsonjava 阅读(5907) | 评论 (2)编辑 收藏
 

1 环境的搭建

要使用JAVA开发Web应用,必需要JAVA的运行环境,还有开发环境。当然Web开发少不了数据库。Web程序要运行也少不了Web服务器。

这里我们选用JAVA运行环境:J2SDk1.4

数据库:Mysql 4.0.15

Web服务器:Tomcat 4.1.18

1.1 JAVA的运行环境

首先要搭建的是JAVA的运行环境。到Sun公司http://java.sun.com/j2se/1.4.2/download.html免费下载j2sdk-1_4_2_05-linux-i586.bin然后拷贝到安装目录

执行二进制文件即可解压缩文件:

[root@localhost jdk]#./ j2sdk-1_4_2_05-linux-i586.bin

解压缩完成之后就可以在当前目录下面找到j2sdk1.4.2_05目录

为了方便可以做一个简单的连接到这个目录

[root@localhost jdk]#ln –s j2sdk1.4.2_05 jdk

然后加入环境变量

export JVAV_HOME=/var/jdk/jdk1.4

export CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib:.

export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin

现在JAVA运行环境就搭建好了,你可以试试写一个java程序来验证

[root@localhost jdk]#vi HelloWorld.java

输入如下内容

public class HelloWorld{

   public static void main(String args[]){

    System.out.println("HelloWrold");

   }

}

:wq

写盘并退出编辑器

[root@localhost jdk]#javac HelloWorld.java

没有错误

[root@localhost jdk]#java HelloWorld

Hello,World

恭喜,

Kq[业Kk0+?bv

你的JAVA运行环境搭建好了。现在进入下一步。

 

1.2 Mysql数据库安装

1下载数据库安装文件mysql-4.0.15a.tar.gz

2建立MySLQL的用户和组

[root@localhost var]# groupadd mysql

[root@localhost var]# useradd –g mysql mysql

3修改根目录下的root目录下面的.bash_profile文件添加PATH环境变量

[root@localhost root]#vi .bash_profile

PATH=$PATH:$HOME/bin:/usr/local/mysql/bin

保存退出

下面就可以看是安装MySql

4解压缩文件

[root@localhost jdk]#tar xzvf mysql-4.0.15a.tar.gz

进入解压缩得到的目录

[root@localhost var]# cd mysql-4.0.15a

5配置发行版本并且编译

[root@localhost mysql-4.0.15a]#./configure --prefix=/usr/local/mysql

[root@localhost mysql-4.0.15a]#make

当你运行configure时,你可能想要指定一些选项,--prefix选项制定安装mysql的目录为/usr/local/mysql

6安装所有东西

[root@localhost mysql-4.0.15a]#make install

你可能需要root用户来运行这个命令

ok现在mysql数据库服务器就安装好了。接下来还有重要的步骤需要执行

7创造MySQL授权表(只有你以前没安装MySQL是必需的):

[root@localhost mysql-4.0.15a]#scripts/mysql_install_db

8做完上面的步骤我们就可以启动mysql进行操作了。

[root@localhost mysql-4.0.15a]#cd /usr/local/mysql/bin

[root@localhost bin]# ./mysqld_safe &

如果没有出错提示,查看一下mysql进程

[root@localhost bin]# ps aux|grep mysql

root      1205 0.0 0.0 5388 168 ?        S    Apr22   0:00 /bin/sh /usr/loca

l/mysql/bin/mysqld_safe

mysql     1227 0.0 1.3 100316 13756 ?      S    Apr22   0:36 [mysqld]

root     22956 0.0 0.0 4816 640 pts/1    S    10:41   0:00 grep mysql

粗体的就是mysql的进程了。

现在就可以使用mysql,root登陆MySQL服务器

[root@localhost bin]#mysql –u root –p

这里会提示输入密码默认的root用户密码为空。直接回车就可以了

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 95 to server version: 4.0.15a-log

 

Type ''''help;'''' or ''''\h'''' for help. Type ''''\c'''' to clear the buffer.

 

mysql>

这样就成功登陆MySQL服务器了。

mysql> show databases;

+----------+

| Database |

+----------+

| cumt     |

| mysql    |

| test     |

+----------+

3 rows in set (0.01 sec)

现在就可以建立数据库了。这里就不介绍怎样建立数据库和建立表了。

Root用户没有密码是不安全的所以你需要修改root用户的密码

[root@localhost bin]# mysqladmin -u root password ''''new-password''''

''''new-password''''换成你自己的密码就可以了。

注意:做完上面的我们就可以用mysqlbin目录下的mysql来管理数据库了。可是这还没法在程序中使用数据库。我在jsp中连接数据库的时候出现如下错误:

java.sql.SQLException: Data source rejected establishment of connection, message from server: "Host ''''localhost.localdomain''''is not allowed to connect to this MySQL server"

这是MySQL的权限问题具体的讲解请参考MySQL参考手册的存取权限系统

我们需要做的是让root可以从localhost连接到数据库,

,G专}Uc{|g[

你也可以建立一个新用户来连接

 

登陆到mysql服务器

mysql> GRANT ALL PRIVILEGES ON *.* TO cumt@localhost
           IDENTIFIED BY ''''cumt'''' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO cumt@"%"
           IDENTIFIED BY ''''cumt'''' WITH GRANT OPTION;

这两天语句添加用户cumt使得它可以从任何地方连接服务器的一个完全的超级用户,但是必须使用一个口令cumt做这个。现在我们就可以从程序中用cumt来连接数据库了。

但是在程序中还是没有对表的写权限。这是由于我们的数据库用户是root而不是我们建立的mysql组的mysql用户。所以只有读的权限而没有写的权限。我们需要把mysql目录下面的var目录下面的目录和文件的所有者改成mysql组的myql用户:

[root@localhost bin]#chown -R mysql /usr/local/mysql/var

[root@localhost bin]#cp support- files/my-medium.cnf /etc/my.cnf

好了做完上面的我们就可以在程序中使用cumt连接数据库和操作数据库了。

1.3 Web服务器tomcat安装配置

下载tomcat,

件_:87VNLPI$

jakarta-tomcat-4.1.18.tar.gz

 

解压缩

root@localhost var]#tar xzvf jakarta-tomcat-4.1.18.tar.gz
为方便操作:
ln -s jakarta-tomcat-4.0.1 tomcat
ln -s j2sdk1.4.0 jdk

修改Tomcat/bin/startup.sh :

export JAVA_HOME=/usr/local/jdk
export CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib:.
export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin

/usr/local/tomcat/bin/catalina.sh start


启动Tomcat/bin/startup.sh
1.使用ps -ax | grep tomcat可以查询出内存中存在tomcat程序
使用http://你的服务器域名或IP地址或localhost:8080/可访问

2.
如要求系统开机自动tomcat /etc/rc.d/rc.local中加入:
/usr/local/tomcat/bin/startup.sh

3.
对于linux7.1系统, tomcat好象不能正常启动,需要安装:
compat-libstdc++-6.2-2.9.0.14.i386.rpm

rpm -i compat-libstdc++-6.2-2.9.0.14.i386.rpm

4.
如果希望以http://www.xxx.com:8080/myweb 访问自己的jsp程序,以下步骤:
(1).
在任意地方建立目录myweb 比如 /home/bqlr/myweb
(2).
myweb下建立 WEB-INF WEB-INF/classes目录
(3).
tomcatconf目录下修改server.xml:

<!-- Tomcat Manager Context -->
<Context path="/manager" docBase="manager" debug="0" privileged="true"/>

<!--
下面是自己加入的-->

<Context path="/myweb" docBase="/home/bqlr/myweb" debug="0" reloadable="true"/>

(4) tomcatwebapps目录下,建立目录连接myweb
ln -s /home/bqlr/myweb /usr/local/tomcat/webapps/myweb

(5)
重新启动Tomcat. class文件放在/home/bqlr/myweb/WEB-INF/classes目录下 jsp文件放在/home/bqlr/myweb

posted @ 2007-05-14 10:49 edsonjava 阅读(383) | 评论 (0)编辑 收藏
 

jfreechart是一个免费(文档收费40$)创建图片的java工具.可以创建如下图形:
饼图(pie charts;)
曲线图(line charts )
柱状图(horizontal/vertical bar charts)
甘特图(Gantt charts; )
XY plots and scatter plots;
time series, high/low/open/close charts and candle stick charts;
combination charts;
Pareto charts;
bubble charts;
wind plots, meter charts and symbol charts;

必看的几个贴:

http://www-900.ibm.com/developerWorks/cn/wsdd/library/techarticles/yangyaping0307/waslinux.shtml

http://www.lslnet.com/linux/docs/linux-2940.htm

http://blog.blogchina.com/article_81038.344426.html

在向linux移植的时候会显示不出中文,出现方块。

解决方法:copy或引用/usr/share/fonts/zh_CN/TrueType目录下的中文字体,修改/home/jdk/jre/lib/fonts.properties

配置文件,如下:

sansserif.0=-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

sansserif.italic.0=-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

sansserif.bold.0=-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

sansserif.bolditalic.0=-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

# Component Font Character Encodings
#
fontcharset.serif.0=sun.io.CharToByteISO8859_1
fontcharset.serif.1=sun.awt.motif.CharToByteX11GBK

fontcharset.sansserif.0=sun.io.CharToByteISO8859_1
fontcharset.sansserif.1=sun.awt.motif.CharToByteX11GBK

fontcharset.monospaced.0=sun.io.CharToByteISO8859_1
fontcharset.monospaced.1=sun.awt.motif.CharToByteX11GBK

fontcharset.dialog.0=sun.io.CharToByteISO8859_1
fontcharset.dialog.1=sun.awt.motif.CharToByteX11GBK

fontcharset.dialoginput.0=sun.io.CharToByteISO8859_1
fontcharset.dialoginput.1=sun.awt.motif.CharToByteX11GBK

fontset.sansserif.plain=\
-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

fontset.sansserif.italic=\
-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

fontset.sansserif.bold=\
-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1

fontset.sansserif.bolditalic=\
-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1


fontset.default=\
-misc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-iso10646-1


appendedfontpath=/usr/share/fonts/zh_CN/TrueType

然后对自己基于jfreechart写的程序进行重新编译、运行:

javac -encoding GBK   BarChartDemo.java //以GBK进行编码
java -Djava.awt.headless=true BarChartDemo//使用awt时不用调用x11的图形环境

 

在tomcat使用jfreechart时

tomcat是5.0,在redhat8上,未启动X,方法如下:
1)终止你的tomcat。即:
tomcat目录/bin/shutdown.sh
2)设置环境变量:
CATALINA_OPTS="-Djava.awt.headless=true"
export CATALINA_OPTS
(如果你想每次开机自动生效,则可把这两句写入系统或者你的账号启动sh的.profile里)
3)启动你的tomcat。即:
tomcat目录/bin/startup.sh

用的Web服务器resin时,

修改resin/bin/下面的wrapper.pl中的一行
$JAVA_ARGS="-Djava.awt.headless=true";


 

==============================================================

最近项目中用到jfreechart,使用的版本是:jfreechart-0.9.8。
如果不进行相关的font的设置,生成的统计图表显示的中文非常模糊。
做了一个例子,可以解决这个问题。

[该方法是将一般字体替换为“黑体”使中文更加清楚,

因此必须保证你的OS上有该字体,并且jdk能够识别得到]

核心代码如下:

JFreeChart chart = ChartFactory.createVerticalBarChart3D(title, domain, range, dataset,true,true,false);

chart.setBackgroundPaint(new GradientPaint(0.0F, 0.0F, Color.white, 1000F, 0.0F, Color.red));
chart.setTitle(new TextTitle(title, new Font("隶书", Font.ITALIC, 15)));

Font font=new Font("黑体",Font.TRUETYPE_FONT, 12);

StandardLegend legend = (StandardLegend) chart.getLegend();
legend.setItemFont(font);

CategoryPlot plot = (CategoryPlot)chart.getPlot();
plot.setForegroundAlpha(0.9F);

CategoryAxis domain_axis = plot.getDomainAxis();
domain_axis.setTickLabelFont(font);

ValueAxis value_axis=plot.getRangeAxis();
value_axis.setTickLabelFont(font);


posted @ 2007-04-12 15:53 edsonjava 阅读(253) | 评论 (0)编辑 收藏
 
     摘要: I'm using WebSphere 5.1 on windows, MyEclipse 3.8.4. I have the TraderX example app deployed (use package deploy, install the app with websphere console), and started. I can "buy" (i.e. use the app). ...  阅读全文
posted @ 2007-04-03 18:19 edsonjava 阅读(1576) | 评论 (0)编辑 收藏
 


Java基础方面:

1、作用域public,private,protected,以及不写时的区别
答:区别如下:
作用域 当前类 同一package 子孙类 其他package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
不写时默认为friendly

2、ArrayList和Vector的区别,HashMap和Hashtable的区别
答:就ArrayList与Vector主要从二方面来说.
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的
二.数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半
就HashMap与HashTable主要从三方面来说。
一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
三.值:只有HashMap可以让你将空值作为一个表的条目的key或value

3、char型变量中能不能存贮一个中文汉字?为什么?
答:是能够定义成为一个中文的,因为java中以unicode编码,一个char占16个字节,所以放一个中文是没问题的

4、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify

5、继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
答:父类:
package test;
public class FatherClass
{
public FatherClass()
{
System.out.println("FatherClass Create");
}
}
子类:
package test;
import test.FatherClass;
public class ChildClass extends FatherClass
{
public ChildClass()
{
System.out.println("ChildClass Create");
}
public static void main(String[] args)
{
FatherClass fc = new FatherClass();
ChildClass cc = new ChildClass();
}
}
输出结果:
C:>java test.ChildClass
FatherClass Create
FatherClass Create
ChildClass Create

6、内部类的实现方式?
答:示例代码如下:
package test;
public class OuterClass
{
private class InterClass
{
public InterClass()
{
System.out.println("InterClass Create");
}
}
public OuterClass()
{
InterClass ic = new InterClass();
System.out.println("OuterClass Create");
}
public static void main(String[] args)
{
OuterClass oc = new OuterClass();
}
}
输出结果:
C:>java test/OuterClass
InterClass Create
OuterClass Create
再一个例题:
public class OuterClass {
private double d1 = 1.0;
//insert code here
}
You need to insert an inner class declaration at line 3. Which two inner class declarations are

valid?(Choose two.)
A. class InnerOne{
public static double methoda() {return d1;}
}
B. public class InnerOne{
static double methoda() {return d1;}
}
C. private class InnerOne{
double methoda() {return d1;}
}
D. static class InnerOne{
protected double methoda() {return d1;}
}
E. abstract class InnerOne{
public abstract double methoda();
}
说明如下:
一.静态内部类可以有静态成员,而非静态内部类则不能有静态成员。 故 A、B 错
二.静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;return d1 出错。

故 D 错
三.非静态内部类的非静态成员可以访问外部类的非静态变量。 故 C 正确
四.答案为C、E

7、垃圾回收机制,如何优化程序?
希望大家补上,谢谢

8、float型float f=3.4是否正确?
答:不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4

9、介绍JAVA中的Collection FrameWork(包括如何写自己的数据结构)?
答:Collection FrameWork如下:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)
Map提供key到value的映射

10、Java中异常处理机制,事件机制?

11、JAVA中的多形与继承?
希望大家补上,谢谢

12、抽象类与接口?
答:抽象类与接口都用于抽象,但是抽象类(JAVA中)可以有自己的部分实现,而接口则完全是一个标识(同时有多重继承的功能)。

13、Java 的通信编程,编程题(或问答),用JAVA SOCKET编程,读服务器几个字符,再写入本地显示?
答:Server端程序:
package test;
import java.net.*;
import java.io.*;

public class Server
{
private ServerSocket ss;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public Server()
{
try
{
ss=new ServerSocket(10000);
while(true)
{
socket = ss.accept();
String RemoteIP = socket.getInetAddress().getHostAddress();
String RemotePort = ":"+socket.getLocalPort();
System.out.println("A client come in!IP:"+RemoteIP+RemotePort);
in = new BufferedReader(new

InputStreamReader(socket.getInputStream()));
String line = in.readLine();
System.out.println("Cleint send is :" + line);
out = new PrintWriter(socket.getOutputStream(),true);
out.println("Your Message Received!");
out.close();
in.close();
socket.close();
}
}catch (IOException e)
{
out.println("wrong");
}
}
public static void main(String[] args)
{
new Server();
}
};
Client端程序:
package test;
import java.io.*;
import java.net.*;

public class Client
{
Socket socket;
BufferedReader in;
PrintWriter out;
public Client()
{
try
{
System.out.println("Try to Connect to 127.0.0.1:10000");
socket = new Socket("127.0.0.1",10000);
System.out.println("The Server Connected!");
System.out.println("Please enter some Character:");
BufferedReader line = new BufferedReader(new

InputStreamReader(System.in));
out = new PrintWriter(socket.getOutputStream(),true);
out.println(line.readLine());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
out.close();
in.close();
socket.close();
}catch(IOException e)
{
out.println("Wrong");
}
}
public static void main(String[] args)
{
new Client();
}
};

14、用JAVA实现一种排序,JAVA类实现序列化的方法(二种)? 如在COLLECTION框架中,实现比较要实现什么样的接口?
答:用插入法进行排序代码如下
package test;
import java.util.*;
class InsertSort
{
ArrayList al;
public InsertSort(int num,int mod)
{
al = new ArrayList(num);
Random rand = new Random();
System.out.println("The ArrayList Sort Before:");
for (int i=0;i<num ;i++ )
{
al.add(new Integer(Math.abs(rand.nextInt()) % mod + 1));
System.out.println("al["+i+"]="+al.get(i));
}
}
public void SortIt()
{
Integer tempInt;
int MaxSize=1;
for(int i=1;i<al.size();i++)
{
tempInt = (Integer)al.remove(i);
if(tempInt.intValue()>=((Integer)al.get(MaxSize-1)).intValue())
{
al.add(MaxSize,tempInt);
MaxSize++;
System.out.println(al.toString());
} else {
for (int j=0;j<MaxSize ;j++ )
{
if

(((Integer)al.get(j)).intValue()>=tempInt.intValue())
{
al.add(j,tempInt);
MaxSize++;
System.out.println(al.toString());
break;
}
}
}
}
System.out.println("The ArrayList Sort After:");
for(int i=0;i<al.size();i++)
{
System.out.println("al["+i+"]="+al.get(i));
}
}
public static void main(String[] args)
{
InsertSort is = new InsertSort(10,100);
is.SortIt();
}
}
JAVA类实现序例化的方法是实现java.io.Serializable接口
Collection框架中实现比较要实现Comparable 接口和 Comparator 接口

15、编程:编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。
答:代码如下:
package test;

class SplitString
{
String SplitStr;
int SplitByte;
public SplitString(String str,int bytes)
{
SplitStr=str;
SplitByte=bytes;
System.out.println("The String is:′"+SplitStr+"′;SplitBytes="+SplitByte);
}
public void SplitIt()
{
int loopCount;


loopCount=(SplitStr.length()%SplitByte==0)?(SplitStr.length()/SplitByte):(SplitStr.length()/Split

Byte+1);
System.out.println("Will Split into "+loopCount);
for (int i=1;i<=loopCount ;i++ )
{
if (i==loopCount){


System.out.println(SplitStr.substring((i-1)*SplitByte,SplitStr.length()));
} else {


System.out.println(SplitStr.substring((i-1)*SplitByte,(i*SplitByte)));
}
}
}
public static void main(String[] args)
{
SplitString ss = new SplitString("test中dd文dsaf中男大3443n中国43中国人

0ewldfls=103",4);
ss.SplitIt();
}
}

16、JAVA多线程编程。 用JAVA写一个多线程程序,如写四个线程,二个加1,二个对一个变量减一,输出。
希望大家补上,谢谢

17、STRING与STRINGBUFFER的区别。
答:STRING的长度是不可变的,STRINGBUFFER的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后需要String,那么使用StringBuffer的toString()方法

Jsp方面

1、jsp有哪些内置对象?作用分别是什么?
答:JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):
 request 用户端请求,此请求会包含来自GET/POST请求的参数
response 网页传回用户端的回应
pageContext 网页的属性是在这里管理
session 与请求有关的会话期
application servlet 正在执行的内容
out 用来传送回应的输出
config servlet的构架部件
page JSP网页本身
exception 针对错误网页,未捕捉的例外

2、jsp有哪些动作?作用分别是什么?
答:JSP共有以下6种基本动作
jsp:include:在页面被请求的时候引入一个文件。
jsp:useBean:寻找或者实例化一个JavaBean。
jsp:setProperty:设置JavaBean的属性。
jsp:getProperty:输出某个JavaBean的属性。
jsp:forward:把请求转到一个新的页面。
jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

3、JSP中动态INCLUDE与静态INCLUDE的区别?
答:动态INCLUDE用jsp:include动作实现
<jsp:include page="included.jsp" flush="true" />它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数
静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面
<%@ include file="included.htm" %>

4、两种跳转方式分别是什么?有什么区别?
答:有两种,分别为:
<jsp:include page="included.jsp" flush="true">
<jsp:forward page= "nextpage.jsp"/>
前者页面不会转向include所指的页面,只是显示该页的结果,主页面还是原来的页面。执行完后还会回来,相当于函数调用。并且可以带参数.后者完全转向新页面,不会再回来。相当于go to 语句。

Servlet方面

1、说一说Servlet的生命周期?
答:servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。

2、Servlet版本间(忘了问的是哪两个版本了)的不同?
希望大家补上,谢谢

3、JAVA SERVLET API中forward() 与redirect()的区别?
答:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。

4、Servlet的基本架构
public class ServletName extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
}
}

Jdbc、Jdo方面

1、可能会让你写一段Jdbc连Oracle的程序,并实现数据查询.
答:程序如下:
package hello.ant;
import java.sql.*;
public class jdbc
{
String dbUrl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String theUser="admin";
String thePw="manager";
Connection c=null;
Statement conn;
ResultSet rs=null;
public jdbc()
{
try{
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
c = DriverManager.getConnection(dbUrl,theUser,thePw);
conn=c.createStatement();
}catch(Exception e){
e.printStackTrace();
}
}
public boolean executeUpdate(String sql)
{
try
{
conn.executeUpdate(sql);
return true;
}
catch (SQLException e)
{
e.printStackTrace();
return false;
}
}
public ResultSet executeQuery(String sql)
{
rs=null;
try
{
rs=conn.executeQuery(sql);
}
catch (SQLException e)
{
e.printStackTrace();
}
return rs;
}
public void close()
{
try
{
conn.close();
c.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
ResultSet rs;
jdbc conn = new jdbc();
rs=conn.executeQuery("select * from test");
try{
while (rs.next())
{
System.out.println(rs.getString("id"));
System.out.println(rs.getString("name"));
}
}catch(Exception e)
{
e.printStackTrace();
}
}
}

2、Class.forName的作用?为什么要用?
答:调用该访问返回一个以字符串指定类名的类的对象。

3、Jdo是什么?
答:JDO是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。

4、在ORACLE大数据量下的分页解决方法。一般用截取ID方法,还有是三层嵌套方法。
答:一种分页方法
<%
int i=1;
int numPages=14;
String pages = request.getParameter("page") ;
int currentPage = 1;
currentPage=(pages==null)?(1):{Integer.parseInt(pages)}
sql = "select count(*) from tables";
ResultSet rs = DBLink.executeQuery(sql) ;
while(rs.next()) i = rs.getInt(1) ;
int intPageCount=1;
intPageCount=(i%numPages==0)?(i/numPages):(i/numPages+1);
int nextPage ;
int upPage;
nextPage = currentPage+1;
if (nextPage>=intPageCount) nextPage=intPageCount;
upPage = currentPage-1;
if (upPage<=1) upPage=1;
rs.close();
sql="select * from tables";
rs=DBLink.executeQuery(sql);
i=0;
while((i<numPages*(currentPage-1))&&rs.next()){i++;}
%>
//输出内容
//输出翻页连接
合计:<%=currentPage%>/<%=intPageCount%><a href="List.jsp?page=1">第一页</a><a

href="List.jsp?page=<%=upPage%>">上一页</a>
<%
for(int j=1;j<=intPageCount;j++){
if(currentPage!=j){
%>
<a href="list.jsp?page=<%=j%>">[<%=j%>]</a>
<%
}else{
out.println(j);
}
}
%>
<a href="List.jsp?page=<%=nextPage%>">下一页</a><a href="List.jsp?page=<%=intPageCount%>">最后页

</a>


Xml方面

1、xml有哪些解析技术?区别是什么?
答:有DOM,SAX,STAX等
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming API for XML (StAX)

2、你在项目中用到了xml技术的哪些方面?如何实现的?
答:用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。

3、用jdom解析xml文件时如何解决中文问题?如何解析?
答:看如下代码,用编码方式加以解决
package test;
import java.io.*;
public class DOMTest
{
private String inFile = "c:\people.xml";
private String outFile = "c:\people.xml";
public static void main(String args[])
{
new DOMTest();
}
public DOMTest()
{
try
{
javax.xml.parsers.DocumentBuilder builder =


javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder();
org.w3c.dom.Document doc = builder.newDocument();
org.w3c.dom.Element root = doc.createElement("老师");
org.w3c.dom.Element wang = doc.createElement("王");
org.w3c.dom.Element liu = doc.createElement("刘");
wang.appendChild(doc.createTextNode("我是王老师"));
root.appendChild(wang);
doc.appendChild(root);
javax.xml.transform.Transformer transformer =
javax.xml.transform.TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "gb2312");
transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");


transformer.transform(new javax.xml.transform.dom.DOMSource(doc),
new

javax.xml.transform.stream.StreamResult(outFile));
}
catch (Exception e)
{
System.out.println (e.getMessage());
}
}
}

4、编程用JAVA解析XML的方式.
答:用SAX方式解析XML,XML文件如下:
<?xml version="1.0" encoding="gb2312"?>
<person>
<name>王小明</name>
<college>信息学院</college>
<telephone>6258113</telephone>
<notes>男,1955年生,博士,95年调入海南大学</notes>
</person>
事件回调类SAXHandler.java
import java.io.*;
import java.util.Hashtable;
import org.xml.sax.*;
public class SAXHandler extends HandlerBase
{
private Hashtable table = new Hashtable();
private String currentElement = null;
private String currentValue = null;
public void setTable(Hashtable table)
{
this.table = table;
}
public Hashtable getTable()
{
return table;
}
public void startElement(String tag, AttributeList attrs)
throws SAXException
{
currentElement = tag;
}
public void characters(char[] ch, int start, int length)
throws SAXException
{
currentValue = new String(ch, start, length);
}
public void endElement(String name) throws SAXException
{
if (currentElement.equals(name))
table.put(currentElement, currentValue);
}
}
JSP内容显示源码,SaxXml.jsp:
<HTML>
<HEAD>
<TITLE>剖析XML文件people.xml</TITLE>
</HEAD>
<BODY>
<%@ page errorPage="ErrPage.jsp"
contentType="text/html;charset=GB2312" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Hashtable" %>
<%@ page import="org.w3c.dom.*" %>
<%@ page import="org.xml.sax.*" %>
<%@ page import="javax.xml.parsers.SAXParserFactory" %>
<%@ page import="javax.xml.parsers.SAXParser" %>
<%@ page import="SAXHandler" %>
<%
File file = new File("c:\people.xml");
FileReader reader = new FileReader(file);
Parser parser;
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
SAXHandler handler = new SAXHandler();
sp.parse(new InputSource(reader), handler);
Hashtable hashTable = handler.getTable();
out.println("<TABLE BORDER=2><CAPTION>教师信息表</CAPTION>");
out.println("<TR><TD>姓名</TD>" + "<TD>" +
(String)hashTable.get(new String("name")) + "</TD></TR>");
out.println("<TR><TD>学院</TD>" + "<TD>" +
(String)hashTable.get(new String("college"))+"</TD></TR>");
out.println("<TR><TD>电话</TD>" + "<TD>" +
(String)hashTable.get(new String("telephone")) + "</TD></TR>");
out.println("<TR><TD>备注</TD>" + "<TD>" +
(String)hashTable.get(new String("notes")) + "</TD></TR>");
out.println("</TABLE>");
%>
</BODY>
</HTML>

EJB方面

1、EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?
答:规范内容包括Bean提供者,应用程序装配者,EJB容器,EJB配置工具,EJB服务提供者,系统管理员。这里面,EJB容器是EJB之所以能够运行的核心。EJB容器管理着EJB的创建,撤消,激活,去活,与数据库的连接等等重要的核心工作。JSP,Servlet,EJB,JNDI,JDBC,JMS.....

2、EJB与JAVA BEAN的区别?
答:Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理,EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。

3、EJB的基本架构
答:一个EJB包括三个部分:
Remote Interface 接口的代码
package Beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Add extends EJBObject
{
//some method declare
}
Home Interface 接口的代码
package Beans;
import java.rmi.RemoteException;
import jaax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface AddHome extends EJBHome
{
//some method declare
}
EJB类的代码
package Beans;
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javx.ejb.SessionContext;
public class AddBean Implements SessionBean
{
//some method declare
}

J2EE,MVC方面

1、MVC的各个部分都有那些技术来实现?如何实现?
答:MVC是Model-View-Controller的简写。"Model" 代表的是应用的业务逻辑(通过JavaBean,EJB组件实现), "View" 是应用的表示面(由JSP页面产生),"Controller" 是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。

2、应用服务器与WEB SERVER的区别?
希望大家补上,谢谢


3、J2EE是什么?
答:Je22是Sun公司提出的多层(multi-diered),分布式(distributed),基于组件(component-base)的企业级应用模型(enterpriese application model).在这样的一个应用系统中,可按照功能划分为不同的组件,这些组件又可在不同计算机上,并且处于相应的层次(tier)中。所属层次包括客户层(clietn tier)组件,web层和组件,Business层和组件,企业信息系统(EIS)层。

4、WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。
答:Web Service描述语言WSDL
SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML编码信息的轻量级协议。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为Web Service提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。


5、BS与CS的联系与区别。
希望大家补上,谢谢

6、STRUTS的应用(如STRUTS架构)
答:Struts是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。 采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。 Struts有如下的主要功能:
一.包含一个controller servlet,能将用户的请求发送到相应的Action对象。
二.JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。
三.提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。

设计模式方面

1、开发中都用到了那些设计模式?用在什么场合?
答:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。主要用到了MVC的设计模式。用来开发JSP/Servlet或者J2EE的相关应用。简单工厂模式等。


2、UML方面
答:标准建模语言UML。用例图,静态图(包括类图、对象图和包图),行为图,交互图(顺序图,合作图),实现图,

JavaScript方面

1、如何校验数字型?
var re=/^d{1,8}$|.d{1,2}$/;
var str=document.form1.all(i).value;
var r=str.match(re);
if (r==null)
{
sign=-4;
break;
}
else{
document.form1.all(i).value=parseFloat(str);
}


CORBA方面

1、CORBA是什么?用途是什么?
答:CORBA 标准是公共对象请求代理结构(Common Object Request Broker Architecture),由对象管理组织 (Object Management Group,缩写为 OMG)标准化。它的组成是接口定义语言(IDL), 语言绑定(binding:也译为联编)和允许应用程序间互操作的协议。 其目的为:
用不同的程序设计语言书写
在不同的进程中运行
为不同的操作系统开发


LINUX方面

1、LINUX下线程,GDI类的解释。
答:LINUX实现的就是基于核心轻量级进程的"一对一"线程模型,一个线程实体对应一个核心轻量级进程,而线程之间的管理在核外函数库中实现。
GDI类为图像设备编程接口类库。

posted @ 2007-04-03 11:24 edsonjava 阅读(269) | 评论 (0)编辑 收藏
 
1。创建cvs

系统管理员登陆CVS服务器,创建CVS库的存放目录,比如C:\cvs\cvsrep。使用“cvs -d /cvs/cvsrep init ”初始化目录,该命令会自动创建CVSROOT,CVS等目录。然后,在CVS的配置文件里,同时为该库起一个别名,比如/cvs/cvsrep,以防止用户知道系统的真实的目录结果。别名的设置,在Unix下,是在/etc/cvsnt/PServer文件里;Windows下,可以通过CVSNT Control Panel来进行。

 

了简化操作,我们先设置环境变量,比如"set cvsroot=c:\cvs\cvsrep"。这样,就不用每次指定CVSROOT的位置了。

 

2。创建cvs库管理员

系统管理员使用passwd命令创建用户(同时设置初始口令),然后把新建的用户名列在CVSROOT/admin里,这些用户就成为了CVS库管理员。

 

比如通过“cvs passwd -r cvsuser -u repadmin”创建一个叫repadmin的用户。这里,cvs服务器上必须存在一个cvsuser的系统用户,该用户必须有对C:\cvs\cvsrep的完全访问权限。cvsuser账户,可以为多个cvs库用户所共享。repadmincvs的远程访问,是以cvsuser的身份运行的。

 

系统管理员使用chacl命令把根目录的确省权限设为read,nowrite,nocontrol,nocreate,notag。当然,这些工作也可以交由cvs库管理员来做。

比如:cvs chacl -a read,nowrite,nocontrol,nocreate,notag

如果子目录不重新设置权限,自动继承父目录的权限。

 

 

系统管理员通过emai把账号、密码以及CVSROOT的路径发送给cvs库管理员。

 

cvs库管理远程访问cvs库,比如通过pserver协议。

 

设置环境变量,set cvsroot=:pserver:repadmin@servername:/cvs/cvsrep.

 

转到工作目录,登陆,并初始化当前目录

cd workdir

cvs login

cvs co .

 

修改密码

cvs passwd

 

 

3。创建普通用户

cvs库管理员通过passwd创建用户,同时设置初始密码,然后通email告知相关信息。

cvs passwd -a -r cvsuser -u testuser

 

 

4。创建目录树

cvs库管理员通过importadd命令创建目录树,并使用chacl命令给用户分配权限。

可以通过lsacl命令来检查权限的分配情况。

 

添加一个目录

cvs add subdir

 

testuser分配subdir的读写权限

cvs chacl -a read,write -u testuser subdir

 

5Check in / Check out

现在普通用户可以使用check out数据,并修改和提交了。

 

set cvsroot=:pserver:testuser@servername:/cvs/cvsrep.

cd workdir

cvs co .

 

…modify files…

 

cvs commit -m "do some change"

 

结论:

1。我们不需要给cvs管理员以服务器的root账号。而只需要一个公共的,可以访问cvs库目录的账号就可以。这个账户甚至不需要对外公布密码,也不允许远程登录。

2。我们不需要为每个cvs用户创建cvs服务器的系统账号,也无需依赖文件系统的访问控制。

3。我们可以为每个cvs库指定库管理员,由其对各自的cvs库进行用户的创建和权限分配。

4。普通用户可以自行修改cvs账户的密码,无需管理员干预。

5cvs服务器管理员的工作主要是创建cvs库(可以创建特定的系统账户以负责cvs库的创建和管理,不需要root账户),并负责服务器的日常维护,而日常的cvs管理由cvs库各自的管理员完成。

posted @ 2007-04-03 10:28 edsonjava 阅读(304) | 评论 (0)编辑 收藏
 
CVS服务器端和客户端的配置:
1、 下载并安装CVS服务器。
网址为:<http://www.cvsnt.com/cvspro/>,下载cvsnt 并安装,我们用的版本号为:
cvsnt-2.0.41.exe,
参考:<http://www.devguy.com/fp/cfgmgmt/cvs/cvs_admin_nt.htm>

2、 配置CVS服务器。
启动cvsnt控制面板Service control panel:
切换到repositories控制版,点击add按钮,输入你要建立的cvs服务器端文件保存的位置(最好输入绝对路径,例如:D:\cvs)。
切换到Advance控制版,勾选Impersonation enable 和 Use local users for pserver authentication instead for domain users和 lock server listens local。
注意:这样就可以用CVS服务器的用户名和密码进行登陆。
具体的添加用户和权限的操作,请参考:
<http://www.cvsnt.org/wiki/CvsCommand>
<http://www.cvsnt.org/manual/>

3、 安装Eclipse客户端。
略。参见:www.eclipse.org

4、 配置CVS客户端。
启动eclipse,切换到CVS Repositories面板,右击面板,选择new/Repositor Location。出现Add CVS Repository 对话框。
在对话框中填入:
Host: 服务器Ip地址
Repository path: cvs数据的路径,如上为:D:\cvs (为配置CVS服务器时的路径)
User: 服务器的用户名
Password: 服务器的用户名的密码
Connection type: pserver
点击Finish,完成建立过程。
在建立的CVS Repositories面板中,选择:head下的一个工程,即可进行各种操作。如:checkout
将新建的工程上传至cvs服务器:右击工程,选择:team-Share Project ,出现Share Project 对话框,然后选择一个repositories地址,点击next 下去,即可完成上传。
具体操作参考:<http://www.cvsnt.org/manual/>

5、 命令方式访问cvs服务器
参加:<http://www.cvsnt.org/manual/>
posted @ 2007-04-03 10:25 edsonjava 阅读(780) | 评论 (1)编辑 收藏
 
     摘要: WAS6.1 +HIBERNATE+SPRING +MYECLIPSE5.1.1 整合 概述 Hibernate 是传统的 Java 对象 ...  阅读全文
posted @ 2007-03-30 11:20 edsonjava 阅读(3371) | 评论 (5)编辑 收藏
 
本文介绍WebSphere下Oracle、SQL Server、Sybase、MySQL数据库连接池的配置方法,并给出相应调用连接池的示例。相对于Weblogic,WebSphere连接池的配置要稍微复杂一些,因为缺少相关的文档,需要一定的技巧和经验。特别是对于初学者,完整的配置好Websphere连接池还是有一定难度的。

一、系统准备
1.在相应的数据库中建立本文用到的表

create table TEST(C1 CHAR(10) )
insert into TEST values('FromTest')


2.准备环境变量,此部分往往会被初学者忽略。

image

点击“管理WebSphere变量”,ORACLE_JDBC_DRIVER_PATH的值输入操作系统中jar文件(classes12.jar)的位置。
“确认”后,界面最上方,点击“保存”,会再提示“保存”还是“放弃”,再选“保存”。

为方便起见,本文中,SQL Server的jar文件(msbase.jar、mssqlserver.jar、msutil.jar )、Sybase的jar文件(jconn2.jar)、mySQL的jar文件(mysql-connector-java-3.1.10-bin.jar)都放在同一目录(如:C:\oracle\ora92\jdbc\lib)。

3.本文中的所有例子测试均通过,环境:Windows2003、WebShpere5.1、ORACLE9I、SQL Server 2000、SYBASE12.5、MySQL5.0。

二、Oracle、SQL Server、Sybase、MySQL数据库连接池在WebSphere中的详细配置步骤

(一)、Oracle连接池的配置

1.进入管理控制台(http://localhost:9090/admin/)

2.选择:资源->JDBC提供程序,点击“新建”,建立JDBC提供程序。

image
点击“应用”后,类路径中,输入“${ORACLE_JDBC_DRIVER_PATH}/classes12.jar”,再点击“应用”。

3.定义数据源
点击界面中“数据源后”再点击“新建”,建立数据源。

image
JNDI取名ORACLE_JNDI,点击“应用”。

4.界面上点击“定制属性”,定义连接池的JDBC Driver、URL、用户名和口令等信息。
点击“URL”,URL的值输入:jdbc:oracle:thin:@localhost:1521:MYHORA,其中,localhost可以为ORACLE的IP地址,MYHORA是ORACLE的服务名称。
点击“确定”保存配置。
同样的方法输入:
driverType的值oracle.jdbc.driver.OracleDriver
databasename的值MYHORA
servername的值localhost
preTestSQLString的值为SELECT COUNT(1) FROM TEST
其余的取默认值。

5.本部分比较关键,是初学着比较困惑的地方。
我们看到,界面上并没有输入用户名、口令的地方,而没有用户名称、口令是无法连接数据库的。

image
在“定制属性”中点击“新建”,“名称”中输入user,“值”中输入数据库的用户名称,如:study,点击“确定”;
在“定制属性”中点击“新建”,“名称”中输入password,“值”中输入数据库的口令,如:study,点击“确定”;
我们看到,“定制属性”中多了两个我们自定义的属性user、password

image

6.保存配置,在“定制属性”界面的最上方点击“保存”。

7.测试连接

image

系统提示:成功信息,表明,连接池配置成功。
连接池配置成功后,WebSphere需要重新启动。

(二)、SQL server连接池的配置

SQL Server连接池的配置步骤同Oracle,具体的参数值:
JDBC 提供程序:下拉选择Microsoft JDBC driver for MSSQLServer 2000
Sybase连接池的配置步骤也同Oracle,具体的参数值:
常规属性中的名称:Microsoft JDBC driver for MSSQLServer 2000
常规属性中的描述:Microsoft JDBC driver for MSSQLServer 2000
常规属性中的类路径:
${ORACLE_JDBC_DRIVER_PATH}/msbase.jar
${ORACLE_JDBC_DRIVER_PATH}/mssqlserver.jar
${ORACLE_JDBC_DRIVER_PATH}/msutil.jar

常规属性中的实现类名:默认
数据源中的名称:SQLSERVER_JNDI
数据源中的JNDI:SQLSERVER_JNDI
定制属性中的databaseName:数据库名称
定制属性中的serverName:Sybase数据库服务器的名称或IP
定制属性中的portNumber:端口号
定制属性中的preTestSQLString:SELECT COUNT(1) FROM TEST
同Oracle,手工“新建”user和password属性,值为数据库的用户名和口令,该用户的缺省数据库必须为databaseName的值。
其他默认。

(三)、Sybase连接池的配置

JDBC 提供程序:下拉选择Sybase JDBC Driver
Sybase连接池的配置步骤也同Oracle,具体的参数值:
常规属性中的名称:SYBASE JDBC Driver
常规属性中的描述:SYBASE JDBC Driver
常规属性中的类路径:${ORACLE_JDBC_DRIVER_PATH}/jconn2.jar
常规属性中的实现类名:默认
数据源中的名称:SYBASE_JNDI
数据源中的JNDI:SYBASE_JNDI
定制属性中的databaseName:数据库名称
定制属性中的serverName:Sybase数据库服务器的名称或IP
定制属性中的portNumber:端口号
定制属性中的preTestSQLString:SELECT COUNT(1) FROM TEST
同Oracle,手工“新建”user和password属性,值为数据库的用户名和口令,该用户的缺省数据库必须为databaseName的值。
其他默认。

(四)、MySQL连接池的配置

MySQL连接池的配置步骤同Oracle稍有不同,JDBC 提供程序中并没有MySQL中的选项,选Oracle JDBC Driver就可以,实际运行中,WebSphere是以设置的参数为准的。

具体的参数值:
常规属性中的名称:MySQL JDBC Driver
常规属性中的描述:MySQL JDBC Driver
常规属性中的类路径:${ORACLE_JDBC_DRIVER_PATH}/mysql-connector-java-3.1.10-bin.jar
常规属性中的实现类名:com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
数据源中的名称:MYSQL_JNDI
数据源中的JNDI:MYSQL_JNDI

image

由于WebSphere没有缺省的MySQL选项,“定制属性”全部需要手工新建。具体的值要根据MySQL的实际环境要做相应修改(petshop是我机器上的数据库名称)。

三、应用程序中测试连接池。

1. jsp程序中测试连接池

附件中的TestConnPoolWeb.ear文件直接发布后,
运行:http://localhost:9080/TestConnPoolWeb/oracle_pool.jsp,结果:ORACLE_JNDI:FromTest
运行:http://localhost:9080/TestConnPoolWeb/sqlserver_pool.jsp,结果:SQLSERVER_JNDI:FromTest
运行:http://localhost:9080/TestConnPoolWeb/sybase_pool.jsp,结果:SYBASE_JNDI:FromTest
运行:http://localhost:9080/TestConnPoolWeb/mysql_pool.jsp,结果:MYSQL_JNDI:FromTest

2. 程序说明

得到连接的方法:

private static Connection getConnection(String strConnPoolJndi) throws NamingException, SQLException {
Context ctx = null;
ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(strConnPoolJndi);
Connection conn = ds.getConnection();

return conn;

}

参数strConnPoolJndi分别为:ORACLE_JNDI、SQLSERVER_JNDI、SYBASE_JNDI、MYSQL_JNDI,对于相应的数据库。
从表中查询一条数据:

public static String getDBData(String strConnPoolJndi) {
String strReturn="";
Connection conn = null;
Statement st = null;
ResultSet rs = null;

try {
conn = getConnection(strConnPoolJndi);
st = conn.createStatement();
rs = st.executeQuery( "select C1 from TEST" );

if (rs.next()) {
strReturn = rs.getString(1);
}

}
...

jsp中打印出表中的一条记录:

<%
out.println("ORACLE_JNDI:" + test.pool.TestPool.getDBData("ORACLE_JNDI"));
%>


附件TestConnPoolWeb.ear(包括源程序)
posted @ 2007-03-28 11:08 edsonjava 阅读(1672) | 评论 (0)编辑 收藏
 
1.环境 sun jdk1.4.2,eclipse3.1 MyEclipse 4.0
2.介绍:MyEclipse配置Websphere6的时候需要做比配置其它server更多的工作;
        Websphere6必须和MyEclipse安装在一个机器上;
        Websphere6必须使用IBM的jdk,位置在<was-install-dir>/java;
        只有MyEclipse Enterprise J2EE project可以部署在Websphere6上。
3.Websphere6安装和配置
第一步 点击创建概要表(profile);
第二步 输入概要表名称(例如 MyEclipse);
第三步 选择概要表路径 D:\IBM\WebSphere\AppServer\profiles\MyEclipse;
第四步 输入结点名:MyEclipseNode,主机名:127.0.0.1;
第五步 输入端口号,默认端口即可;
第六步 不选做为windows服务;
第七步 完成;
第八步 启动服务器,打开管理控制台;
第九步 点击服务器-应用服务器,然后点击服务器名称;
第十步 查看标签“配置”->故障诊断->记录和跟踪;
第十一步 点击“JVM日志”->配置标签,修改“文件名”为console,点击确定;
第十二步 提示已更改了您的本地配置。单击“保存”应用对主配置的更改,点击“保存”,下一页再点“保存”;
第十三步 推出管理控制台,停止服务器;
4.MyEclipse配置
第一步 配置 Window > Preferences > MyEclipse > Application Servers > WebSphere 6;
第二步 配置JDK JRE home directory 为 /java,WebSphere只能运行在IBM JDK上;
        (WebSphere is known to run on a heavily modified IBM JDK and running it with Sun's JDK, JRockit, GCJ or other JDKs simply will not work)
第三步 创建 Web project 例如“IBMTestWeb”,再创建 Enterprise project 例如“IBMTest”;
第四步 部署 必须点击菜单条上的部署的快捷方式 Deploy MyEclipse J2EE Project to Server;

(MyEclipse Deployment Manager must be launched by clicking the button on your toolbar as shown in the screenshot below)
第五步 部署 IBMTest,选择第二项“Packaged Archive” 部署-完成;
第六步 在MyEclipse中启动 Websphere6,等待出现“为电子商务开放服务器 server1”,即启动完成;
(wait for the Server server1 open for e-business message as before.)
第七步 打开Websphere6的管理控制台 应用程序->安装新的应用程序,选择
           D:\IBM\WebSphere\AppServer\profiles\MyEclipse\installableApps\IBMTest.ear,点击下一步;
第八步 选择“启用类重新装入,点击下一步;
第九步 选中 “IBMTestWeb.war”,点击下一步 ;
第十步 选中 “IBMTestWeb.war”,点击下一步 ;
第十一步 提示“应用程序 IBMTest 安装成功。”后,点击  “保存到主配置”,最后启动该应用程序;
第十二步 删除以前部署的包,重新部署 IBMTest,选择第一项“Exploded Archive”,下面选择“Delete remote resource before deployment” 部署-完成;


 最终访问http://127.0.0.1/IBMTestWeb/index.jsp  这样就可以实时调试,实时更新了
posted @ 2007-03-28 10:41 edsonjava 阅读(665) | 评论 (0)编辑 收藏
 

摘自 IBM WebSphere 开发者技术期刊

引言

Spring 和 Hibernate 是两个开放源代码项目,有些客户可能有兴趣使用它们。本文描述如何配置这些框架,以便与 WebSphere Application Server 一起在各种场景中使用。不过,本文不是对某一框架的认可或推荐。IBM 也不直接支持其中任何一个开放源代码项目。IBM 支持有限制地使用这些应用程序框架(如下面所述),就像它支持在 WebSphere Application Server 上运行的任何客户的应用程序一样。本文还描述一些不支持的场景,在 WebSphere Application Server 和堆栈产品中应该避免此场景。





回页首


产品和客户技术支持

一个用户理应关注的问题是那些使用开放源代码的项目所获得的支持,以及使用开放源代码对供应商支持其许可产品的影响。IBM 了解某些客户可能希望将非 IBM 的框架和 IBM WebSphere Application Server 结合使用,并且在为客户提供一些信息,以促进他们为 IBM WebSphere Application Server 创建最可靠的操作环境。IBM 考虑了客户安装的开放源代码和应用程序框架,它们或者绑定为应用程序的一部分,或者作为共享库成为应用程序代码的一部分。在使用开放源代码项目时通过谨慎地利用此信息,客户可以满怀信心地使用 IBM 产品,并继续访问 IBM 产品和技术支持。如果在将这些框架与 WebSphere 产品结合使用时遇到问题,IBM 将尽力确保 WebSphere 产品不出现问题。

如果客户认真研究了本文中的建议,并记住避免和理解以下几个关键点,则预期可以安全地在 IBM 产品上使用 Spring 和 Hibernate 之类的框架:

  • 客户必须确保按 WebSphere Application Server 允许的方式使用这些框架。具体说来,这意味着客户在使用内部产品接口时,不应使用框架——遗憾的是,许多开放源代码框架在未经认真配置的情况下就这样使用了。客户应避免在 WebSphere 上明确记录应避免的场景。

  • 对于开放源代码框架,客户应该确保理解并能够访问他们与 WebSphere Application Server 一起使用的框架的匹配源代码和二进制。

  • 建议客户从开放源代码社区或使用开放源代码社区的合作伙伴那里获取框架的补救性服务。

有关 IBM 支持和策略的详细信息,请参考 IBM Support HandbookWebSphere Application Server Support Statement

尽管在开放源代码环境中使用 WebSphere Application Servers 时按照本文的建议做法有助于增强您的体验,但本文并没有列出开放源代码组件影响 WebSphere Application Server 操作或其他组件操作的所有情况。使用开放源代码的用户务必检查所有组件的规范,以避免出现许可、支持和技术问题。

在本文的剩余部分中,当使用术语“支持”或“受支持”时,描述的用法仅限于使用 IBM 有文档记录的功能。作者已尽了最大努力来提供有关如何配置和使用这些框架的建议,以确保其用法与记录的产品行为一致。





回页首


Hibernate

Hibernate 是传统的 Java™ 对象 (POJO) 的开放源代码持久性框架,它通过 XML 配置文件提供 POJO 到关系数据库表的与对象相关的映射。Hibernate 框架是应用程序调用的、用于数据持久性的数据访问抽象层。此外,Hibernate 还提供了从 Java 类到数据库表(以及从 Java 数据类型到 SQL 数据类型)的映射以及数据查询和检索功能。Hibernate 生成必需的 SQL 调用,还负责结果集处理和对象转换。

Hibernate 由 Gavin King(Hibernate 项目的奠基人)领导的软件开发团队开发,目的是解决 EJB 2.x 实体 EJB 中存在的诸多缺陷。Hibernate 与 JBoss Group 紧密关联,这是由于 JBoss 雇佣了大量的一流 Hibernate 开发人员的结果。最近,在 Java Community Process (JCP) 中的持久性框架的发展中已涉及 Hibernate,并且 Hibernate 的许多概念已合并到 Java Persistence API (JPA) 规范中。最新版本的 Hibernate 将发展为符合 JPA 规范。JPA 似乎将成为主要的 Java 持久性 API。具体说来,JPA 是 Java EE 5 必要部分。(有关如何使用 Hibernate 的 developerWorks 文章,请参见参考资料。)

尽管下文的描述中支持在 WebSphere Application Server 上使用 Hibernate,但是 IBM 并不直接支持 Hibernate。任何人使用 Hibernate 时都需要按 Hibernate Project Support page 的描述获取对它的支持。客户应该保留与开源项目相关的源数据,以用于问题调试。

使用场景

以下场景描述了有关如何将 Hibernate 与 WebSphere Application Server 和 WebSphere 堆栈产品结合使用的一些可能场景。这些仅是示例场景,不应认为是推荐的场景。前文已提到,客户应该保留与开源项目相关的源数据,以用于问题调试

使用 WebSphere Application Server 数据源

为使 Hibernate 从 WebSphere Application Server 获取数据库连接,必须使用 Java EE(以前称为 J2EE)规范中强制规定的资源引用。这可以确保 WebSphere Application Server 能够为连接池、事务语义和隔离级别提供正确的行为。通过设置 hibernate.connection.datasource 属性(在 Hibernate 配置文件中进行了定义)将 Hibernate 配置为从 WebSphere Application Server 检索数据源,以引用在模块的部署描述符中定义的资源引用(例如 java:comp/env/jdbc/myDSRef)。例如:

<property name="hibernate.connection.datasource">
	java:/comp/env/jdbc/myDSRef
</property>

Web 应用程序的 Java EE 资源引用在 WAR 文件级别定义,这意味着容器中的所有 Servlet 和 Java 类均共享资源引用。在 EJB 模块内部,资源引用在各个 EJB 组件上定义。这意味着,如果许多 EJB 组件都使用相同的 Hibernate 配置,则每个 EJB 必须在每个 EJB 组件上定义相同的引用名称。这会导致复杂的情况,稍后我们将进一步讨论。

配置了数据源之后,确保 Hibernate 正常工作的下一个步骤是正确配置事务支持。

事务策略配置

为了正确地使用事务,Hibernate 需要两个重要部分的配置。第一个部分是 hibernate.transaction.factory_class,它定义事务控制,第二个部分是 hibernate.transaction.manager_lookup_class,它定义注册事务同步的机制,这样,当持久性管理器需要与数据库同步更改时会在事务端得到通知。对于事务控制,同时支持容器管理的配置和 Bean 管理的配置。将 Hibernate 和 WebSphere Application Server 结合使用时,必须在 Hibernate.cfg.xml 中设置以下属性:

  • 对于容器管理的事务:

    <property name="hibernate.transaction.factory_class">
    	org.hibernate.transaction.CMTTransactionFactory
    </property>
    <property name="hibernate.transaction.manager_lookup_class">
    	org.hibernate.transaction.WebSphereExtendedJTATransactionLookup
    </property>

  • 对于 Bean 管理的事务:

    <property name="hibernate.transaction.factory_class">
    	org.hibernate.transaction.JTATransactionFactory
    </property>
    <property name="hibernate.transaction.manager_lookup_class">
    	org.hibernate.transaction.WebSphereExtendedJTATransactionLookup
    </property>
    <property name="jta.UserTransaction">
    	java:comp/UserTransaction
    </property >

jta.UserTransaction 属性将工厂类配置为从 WebSphere 容器获取 UserTransaction 对象实例的实例。

WebSphere Application Server V6.x 和更高版本在 WebSphere 平台上支持 hibernate.transaction.manager_lookup_class 属性,WebSphere Business Integration Server Foundation V5.1 和更高版本也支持此属性。此属性将 Hibernate 配置为使用在 WebSphere Business Integration Server Foundation V5.1 和 WebSphere Application Server V6.0 中引入的 ExtendedJTATransaction 接口,WebSphere ExtendedJTATransaction 接口建立了一个模式,并通过 JTA 1.1 规范在 Java EE 5 中正式确立。

WebSphere Application Server 环境中 Hibernate 的使用模式

当结合使用 Hibernate 和 WebSphere Application Server 时,Hibernate 的“按请求会话”和“长对话”模式均可使用。客户必须选择适用于其应用程序的模式,不过我们主张使用“按请求会话”模式,因为它可以提供更好的扩展性。

  • 多个隔离级别

    可共享的连接通过让多个资源用户能够共享现有的连接,改进了在 WebSphere Application Server 中的性能。不过,如果可共享的连接和多个隔离级别都是必需的,则为每个连接配置定义单独的资源引用和 Hibernate 会话工厂。不能够更改共享连接的隔离级别。因此,也不可能使用 hibernate.connection.isolation 属性在共享连接上设置隔离级别。有关连接共享的策略和约束的详细信息,请参见 在 WebSphere Application Server V5 中共享连接。(尽管本文通常适合于在 WebSphere Application Server V5 上使用的所有共享连接,但是连接共享建议仍适用于在 V6.x 上运行的 Hibernate。)

  • Web 应用程序

    可以在 HttpSession 对象中使用和存储 Hibernate 的“长对话”会话;不过,Hibernate 会话持有活动实例,由于可能需要将会话序列化或将其复制到其他集群成员,因此将其存储在 HttpSession 中不是可扩展模式。最好使用 HttpSession 来存储断开连接的对象(只要它们非常小,即 10KB 到 50KB),并且在需要更新时,重新将它们与新的 Hibernate 会话关联。这是因为 HttpSession 最适用于书签,而不适用于缓存。在 Improving HttpSession Performance with Smart Serialization 中讨论了如何使 HttpSession 的内存使用率降至最低。不是将 HttpSession 用作缓存,而是考虑使用 ObjectGrid 或 DistributedObjectCache 之类的 WebSphere 数据缓存技术,这在下一部分进行介绍。

有关高性能、可扩展应用程序的最佳实践,强烈建议您阅读 Performance Analysis for Java Websites 一书。

集成二级缓存

Hibernate 会话表示工作单元的范围。在 Hibernate 会话的生命周期中,Session 接口管理持久性。通常,它通过保持对单一线程有效的一级缓存实例并维护它负责的映射实体类实例的识别或状态做到这一点。在完成工作单元(会话)时释放缓存。还可以将二级缓存配置为在 SessionFactory 的所有会话之间共享(包括在群集之间共享)。请注意,在 Hibernate 中进行缓存会导致一些需要解决的问题。第一,对于数据库的外部更改或跨群集更改,无法确保缓存的一致性(除非使用识别群集的缓存)。第二,其他层(如数据库)可能已经缓存,从而使 Hibernate 缓存的值降至最低。在进行应用程序设计时,必须认真考虑这些问题,但是这些问题超出了本文的讨论范围。

在发布时,没有确定与 WebSphere Application Server 相关的 Hibernate 群集识别缓存的行为,因此,还不能确定是否支持该行为,我们对此不做进一步的讨论。所以,需要分布式缓存的客户应当考虑创建使用属性 hibernate.cache.provider_class 实现 org.hibernate.cache.CacheProvider 的类,这会在 WebSphere 中使用两个分布式缓存实现中的一个。

Hibernate 附带了几个预配置的缓存。在 Hibernate Cache 文档页中可以找到关于这些缓存的信息。对于只读数据,一个内存缓存可能就足够了。不过,当聚集应用程序并需要群集识别缓存时,本地只读缓存是不够的。如果需要分布式缓存,我们建议使用 WebSphere 提供的分布式缓存实现之一。可以将它们用作 Hibernate 的二级缓存:

在 WebSphere Enterprise Service Bus 和 WebSphere Process Server 中使用 Hibernate

WebSphere Process Server 和 WebSphere Enterprise Service Bus (ESB) 将 Service Component Architecture (SCA) 和 Service Data Objects (SDO) 用作 SOA 的装配和编程模式。(请参见参考资料,了解关于 SCA 和 SDO 的更多信息。)SCA 组件不是 Java EE 组件,因此它们没有资源引用,但是改为依靠服务和适配器来连接系统。在构建 Java SCA 组件时,不能使用资源引用;因此,SCA 组件不能直接使用 Hibernate。

在这种情况下,应将 Hibernate 持久性隐藏在某种 Facade 后面。有两个备选方案:

  • 创建本地 EJB 会话 Facade 以包装 Hibernate 持久性。会话 Facade 提供适配器逻辑,以便将 Hibernate 实体 POJO 映射到服务数据对象并返回。然后集成开发人员可以使用 EJB 导入调用会话 Facade,并以紧密耦合方式使用对应的服务质量 (QoS) 调用它。

  • 创建 EJB Web 服务会话 Facade 以包装 Hibernate 持久性。然后集成开发人员可以使用 Web 服务导入调用持久性的 Web 服务。这不需要构建 POJO 到 SDO 的转换程序,由于目前 SCA 对数据类型只使用 SDO。图 1 说明了使用两个模式的业务流程,但该流程的详细信息不在本文的讨论范围之内。


图 1. 示例业务流程
图 1. 示例业务流程

WebSphere Application Server V6.1 上的 Hibernate JPA API

Hibernate 的 JPA 支持提供 JPA 标准持久性,并且是专有 Hibernate API 的较好备选方案。Hibernate 的 JPA 实现需要基于 Java EE 5 的运行时,因此,它仅在 WebSphere Application Server V6.1 或更高版本上运行。在发布时,Hibernate 的 JPA 支持不能在 WebSphere z/OS 和 iSeries 平台上运行。Hibernate 文档描述了如何使用 Hibernate 的 JPA 实现包装和部署应用程序。

避免的场景

以下各部分将描述不支持的配置和用例。这仅表示当前已确定的不支持的场景。其他不支持的场景将在以后确定。

不支持的事务配置

Hibernate 文档描述了用于在 WebSphere Application Server 版本 4 和 5 产品上运行的事务策略配置,但是这些配置使用内部 WebSphere 接口,在那些早期版本上不受支持。在上面的事务策略配置部分中描述了 Hibernate 唯一支持的事务配置。正如前面所述,这意味着仅在 WebSphere Application Server V6.x 之后(包括 V6.x)的版本和 WebSphere Business Integration Server Foundation V5.1 上支持使用 Hibernate。

不可交互操作的/不可移植的功能

JPA 规范中的 3.2.4.2 部分描述了可能导致互操作性和潜在的可移植性问题的情况。这与结合使用延迟加载(即 @Basic(fetch=LAZY))和分离对象有关。将分离对象合并回会话时,JPA 将检查该对象,并使用任何更改值来更新数据存储。不过,数据对象是简单的 POJO。在分离时,如果部分 POJO 状态没有加载,则在合并回去时可能显示为已更改。要使它正常工作,供应商必须实现特定于其运行时的序列化技术。这不是互操作的,语义也不能移植。





回页首


Spring

通常将 Spring 描述为轻量级容器环境,但是将其描述为用于简化开发的框架可能更适当。其主要概念是使用依赖项注入 (DI) 和面向方面的编程 (AOP) 来简化和平稳地进行从开发到测试再到生产的转换。

Spring 框架是一个流行的开放源代码框架,它由 Interface21, Inc. 根据 Rod Johnson 发表的关于依赖项注入 (DI) 的设计模型开发。Spring 可以在独立应用程序或应用服务器中使用。由于 Spring 提供的一些服务通常由应用服务器运行时提供,所以需要认真设计 Spring 与应用服务器的结合使用,以避免在 Spring 实现和应用服务器提供的实现之间发生冲突。

本部分其余内容将描述使可能发生的实现冲突或矛盾点降至最低的用例,还将描述应避免的其他矛盾点(如果可能)。

尽管下文的描述中支持在 WebSphere Application Server 上有限使用 Spring,但是 IBM 并不直接支持 Spring。任何人使用 Spring 时都需要按 Spring Product Support page 的描述获取对它的支持。客户应该保留用于帮助调试问题的与开放源代码项目相关的源。

使用场景

以下部分将描述 Spring 在 WebSphere Application Server 上的一些使用场景。不过,应由客户负责确定这些场景是否适合他们的具体情况。IBM 不支持、也不建议使用 Spring。本文仅演示在 WebSphere Application Server 上使用 Spring 的一组场景,并演示必须如何自定义 Spring 才能与 WebSphere Application Server 一起运行。

简单的 Bean 逻辑容器

使用 Spring 的最常用的场景之一是使用简单的 Java Bean 类配置并驱动业务逻辑。本文稍后描述的避免了问题的 Spring 应用程序在 WebSphere Application Server 中运行时不应该有任何问题。Spring documentation page 应提供了使用 Spring Bean 构建应用程序的足够信息。此处不存在任何特定于 WebSphere 的内容。

在 WebSphere 中使用 Spring DAO 框架

下面是关于在 WebSphere Application Serveruse 上使用 Spring DAO 功能的三个示例:

  • 基本型 DAO 用法

    WebSphere Application Server 管理在应用服务器执行环境中使用的资源。需要访问 JDBC 数据源之类资源的 Spring 应用程序应该利用 WebSphere 管理的资源。为此,需要执行以下若干步骤。

    1. 在开发过程中,应该使用资源引用配置 WAR 模块。例如:

      <resource-ref>
      	<res-ref-name>jdbc/springdb</res-ref-name>
      	<res-type>javax.sql.DataSource</res-type>
      	<res-auth>Container</res-auth>
      	<res-sharing-scope>Shareable</res-sharing-scope>
      </resource-ref>
      

    2. 对于 EJB JAR 文件,应该在需要访问数据源的每个 EJB 中声明同一资源引用。

    3. 然后使用与以下类似的内容在 Spring 配置文件中声明数据源 Bean:

      <bean id="WASDataSource" 
          class="org.springframework.jndi.JndiObjectFactoryBean">
      	<property name="jndiName" 
      		value="java:comp/env/jdbc/springdb"/>
      	<property name="lookupOnStartup" 
      		value="false"/>
      	<property name="cache" 
      		value="true"/>
      	<property name="proxyInterface" 
      		value="javax.sql.DataSource"/>
      </bean>

      这将配置 Bean,以便从模块的配置引用查找数据源。注意,jndiName 属性值匹配与资源引用中声明的资源引用名称连接的模式 java:comp/env/

    4. 然后,Spring 应用程序可以在适当情况下使用数据源 Bean。

    5. 将应用程序部署到 WebSphere Application Server 时,必须配置资源提供程序和资源数据源,以便由 Spring 应用程序资源引用使用。在部署过程中,在模块的部署描述符中声明的资源引用将绑定到应用服务器的配置数据源。

  • 事务型 DAO 用法

    在 Spring 中,有许多方法可以驱动事务控制下的资源更新。这包括编程形式和声明形式。声明形式有注释形式和非注释形式。在使用 Spring 事务支持时,获得权限的重要方法是适当地配置 Spring 对底层事务管理器的使用。在 WebSphere Application Server 中,Spring 使用的事务管理器的唯一支持形式如下:

    <bean d="transactionManager"
    class="org.springframework.transaction.jta.JtaTransactionManager">
    	<property name="autodetectTransactionManager"
    		value="false"/>
    </bean>

    可以将此事务管理器 Bean 提供给 JdbcTemplate 类,或通过事务策略的声明形式利用。有关为什么这是唯一形式的详细信息,请参见避免的场景部分。

  • 休眠型 DAO 用法

    Spring 记录将 Hibernate 和 Spring 一起使用的基本设置。在 WebSphere Application Server 环境中使用 Hibernate 和 Spring 时,有必要将上述对二者的配置要求合并在一起。正如前面所述,在 Spring 和 Hibernate 中使用 JNDI 访问事务管理的资源和适当配置是必要的。

Spring Web MVC

Spring 的 Web MVC 框架在某一时间内一直是其他框架的备选框架。由 WebSphere Application Server 提交、使用和支持的 Web MVC 框架包括 JSF 和 Struts。Spring 参考手册描述了如何将 Spring 与这些 Web 框架集成。尽管 WebSphere Application Server 支持上面的任何用法,但 IBM 仅为 WebSphere Application Server 附带的框架提供缺陷支持。

计划和线程池

如果需要异步运行代码,那么应该使用 WorkManagerTaskExecutor。在 Spring 中,这是唯一使用由 WebSphere Application Server 托管的线程池的 TaskExecutor 实现。其他 TaskExecutor 实现可以启动非托管线程。有关非托管线程的详细信息,请参见避免的场景

在 WebSphere Application Server 管理控制台中,通过导航到 Resources => Asynchronous beans => Work managers 可以对 WorkManager 进行设置。然后可以在 Spring 配置文件中使用资源的 JNDI 名称来定义 WorkManagerTaskExecutor。

Spring Portlet

Spring Portlet 可以在 WebSphere Portal V6.0 和 WebSphere Application Server V6.1 Portlet 容器中运行。有关 Spring Portlet 的示例集,请参见 Spring Portlet MVC。在 WebSphere Application Server V6.1 Portlet 容器中运行 Portlet 需要创建其他 Web 应用程序才能定义 Portlet 的布局和聚合。从 WebSphere Application Server 信息中心可以获得关于如何使用 Portlet 聚合器标记库的信息,在文章 探索 WebSphere Application Server V6.1 Portlet 容器: 第 1 部分:Portlet 容器介绍 中的其他补充信息中也可找到。

通常结合使用 JSF 和 Portlet 进行呈现。关于如何将 Spring、Hibernate、JSF 和 WebSphere Portal 组合起来运行的信息可以通过文章 Configuring Hibernate, Spring, Portlets, and OpenInSessionViewFilter with IBM WebSphere Portal Server 了解。

避免的场景

已经知道以下场景在 WebSphere Application Server 环境中会出现问题,应该避免它。列出这些场景的原因有许多,不过,最常见的原因是 Spring 使用了内部 WebSphere 接口。这些内部接口的使用破坏了 WebSphere Application Server 保证应用程序环境的完整性和管理应用程序资源的能力。应该避免使用任何依赖于内部 WebSphere 接口的 Spring 功能。

REQUIRES_NEW 和 NOT_SUPPORTED 事务策略

WebSphere Application Server 为维护数据完整性提供了可靠的环境。它提供此环境的方法之一是始终确保应用程序逻辑在事务范围内执行。在容器将要执行业务逻辑时,它会检查事务状态。如果全局事务不是动态的,则容器会创建并管理新的 local transaction containment (LTC) 范围。容器将在业务逻辑结束时完成 LTC,并在任何应用程序启动的全局事务过程中将它挂起。这可以确保在业务逻辑执行结束时总是正确地清除资源。

Spring 为管理事务提供了机制。不过,它支持的事务策略超出了通常可以在应用程序级别处理的策略集的范围。具体说来,它为 REQUIRES_NEW 和 NOT_SUPPORTED 的 EJB 容器管理的语义提供它自已的等效支持。这两个容器管理的策略都需要挂起和恢复事务的功能,但是为使用 EJB 容器而保留了此功能(不适当的使用会导致数据完整性问题)。为提供此行为,Spring 使用了内部 WebSphere 接口,但不知道它对容器的影响。使用内部接口挂起和恢复事务将破坏 Web 和 EJB 容器管理资源和 LTC 的功能,并可能使容器处于未知状态,从而可能导致数据损坏。

以下两个步骤可以避免此场景:

  • 不使用需要挂起和恢复语义的 Spring 事务策略。
  • 不配置 Spring JtaTransactionManager 使其支持挂起和恢复语义。

第一是应用程序设计时间限制。如果您构建应用程序,请不要在语义中设计需要使用 Spring 来挂起和恢复事务。如果需要挂起和恢复语义,请改为使用无状态会话 EJB。

第二是部署时间配置约束。如果在 WebSphere Application Server 中部署基于 Spring 的应用程序,则该应用程序在尝试使用 Spring 的挂起和恢复语义时将会失败。以这种方式失败比随机数据损坏错误要好得多。下面是必须在 WebSphere Application Server 中配置 Spring JtaTransactionManager 的正确方法:

<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
	<property name="autodetectTransactionManager" 
		value="false"/>
</bean>

这将把 transactionManager Bean 配置为使用 UserTransaction API 来控制事务的开始、提交和回滚。这使得可以不使用挂起和恢复语义来支持事务控制。

上面所述的 Spring JtaTransactionManager 用法不支持与现有事务同步的 Spring 注册功能。要做到这一点,需要使用 WebSphereTransactionManagerFactoryBean。使用 WebSphereTransactionManagerFactoryBean 的客户或调用内部事务管理器接口的任何其他类将不受支持。

JDBC 本机连接

当各种 JDBC 操作需要与本机 JDBC 资源交互时,Spring 可提供访问本机连接的机制。当在 JdbcTemplate 类上设置了 NativeJdbcExtractor 类时,JdbcTemplate 类才可以利用此功能。设置 NativeJdbcExtractor 类后,当与 WebSphere Application Server 一起使用时,Spring 总是向下找到本机 JDBC 连接。这将忽略以下 WebSphere QoS 功能和优点:

  • 连接处理跟踪和再关联
  • 连接共享
  • 事务状态
  • 池管理
  • 最后的代理事务参与。

这带来的另一个问题是 WebSphereNativeJdbcExtractor 类将依赖于内部 WebSphere 适配器类。这些内部类可能因 WebSphere Application Server 的版本而异,并且以后可能更改,从而中断依赖于此功能的应用程序。

在 WebSphere Application Server 上不支持使用 NativeJdbcExtractor 类实现(例如 WebSphereNativeJdbcExtractor),客户应避免需要它的场景。备选方案是使用 WebSphere Application Server WSCallHelper 类来访问非标准供应商的数据源扩展。

类加载器的缺点

Spring 和 WebSphere Application Server 都使用多个开放源代码项目,遗憾的是,它们共有的项目版本并不总是匹配。应该将 Spring 依赖项包装为应用程序的一部分,并且应该按照下面的描述设置服务器以避免冲突。否则,类加载器可能无法为运行时或应用程序加载适当的版本。通常,这将导致在日志中出现有关类的版本不匹配、ClassCastExceptions 或 java.lang.VerifyErrors 等异常。

其中一个示例是使用 Jakarta Commons Logging。配置供应用程序使用的 Jakarta Commons Logging (JCL),或者使用不是由应用服务器提供的其他版本的 JCL(例如,使用应用程序代码嵌入的 JCL)需要在 WebSphere Application Server 上进行特定的配置。有关如何配置部署的应用程序,以使用内嵌版本的常用技术的策略,请参见 Integrating Jakarta Commons Logging。请关注支持网站,了解是否提供了如何在 WebSphere Application Server V6.x 产品上配置内嵌 JCL 的更新。这仅仅是冲突的一个示例。其他示例可能包括应用程序使用的 JDOM 或特定版本的 JavaMail。不支持将 WebSphere Application Server 的 JAR 文件替换为这些或具有更高版本或不同版本的其他包。

在 WebSphere Application Server 上困扰 Spring 用户的另一个类加载器问题是 Spring 加载资源的方法。资源可以包括消息绑定之类的内容,通过类加载器层次结构和在层次结构中查找资源的各种策略,能够在非目标位置找到使用公共名称的资源。可以使用 WebSphere Application Server 类加载器查看器来帮助解决此问题。合并资源和包括其他版本的公共库可能需要应用程序将资源重命名为唯一的名称。

非托管线程

某些 Spring 场景可能导致创建非托管的线程。非托管线程对 WebSphere Application Server 是未知的,并且不能访问 Java EE 上下文信息。此外,它们可以在 WebSphere Application Server 不知道的情况下利用资源,存在管理员不能控制其数量和资源使用情况的问题,在发生故障时,它们还阻止应用服务器正常关机或恢复资源。应用程序应该避免导致启动非托管线程的任何场景:

  • registerShutdownHook

    第一种场景是在使用 Spring AbstractApplicationContext 或它的一个子类时。registerShutdownHook 是一个公共方法,它可以创建线程并将其注册到 Java 虚拟机,以便在关机时运行以关闭 ApplicationContext。应用程序可以避免这一点,方法是利用从 WebSphere 容器接收的常规生命周期通知来显式调用 ApplicationContext 上的关闭。

  • 入站 JMS 消息

    Spring JMS MessageListenerContainer 类利用启动非托管线程(例如 SimpleAsyncTaskExecutor)的类来侦听传入的 JMS 消息。Spring JMS 容器不仅可以启动非托管线程,而且还可以使用无法由 Java EE 环境中的应用程序调用的 JMS API。(有关详细信息,请参见 Java EE 规范 J2EE.6.6 部分。)

    可以使用 Spring 远程处理 API 生成 JMS 消息,但是客户应该使用消息驱动的 Bean 处理传入的消息,避免使用 Spring JMS MessageListenerContainer。这可以使用容器适当地控制和恢复资源和事务。

  • JMX 服务器

    应用程序应避免使用 Spring JMX ConnectorServerFactoryBean,它是启动非托管线程的另一个类。WebSphere Application Server 已经向 JMX 服务器提供了多个受支持的连接器协议。应用程序可以向 WebSphere Application Server 的 JMX 服务器注册 MBean,并对它们进行远程访问。

  • 非集成的计划包

    Spring 提供或集成了大量的计划包。只有与 WebSphere Application Server 托管的线程一起使用的 Spring 计划包是 CommonJ WorkManager 计划包。其他包(如 quartz 和 JDK Timer)会启动非托管线程,应该避免使用。

  • WeakReferenceMonitor

    Spring 为简化开发 EJB 组件提供了方便的类。请注意,这些方便的类会生成 WeakReferenceMonitor 用来执行清除操作的非托管线程。





回页首


结束语

如果经过充分考虑避免了问题场景,则可以在 WebSphere 平台上以完全支持的方式使用 Hibernate 和 Spring。尤其是,客户必须确保他们使用这些框架不涉及使用内部 WebSphere 接口。获得 IBM 的 WebSphere Application Server 支持的用户如果在将 WebSphere Application Server 和 Spring 或 Hibernate 一起使用时遇到问题,可以获得 IBM 的帮助来诊断问题,除非问题被确认为使用了不支持的场景,或不是 WebSphere Application Server 的问题。客户应该按照相应项目网络上的说明自行从其他公司获得对 Hibernate 和 Spring 框架的支持。


posted @ 2007-03-27 17:17 edsonjava 阅读(445) | 评论 (0)编辑 收藏
 
下载mysql jdbc 驱动

1.建立用户自定义配置jdbc提供程序 (!!!)
配置类路径指向 mysql jdbc driver jar file
实现类名为:com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource

2.建立数据源
设置jndi名等职


3.定制连接池属性(!!!)
自定义下列名称和值
名称    值
databaseName schemaname?autoReconnect=true
factory com.mysql.jdbc.jdbc2.optional.MysqlDataSourceFactory
password password
port 3306   
serverName localhost  
user username  
4.重启应用服务器,测试连接
posted @ 2007-03-27 16:59 edsonjava 阅读(697) | 评论 (0)编辑 收藏
 

 



  轻量级的企业应用开发越来越受到广大J2EE应用开发者的追捧,而Spring框架又是轻量级容器的杰出代 表。由于Spring的使用日渐广泛,因此已有许多基于WebSphere应用服务器(WAS)的应用采用了Spring框架。本文首先介绍使用 Spring开发Web应用的基本问题,然后结合WebSphere应用服务器,讲述Spring应用如何结合容器提供的服务。文章目的是与大家一起探讨 如何更好的采用Spring框架开发基于WebSphere应用服务器的应用。

  1、Spring框架的主要思想描述

   Spring框架的核心思想我们可以用两个字来描述,那就是"解耦"。应用程序的各个部分之间(包括代码内部和代码与平台之间)尽量形成一种松耦合的结 构,使得应用程序有更多的灵活性。应用内部的解耦主要通过一种称为控制反转(IOC)的技术来实现。控制反转的基本思想就是本来由应用程序本身来主动控制 的调用等逻辑转变成由外部配置文件来被动控制。通常我们用一个所谓的好莱坞原则(Don't call me. I will call you.)来比喻这种控制反转的关系。由于控制反转的概念相对比较广泛,很多应用服务器实际上也实现了不同程度的控制反转技术,只是这些应用服务器对应用 程序的侵入性太强。因此Martin Fowler专门写了一篇文章讨论控制反转这个概念,并提出一个描述更为准确的概念,叫依赖注入(Dependency Injection)。

   Spring框架中的各个部分都充分使用了这种依赖注入的技术实现,从而给应用以最大的灵活度。实际上,这种依赖注入的参数化应用控制并不是 Spring的首创,比如IBM的多渠道应用整合平台(Branch Transformation Toolkit,BTT)很早就采用了这种外部参数化控制的技术。BTT中的"对象工厂"与Spring框架中的BeanFactory也有着异曲同工之 妙。

  Spring框架另外一个比较重要的技术是它对于面向切面的编程(AOP)的支持。随着应用复杂度的逐渐上升和对应用灵活性要求 的提高,IT逻辑和业务逻辑尽量分离的呼声也越来越高。AOP技术作为实现这种分离的一种比较好的途径而越来越受到大家的重视。Spring提供的是一种 动态AOP实现,也即通过代理模式动态地在目标对象的方法前后插入相应的处理代码。应用程序与底层应用服务器平台的解耦也可以借助AOP技术来实现。 Spring内置的AOP支持是一种锦上添花的功能。它使得一些本来必须由容器支持的功能,比如事务控制可以脱离开容器运行,从而达到"瘦身"的目的。这 也是为什么Spring框架常被人成为轻量级容器的一个原因。

Spring框架可以与许多已有的框架技术结合使用。J2EE技术应用的一 个重要特点是相关的开源社区非常活跃。Web应用的不同层次都有非常多优秀的开源框架存在。比如Web层的Struts,OR映射层的Hibernate 等。Spring框架并不重新发明轮子,它的出现不是为了替代这些已有的框架。相反,Spring框架在设计上可以独立构建应用或者结合已有的框架一起构 建应用。另外一个值得指出的地方是Spring框架的几大模块之间相互耦合度很小,因此Spring框架的使用可以根据实际需要选其部分模块循序渐进的使 用,而非必须统统照搬。

  2、基于Spring的Web应用基础

  2.1 Web应用的典型层次

  Web应用一般在逻辑上根据功能分为以下几层:

  1.展示层

   这一层主要如何生成展示给最终用户的界面,尽可能少的包含业务逻辑处理。对于基于J2EE的Web应用,JSP是其最为常见的一种技术。Spring对 于展示层的支持非常灵活,除了直接支持JSP之外,它还支持基于FreeMarker模板,基于Velocity模板或其它文档类型的界面等的表现层实 现。

  2.业务层

  业务层一般包含主要的业务逻辑,尤其是与用例相对应的那些业务逻辑。另外,这一层也适合包含事务管理和安全控制方面的逻辑。良好的业务层设计可以使得展示层可以采用不同的技术而不影响业务层。业务层的功能上可以类比于J2EE技术中的无状态会话BEAN层次。

  3.数据访问对象(DAO)接口层

   DAO实际上就是数据接口层,在应用中建议通过接口来体现。DAO的存在使得数据访问可以与底层持久化层的具体实现相分离。一般在DAO接口中主要就是 实现数据对象的查询、存储、删除等操作。从理论上讲,DAO层与底层数据的存储方式是独立的,也就是说并不一定要求是关系型数据库。Spring框架在设 计的时候也考虑到了其它非关系型数据库数据源的情况。

  4.持久业务对象

  持久业务对象是问题域中业务对象的持久化 表示,比如一个用户对象,一个银行帐户等。我们一般通过某种O/R映射技术来实现这些业务对象的持久化。持久业务对象是可以包含业务逻辑的,与业务层所包 含的业务逻辑不同的地方是持久业务对象所包含的是与具体业务对象直接相关且更为通用的业务逻辑。

  5.企业信息系统

  企业信息系统泛指Web应用需要连接的后台系统,一般可以分为三大类,即ERP系统,企业传统的遗留系统和关系型数据库。大部分Web应用都是基于关系型数据库的,这也是像Spring等常见框架所主要考虑的企业信息系统。

  设计良好的Web应用在层次一般是上一层依赖下一层,但是下一层不依赖上一层。我们可以暂时概括为"向下而不向上依赖原则"。为了使得不同层次之间的依赖降到最低,建议使用接口耦合。这一点又是Spring框架发挥它外部配置优势的地方。

  2.2 MVC的选择

  虽然说MVC这种模式早在Java语言出现前就有了,但是这种模式在J2EE时代才大行其道,为广大Web应用开发者所接受。对于各种基于MVC的框架而言,其要解决的问题主要可以分为以下几部分:

  1.将Web页面中的输入封装成一个数据对象,比如像Struts的表单BEAN,Spring MVC中的命令类等。

  2.根据请求的不同,由负责分发的控制器来映射和调用相应的逻辑处理单元,并将上面的数据对象作为参数传入。

  3.逻辑处理单元完成相应的处理之后又把结果放入一个数据对象。

  4.在选择的展现界面中把返回的数据对象通过某种方式显示出来。

   在使用Spring构建MVC的时候,可以选择直接使用Spring自己的MVC实现,或者利用Spring对已有的一些MVC框架的支持。比如 Spring可以支持Struts,WebWork等,与它们结合使用。Spring引以为傲的非侵入的特性在Spring MVC上表现得并不如人意。它与Servlet API的耦合度较其它部分高,而且需要使用一些Spring的接口和类。

   Spring MVC的主要分发器实现是org.springframework.web.servlet.DispatcherServlet,这是Spring MVC的访问入口。Spring提供SimpleFormController,AbstractCommandController等类来帮助应用构建 各种控制器动作,并用ModelAndView类来联系展示和逻辑返回数据。如上节所述,Spring MVC能够支持不同的界面展示技术,而且界面的展示和其后面控制器的实现是分离的,也即界面展示技术的变化不用修改控制器的实现,只需要利用Spring 的控制反转技术修改外部配置文件即可。比如,在使用JSP展示技术时,外部配置文件的viewResolver定义如下:








<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/view/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>


  如果切换到FreeMaker模板技术,那么除了页面模板的修改之外,主要就是把对应的外部配置文件更改一下即可,如下所示。具体的展示逻辑部分不用做什么修改。








<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass">
<value>
org.springframework.web.servlet.view.freemarker.FreeMarkerView
</value>
</property>
<property name="suffix"><value>.ftl</value></property>
</bean>
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath">
<value>/view/</value>
</property>
</bean>


   如果不使用Spring的MVC框架而想结合已有的一些MVC框架,Spring也是支持的。Spring对于常见的MVC框架都提供了支持,包括 Struts,WebWork,Tapestry和JSF等。结合使用这些框架的一个好处是可以使用一些已有的熟悉的技术,另外结合Spring的AOP 拦截器可以相对比较容易地处理框架动作共有的事情,比如动作的日志处理等。如果选择这些MVC框架,那么在使用框架的配置文件和应用的Spring配置文 件都需要做相应的修改。比如使用Struts的时候,Struts-config.xml配置文件中的映射动作类型一般会设置成 org.springframework.web.struts.DelegatingActionProxy,或者设置控制器为 org.springframework.web.struts.DelegatingRequestProcessor。然后需要在相应应的 WebApplicationContext中定义与Struts Action对应的Bean。这样就可以充分利用Spring的控制反转技术来管理Struts的Action了。

  另外在使用这些框 架的时候要解决的一个问题是上下文的装载。比如使用Struts,可以使用ContextLoaderPlugin来装载Web上下文。这个 ContextLoaderPlugin替换了原来通过DispacherServlet装载的方式。需要在struts-config.xml文件中添 加如下条目:<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/>。这种方式可以使 Spring的Web上下文随着Struts ActionServlet的初始化而装载。

  因此,如果用户已有的应用是基于某个 MVC框架,或者用户熟悉某个框架,那么可以利用Spring对这些框架的支持结合使用。因为我们的目的本来就是为了解决问题而不是为了使用某种技术。但 是对其它用户而言,如果不是对已有的一些MVC框架比较熟悉的话,那就直接使用Spring的MVC框架就可以了。

2.3 Web Context设置

  对于不依赖于应用服务器的Spring 上下文(Context)设置,通常在应用代码中通过FileSystemXmlApplicationContext或ClasspathXmlApplicationContext来获取。比如使用这样的代码来得到上下文:








ApplicationContext ctx = new FileSystemXmlApplicationContext("config.xml");


   但是按照控制反转的原则,应用程序代码应该尽可能少的知道上下文的设置。因此,在基于Spring的Web应用中,这样的代码也可以省去。Spring 可以通过配置让Web容器自动装载上下文配置文件。从本质上讲,Web应用的ServletContext就是Spring用来存放应用上下文的地方。 Spring中与Web Context装载相关的有几个类:

  1.ContextLoaderListener:一般的应用服务器如WAS都能先装载Listener,如果不是的话,那么只能使用ContextLoaderServlet。

   2.ContextLoaderServlet:需要配置<load-on-startup>使得它率先装载。真正装载Context的类是 ContextLoader,上面两个类只是两种调用ContextLoader的不同途径。ContextLoader内部实际调用的是 XmlWebApplicationContext,其缺省配置文件为/WEB-INF/applicationContext.xml。

  如果使用ContextLoaderListener,其在web.xml中的配置一般如下:








<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>


  如果使用ContextLoaderServlet,其在web.xml中的配置一般如下:








<servlet>
<servlet-name>context</servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


   应用本身可能除了基于HTTP的Web渠道之外还通过别的渠道对外提供服务,因此,一个比较好的做法是把展示相关的配置与后面业务处理部分的配置分开。 这样如果更改了应用的访问渠道,只需要修改对应的配置文件即可。因此,Spring提供了一个WebApplicationContext的概念。在 WebApplicationContext中一般包含与Web访问相关的配置定义,包括各种控制动作的定义、界面展示的定义等等。

   WebApplicationContext一般由DispatcherServlet来初始化。在上下文层次结构上可以把它看成是 ApplcationContext的子上下文。在缺省的情况下,DispatcherServlet装载的配置文件名称为其Servlet名称- Servlet.xml,但是可以通过contextConfigLocation参数来定制。DispatcherServlet在web.xml中的 定义示例如下:








<servlet>
<servlet-name>Dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/context/Webcontrollers.xml</param-value>
</init-param>


  2.4 数据持久化层

   虽然使用J2EE技术的Web应用可以连接多种不同的企业信息系统(EIS),但是毫无疑问数据库是其中最为重要和常见的一种。正因如此,Spring 对数据库访问提供了非常完备的支持。数据访问对象(DAO)模式是J2EE模式中非常重要的一种。它的主要目的是使得持久化层与业务逻辑层分离,从而屏蔽 持久化层的具体实现。我们可以把Spring的DAO支持分为两大类,一是直接基于Spring JDBC模板的数据访问,另一类是基于某种O/R映射框架的数据访问。这里刚好可以使用Spring的控制反转特性,通过外部配置文件来定义DAO接口和 实际实现类之间的关系。Spring框架目前支持的O/R映射框架包括Hibernate、JDO、TopLink、iBATIS等。

  假设我们定义了一个userDAO。当使用JDBC来实现这个DAO的时候,定义的类可以如下所示:








public class userDAOJDBC extends JdbcDaoSupport implements userDAO{ … }


  如果使用Hibernate来实现这个DAO的时候,定义的类如下:








public class UserDAOHibernate extends HibernateDaoSupport implements UserDAO { … }


  Spring对于其它的O/R映射机制都有相应的抽象类供应用使用,比如对于iBATIS有SqlMapClientDaoSupport,对于JDO有JdoDaoSupport等。

  下面我们看一下如何在Spring的配置文件中定义上述DAO与具体实现的关系。假设我们的userDAO具体实现是通过Hibernate,那么在applicationContext.xml中的一个DAO可以定义如下:








<bean id="userDAO" class="com.fgw.dao.hibernate.UserDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>


   在这里我们实际DAO接口定义是:com.fgw.dao.UserDAO,而具体实现类为: com.fgw.dao.hibernate.UserDAOHibernate。显然,对于其它DAO的实现,我们只需要在配置文件中修改相应的实现类 (具体实现类当然是比不可少的)和属性即可。比如对于JDBC的DAO实现,属性就定义成相应的数据源。

  3、Spring与WebSphere应用服务器的配合

  Spring与底层J2EE应用服务器还是存在一些需要结合的地方,这里给出WAS中的一些结合点。

  3.1 使用WAS数据源

   在Java应用程序中,数据库的连接一般有两种方式来得到。一种是通过java.sql.DriverManager的方式来得到数据库连接。这种方式 不依赖于应用服务的支持,但是也不提供数据库连接池的功能。另外一种方式是通过javax.sql.DataSource的方式来得到数据库连接。在传统 基于J2EE的应用需要通过JNDI来得到数据源(javax.sql.DataSource)对象,然后再通过数据源来得到相应的数据库连接。常见的应 用服务器都支持这种方式,且一般都提供了数据库连接池的支持。虽然说我们一般推荐使用数据库连接池,但是也有一些时候我们需要脱离开应用服务器的环境使用 数据库(比如单元测试,比如应用移植等)。然而应用程序使用这两种方式的时候代码是不一样的,因此只能通过代码来应变。Spring提供了一个统一使用数 据源的解决方案,然后通过控制反转的机制用外部配置文件来指定使用的数据源。这样一方面可以统一这两种得到数据库连接的方式,另一方面也不需要像通常的 J2EE应用通过繁琐的JNDI代码来得到数据源。这样应用程序也就不需要知道使用的何种数据源。

  Spring提供了一个DriverManagerDataSource类来统一第一种方式的数据源获取。如果使用WAS中的Cloudscape数据库,用外部配置文件可配置如下:








<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.ibm.db2j.jdbc.DB2jDriver</value>
</property>
<property name="url">
<value>jdbc:db2j:D:\\DBName</value>
</property>
</bean>


  Spring提供了JndiObjectFactoryBean类来支持第二种方式的数据源获取。假设WAS中已经配置好的数据源名称为jdbc /MyDB,那么用外部配置文件可配置如下:








<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:comp/env/jdbc/MyDB</value></property>
</bean>
或者
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>jdbc/MyDB</value></property>
<property name="resourceRef"><value>true</value></property>
</bean>


   从上面配置我们可以得知,通过使用Spring,应用程序能够统一使用不同的数据源实现。如果使用环境发生变化,那么只需要修改Spring的配置文件 即可。对于部署在WAS上的Web应用,在生产环境中推荐使用WAS实现的数据库连接池。一方面是因为连接池实现地比较完善。另一方面,使用WAS提供的 数据库连接池可以很完善地支持JTA事务。

  3.2 使用WAS的JTA

  Web应用程序在使用事务的时候常常会涉 及一个事务类型的选择。是选择像JDBC事务这样的本地事务呢还是使用JTA支持的全局事务。这个与应用程序需要涉及到的事务管理器类型和个数密切相关。 Spring本身不支持分布式事务,因此分布式事务需要底层的JTA。但是Spring提供了事务的抽象,即底层真正事务实现可以切换而不影响应用程序代 码。这样应用程序可以依赖于底层WAS,也可以轻易地脱离开应用服务器的环境。这一点与前面数据源的抽象非常类似。

  WAS本身对于事 务划分有两种支持方式,一种是声明式的,当然这种管理方式需要EJB容器的支持,即所谓的容器管理事务(CMT)。另外一种方式是编程式的,通过程序代码 来直接使用JTA编程接口。Spring对于事务的划分也可以分为声明式和编程式两种方式。对于Spring编程式的事务划分方式,总体上可以分为两大 类。一类是通过直接使用实现PlatformTransactionManager接口的类。另一类是通过使用TransactionTemplate模 板类,模板类的使用可以简化事务控制代码。Spring对于声明式事务划分的支持实际上是利用了它的AOP机制。相对于编程式事务划分,这种基于AOP的 方式比较灵活,而且对代码的侵入性几乎为零。因此,如果没有特殊需要推荐使用这种事务划分方式。基于AOP的常用事务划分方式可以使用 ProxyFactoryBean加TransactionInterceptor方式,或者使用 TransactionPorxyFactoryBean的方式。前一种方式相对比较灵活,而后一种则对使用相对比较简单。

  无论是哪 一种事务划分方式,底层都需要一个事务管理机制作为支撑。如果是单一的事务资源管理器,那么根据所使用的后台事务管理资源不同的类型,可以选择的 PlatformTransactionManager实现有DataSourceTransactionManager, HibernateTransactionManager, JdoTransactionManager, PersistenceBrokerTransactionManager,和JmsTransactionManager等。无论是单个还是多个事务资 源管理器,都可以使用JtaTransactionManager类。如果使用JtaTransactionManager,那么所有事务管理实际都会委 托给底层应用服务器的JTA实现。

  例如,如果使用JDBC或iBATIS,那么我们可以使用简单的DataSourceTransactionManager,外部配置文件片断如下:








<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>


  如果使用Hibernate,那么我们可以使用HibernateTransactionManager,外部配置文件片断如下:








<bean id="transactionManager" class="org.springframework.orm.hibernate.
HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>


   使用WAS的JTA支持,我们只需要把上述对应bean中的class属性改成class属性改为 org.springframework.transaction.jta.JtaTransactionManager,然后再把属性改为 WebSphere对应的TransactionManager,参考如下:








<bean id="wasTxMgr"
class="org.springframework.transaction.jta.WebSphereTransactionManagerFactoryBean"/>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref local="wasTxMgr"/>
</property>
</bean>


   通过采用Spring的事务支持,底层事务采用何种方式的决定就不必在一开始开发就做出决定。因为我们能够通过Spring的外部配置文件来进行切换真 正的事务支持。不过,虽然也有第三方的JTA支持,但是WAS能够提供非常稳定的XA支持,因此推荐使用WAS的JTA,尤其是当应用涉及到分布事务处理 的时候。这样无论应用涉及几个事务资源都可以统一解决。

  3.3 如何加载Spring的JAR包

  Spring框 架的核心JAR包是spring.jar,但是根据实际使用情况需要一些扩展JAR包和依赖JAR包。那在WAS中如何处理这些JAR包文件呢?在Web 应用中一个简单而直接的处理方式放是把这些使用到的JAR文件都拷贝到对应的WEB-INF/lib目录下面。这种方法虽然简单,但是当有多个 Spring应用程序的时候这种处理方式就需要在每个应用的WEB-INF/lib目录下都拷贝一份相同的JAR文件。这里可以通过共享库的方式来统一解 决类库共享这个问题。

  共享库就是WAS专门用来解决不同应用程序之间共享JAR或本地库文件的一种机制。共享库由一个名字、一个 JAVA类路径和/或一个装载JNI库本地库路径组成。它可以分别在单元,节点和服务器级别定义。但是共享库定义了并不意味着它会被装载,只有当这个共享 库与某个应用程序或应用服务器关联之后,它才会被加载。如果一个共享库与一个应用程序关联,那么这个共享库由应用程序类加载器加载。如果一个共享库与应用 服务器关联,那么这个共享库就需要一个专门定义的类加载器来加载。这个类加载器需要用户自己定义。其操作如下:选应用服务器比如server1'类加载器 '新建一个类加载器'加载器与共享库关联。

  在创建这个类加载器之前一般都需要预先定义好共享库。 根据上面的介绍可知,通过共享库解决Spring应用的JAR包共享问题,主要就是两个步骤。一是,把Spring应用中需要共享的JAR包定义成为一个 共享库。二是,选定相应的WAS服务器实例,把它与上面创建的共享库关联起来。这样此WAS服务器实例上的所有应用都能够使用共享库中定义的JAR包。使 用共享库这种方式的时候要注意理解类的装载次序和方式。如果是这种与WAS服务器实例关联的共享库JAR包,其类加载器在层次结构上在应用程序类加载器上 面,即是它的父加载器。关于WAS的类装载器结构和策略可以进一步参考WAS信息中心。

  4、结束语

   Spring框架的核心内容并不依赖于任何容器,但是显然基于Web的应用是Spring主要的应用类型。了解和使用Spring框架一方面可以简化应 用的开发和测试,另一方也可以加深对J2EE技术的理解。另外轻量级的Web应用开发正在成为一种趋势,因此何乐而不为之。上面所讨论的只是Spring 使用中常见的一些内容,Spring框架自己也正变得越来越复杂。当然,Spring、Hibernate等框架中体现的一些思想也正被JEE 5规范所借鉴,尤其是EJB 3中也有了控制反转的应用和POJO的大量使用。实际上无论是JEE技术标准还是Spring等框架,其目的都是如何简化企业应用的开发,只是作为标准, JEE要考虑的内容更为广泛一些,程度也更为深入一些。(源自:IBM )
posted @ 2007-03-27 16:58 edsonjava 阅读(374) | 评论 (0)编辑 收藏
 
package   com.hull.validatecode;  
   
  import   java.util.*;  
  import   java.io.*;  
  import   java.awt.*;  
  import   java.awt.image.*;  
   
  import   javax.servlet.*;  
  import   javax.servlet.http.*;  
   
  import   com.sun.image.codec.jpeg.*;  
   
  public   class   ValidateCode   extends   HttpServlet  
  {  
  private   Font   imgFont   =   new   Font("宋体",Font.BOLD,16);   //设置字体  
   
  public   void   doGet(HttpServletRequest   request,HttpServletResponse   response)  
      throws   ServletException,IOException  
  {  
  doPost(request,response);  
  }  
   
  public   void   doPost(HttpServletRequest   request,HttpServletResponse   response)    
      throws   ServletException,IOException  
  {  
  String   vCode   =   "";  
  int     intCode   =   0;  
   
   
  intCode   =   (new   Random()).nextInt(9999);  
  if(intCode<1000)    
        intCode   +=   1000;  
  vCode   =   intCode+"";  
   
  /*  
    *   绘图  
    */  
  response.setContentType("image/gif");  
  ServletOutputStream   vout   =   response.getOutputStream();  
  BufferedImage   image   =   new   BufferedImage(50,20,BufferedImage.TYPE_INT_RGB);  
  Graphics   graph   =   image.getGraphics();  
   
  //   设置背景颜色  
  graph.setColor(Color.white);  
  graph.fillRect(1,1,48,18);  
   
  //设置字体颜色  
  graph.setColor(Color.black);  
  graph.setFont(imgFont);  
   
  char   c;  
  for(int   i=0;i<4;i++)  
  {  
  c   =   vCode.charAt(i);  
  graph.drawString(c+"",9*i+4,16);    
  }  
   
  JPEGImageEncoder   encoder   =   JPEGCodec.createJPEGEncoder(vout);  
  encoder.encode(image);  
   
  //   对session赋值  
  HttpSession   session   =   request.getSession(true);  
  session.removeAttribute("VerifyCode");  
  session.setAttribute("VerifyCode",vCode);  
   
  vout.close();  
  }  
   
  }  
   
  调用:  
  <img   src="ValidateCode"   align="center">
posted @ 2007-03-13 10:55 edsonjava 阅读(407) | 评论 (0)编辑 收藏
 

现在有不少网站在用户填写表单时,同时要求填写验证码,验证码的一个目的就是防范一些恶意的网站下载软件,这些软件能通过遍历链接而将网站的所有网页下载。还可以防止用户不经过本网站的页面而使用网站的资源。所以现在有不少网站都使用了验证码技术,验证码通常是一个在 WEB 服务器上生成的随机字符串,同时以某种方式保存起来,比如保存到与当前的 Session 中,然后在用户提交网页时与用户输入的验证比较是否一致,然而如果直接以明文的方式,还是不能防范一些功能较强的自动填写表格的软件。所以一般将验证码以图片的形式显示出来,同时可以将在图片中显示的字符串进行一些处理,比如使用旋转字符,添加背景纹理等技术以增大被软件识别的难度。下面简要介绍一下如果实现这种验证码:

首先实现一个 servlet 用来生成图片(当然也可以用 jsp 实现):

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.util.*;

import com.sun.image.codec.jpeg.*;

import java.awt.*;

import com.sun.image.codec.jpeg.*;

import java.awt.image.BufferedImage;

import java.awt.image.DataBuffer;

import java.awt.geom.GeneralPath;

import javax.swing.*;

import java.math.*;

public class Servlet1

extends HttpServlet {

//Process the HTTP Get request

  public void doGet(HttpServletRequest request, HttpServletResponse response) throws

      ServletException, IOException {

    response.setContentType(CONTENT_TYPE);

    response.setContentType("image/jpeg");  // 必须设置 ContentType image/jpeg

    int length = 4;      // 设置默认生成 4 个数字

    Date d = new Date();

    long lseed = d.getTime();

    java.util.Random r = new Random(lseed);   // 设置随机种子

    if (request.getParameter("length") != null) {

      try {

       length = Integer.parseInt(request.getParameter("length"));

      }

      catch (NumberFormatException e) {

      }

    }

    StringBuffer str = new StringBuffer();

    for (int i = 0; i <length; i++) {

      str.append(r.nextInt(9));        // 生成随机数字

}

// 可以在此加入保存验证码的代码

    // 创建内存图像

    BufferedImage bi = new BufferedImage(40, 16, BufferedImage.TYPE_INT_RGB);

    Graphics2D g = bi.createGraphics();

    g.clearRect(0, 0, 16, 40);

    g.setColor(Color.green.CYAN);

    g.drawString(str.toString(), 4, 12);

    try {

      // 使用 JPEG 编码,输出到 response 的输出流

      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.

      getOutputStream());

      JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);

      param.setQuality(1.0f, false);

      encoder.setJPEGEncodeParam(param);

      encoder.encode(bi);

    }

    catch (Exception ex) {

   

    }

  }   

}  

然后在需求显示验证码的加入以下代码就可以了

<img alt="" src="/WebModule1/servlet1"   width="40" height="16"/>

/WebModule1/servlet1 替换成你用来生成验证码的 servlet 的全路径。

 

posted @ 2007-03-12 16:37 edsonjava 阅读(210) | 评论 (0)编辑 收藏
 
JSF-Spring

This quickstart will show you how to start using JSF-Spring in a web application. It assumes you've got an existing web application. The first steps describe how to install and configure Spring and JSF. If you've got one or both of them installed already, just skip the corresponding sections.

Installing Spring

Download Spring (2.0) from http://www.springframework.org/download and extract the following jars to WEB-INF/lib:

  • dist/spring.jar
  • lib/jakarta-commons/commons-logging.jar

Then, create a basic application context

WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?/gt;
<!DOCTYPE beans PUBLIC
	"-//SPRING//DTD BEAN//EN"
	"http://www.springframework.org/dtd/spring-beans.dtd">
<beans></beans>

and define the listener so that Spring initializes itself on application startup:

WEB-INF/web.xml (partial)

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Installing JSF

Download either Sun's Reference Implementation (v1.1.01) from http://java.sun.com/javaee/javaserverfaces/download.html or MyFaces (Core 1.1.4 Distribution) from http://myfaces.apache.org/download.html and copy the jars from the lib directory of the downloaded archive to WEB-INF/lib. When using Sun's Reference Implementation, you gotta make sure you've got jstl.jar in your path since the distribution doesn't provide it.

Then, create a basic faces configuration

WEB-INF/faces-config.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC
	"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
	"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config></faces-config>

and define the faces servlet that will start up JSF and handle all requests:

WEB-INF/web.xml (partial)

<servlet>
	<servlet-name>Faces Servlet</servlet-name>
	<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>Faces Servlet</servlet-name>
	<url-pattern>*.jsf</url-pattern>
</servlet-mapping>

Installing JSF-Spring

Download JSF-Spring (4.0) from http://sourceforge.net/project/showfiles.php?group_id=107519 and extract the following jars to WEB-INF/lib:

  • dist/jsf-spring.jar
  • dist/cglib-nodep-2.1_3.jar

Then, define the listener that initializes JSF-Spring on application startup:

WEB-INF/web.xml (partial)

<listener>
	<listener-class>de.mindmatters.faces.spring.context.ContextLoaderListener</listener-class>
</listener>

Note that this is not a replacement for Spring's ContextLoaderListener, you need both. Furthermore, the order in which you define the listeners is important: First Spring's, then JSF-Spring's ContextLoaderListener.

Testing the Setup - a simple application

To verify that JSF-Spring is set up properly, we'll create two simple beans: One service bean providing data and one ui bean formatting it for rendering:

de/mindmatters/faces/quickstart/service/TimeService.java

package de.mindmatters.faces.quickstart.service;

import java.util.Date;

public interface TimeService {
	Date getNow();
}

de/mindmatters/faces/quickstart/service/TimeServiceImpl.java

package de.mindmatters.faces.quickstart.service;

import java.util.Date;

public class TimeServiceImpl implements TimeService {
	public Date getNow() {
		return new Date();
	}
}

WEB-INF/applicationContext.xml (partial)

<bean id="timeService" class="de.mindmatters.faces.quickstart.service.TimeServiceImpl" />

de/mindmatters/faces/quickstart/ui/UiBean.java

package de.mindmatters.faces.quickstart.ui;

import java.text.SimpleDateFormat;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import de.mindmatters.faces.quickstart.service.TimeService;

public class UiBean implements InitializingBean {
	private TimeService timeService = null;

	public void afterPropertiesSet() throws Exception {
		Assert.notNull(getTimeService(), "timeService must be set");
	}

	public String getShortDate() {
		return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT).format(
				getTimeService().getNow());
	}

	public TimeService getTimeService() {
		return timeService;
	}

	public void setTimeService(TimeService timeService) {
		this.timeService = timeService;
	}
}

WEB-INF/faces-config.xml (partial)

<managed-bean>
	<managed-bean-name>uiBean</managed-bean-name>
	<managed-bean-class>de.mindmatters.faces.quickstart.ui.UiBean</managed-bean-class>
	<managed-bean-scope>request</managed-bean-scope>
	<managed-property>
		<property-name>timeService</property-name>
		<value>#{timeService}</value>
	</managed-property>
</managed-bean>

As you can see, uiBean is managed by JSF and references timeService which is managed by Spring. Furthermore, it can benefit from various Spring features, namely InitializingBean in this case.

Now, create a simple page rendering the current date:

index.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:view>
  <html>
    <head>
	  <title>jsf-spring quickstart</title>
    </head>
    <body>
    	<h:outputText value="#{uiBean.shortDate}"/>
    </body>
  </html>
</f:view>

Remember to view this page by accessing /index.jsf with your browser, when using / or /index.jsp, your request won't be handled by FacesServlet and thus will produce errors.

posted @ 2007-02-01 17:36 edsonjava 阅读(426) | 评论 (0)编辑 收藏
 
     摘要: 网上关于 Apache + JK + Tomcat 的集群配置例子很多,按着例子配置下来,基本都能运行,不过,在一些重要的地方却没有进一步的说明。这次公司一个产品就是采用 Apache+JK+Tomcat 集群,在整个配置、测试过程中,遇到了许多的问题,经过不断测试、摸索,最后总算是搞定了,性能也达到了预期的目标。针对网上的例子,感觉有必要再详细的介绍一下我...  阅读全文
posted @ 2006-12-26 10:27 edsonjava 阅读(461) | 评论 (0)编辑 收藏
 

[精华] 原来win+apache实现ssl的证书认证如此简单


http://www.chinaunix.net 作者:ataman  发表于:2006-02-26 15:45:34
发表评论】【查看原文】【Web服务器讨论区】【关闭

windows+apache的情况下,实现ssl的证书认证
(win下用openssl做证书极为困难的问题彻底解决了)

我写得非常详细,一步一步都说得很清楚。实际操作极为简单,要不了5分钟,是我的字打得太多了.如果发现错误,遗漏请提出。

首先,到http://hunter.campbus.com/去下载和自己的apache版本相同的的Apache_xxx-xxxOpenssl_xxx-Win32.zip。解压缩后找到这5个文件mod_ssl.so(modules目录),ssl.conf,ssl.default.conf(conf目录,

其中default.conf作为备份),libeay32.dll, ssleay32.dll(这2个都在bin目录)。把它们全都复制到你自己的apahce下的对应目录。



###############################################################################

接下来,更改设置文件


对于httpd.conf和ssl.conf,如果你的服务器没有域名,那么servername就填ip好了。
比如:ServerName 10.10.10.10:80(httpd.conf)
ServerName 10.10.10.10:443(ssl.conf)

打开httpd.conf:
找到#LoadModule ssl_module modules/mod_ssl.so,去掉前面的‘#‘,这样就在启动时加载了ssl模块。

打开ssl.conf:
找到#<IfDefine SSL>;和#</IfDefine>;,把前面的’#‘号都去掉,否则启动apache时还要加参数,麻烦。
如下设置:
SSLMutex none                         (这个我是none,有人是default,具体怎么设可以研究一下)
SSLCertificateFile conf/server.crt    (服务器证书的位置,就是公钥吧?)
SSLCertificateKeyFile conf/server.key (服务器私钥的位置)
SSLCACertificateFile conf/ca.crt      (CA根证书的位置,进行客户端验证时需要。也是一个CA公钥吧?

我把它们都放在apache的conf目录下了)
DocumentRoot "xxxxx"                  (指向要ssl加密认证的文档目录,比如"f:/http")

SSLVerifyClient require               (去掉前面的‘#’号,进行客户端验证时需要)
SSLVerifyDepth  1                     (去掉前面的‘#’号,把10改为1,进行客户端验证时需要)



##############################################################################
现在,就要制作证书了


去openvpn.net下载并安装openvpn。
这是一个虚拟个人网络制作工具,他能完美的在win(linux,BSD也行)下制作根、服务器、客户端证书。
安装完毕后,开始-程序-附件-命令提示符,进到openvpn的easy-rsa目录,比如:
f:\program files\openvpn\easy-rsa>;_
输入:
init-config 回车

会产生几个文件,切换出来,打开vars.bat文件,修改其中的KEY_COUNTRY(国家2位字母), KEY_PROVINCE(省2位字母), KEY_CITY(城市), KEY_ORG(组织),  KEY_EMAIL(电子邮箱)这几个参数,免的后面制证时

反复输入麻烦。保存退出,继续使用命令提示符。
依次输入以下两个命令,当然是分别回车喽:
vars
clean-all   (这两个是准备工作)




####################################################################################


1.
建立CA根证书


接着输入build-ca   回车(这个就是建立CA根证书啦)


然后显示:

ai:/usr/share/openvpn/easy-rsa # ./build-ca
Generating a 1024 bit RSA private key
............++++++
...........++++++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [KG]:          国家名2位字母,默认的参数就是我们刚才修改过的。
State or Province Name (full name) [NA]:    省、州名2位字母
Locality Name (eg, city) [BISHKEK]:         城市名
Organization Name (eg, company) [OpenVPN-TEST]:     组织名
Organizational Unit Name (eg, section) []:           组织里的单位名
Common Name (eg, your name or your server's hostname) []:这个是关键,应该输入颁发根证书单位的域名

,不过因为是根证书,所以怎么填都无所谓。只有服务器证书才需要认真填。
Email Address [me@myhost.mydomain]: 电子邮箱

好了,CA根证书制作完成!在keys目录下,它的名字就叫ca.crt,CA的私钥是ca.key


#######################################################################


2.
现在制作服务器证书:
在命令提示符下,输入
build-key-server server   回车
你会看到和上面很相似的东西
但要注意这里的Common Name (eg, your name or your server's hostname) []:
这个才是真正的关键。这里应该输入服务器的域名比如www.xxx.com。
如果没有域名,就应该填ip,与httpd.conf和ssl.conf里的设置对应,
ServerName 10.10.10.10:80(httpd.conf)
ServerName 10.10.10.10:443(ssl.conf)

也就是说填:10.10.10.10

接下来看到 a challenge password []:填不填随便,我不填
an optional company name []: 填不填随便,我不填

sign the certificate? [y/n] 敲y回车。用CA根证书对服务器证书签字认证。
1 out 1 certificate requests certified,commit? [y/n] 敲y回车,确认。

好了,建好了在keys目录下的server.crt(证书)和server.key(私钥)


#######################################################################


3.
现在制作客户端证书:

在命令提示符下,输入
build-key client1   回车
又是一通国家省市组织等等,comman name也是随便填的。
然后
a challenge password []:填不填随便,我不填
an optional company name []: 填不填随便,我不填

sign the certificate? [y/n] 敲y回车。用CA根证书对客户端证书签字认证。
1 out 1 certificate requests certified,commit? [y/n] 敲y回车,确认。

好了,建好了在keys目录下的client1.crt(客户端证书)和client1.key(私钥)

等等, .crt的客户端证书是不能使用的,必须把它转化为.pfx格式的文件!!

所以,还是在命令提示符下,输入
openssl 回车
看到openssl>;
再输入
pkcs12 -export –in keys/client1.crt -inkey keys/client1.key -out keys/client1.pfx
回车,
看到Enter export password:会要求你建立客户端证书的输出密码,我填hehe,
verifying-Enter export password再确认一遍hehe,好了!


########################################################################


把keys目录下的ca.crt和server.crt,server.key都复制到apache的conf目录下,(ssl.conf需要)
ca.key自己保留吧,找个合适的地方储存起来.


#########################################################################


客户端安装证书


打开internet explorer(IE),工具-internet选项-内容-证书,点选'个人'
再点击导入,把客户端证书client1.pfx导入到个人组里(别忘了扩展名是pfx)。
这里还要输入刚才建立的输出密码hehe才能倒入呢。

接着,点选'受信任的根证书颁发机构',点击导入,把CA根证书ca.crt导入到受信任的根证书颁发机构里。


#########################################################################

好啦,重新启动apache,打开IE,
在地址栏里输入https://10.10.10.10或者域名,弹出个窗口要选择个人的数字证书。
点选,然后确定。
如果服务器证书的common name填写正确的话,你就可以直接进入网站了,看到右下角的小锁头(可靠的SSL128位)。
如果服务器证书的common name填写不正确,就会弹出个‘安全警报’框,告诉你3条:
1.安全证书由信任的站点颁发
(如果说是由不信任的站点颁发,那就是你的ca根证书ca.crt没有导入到ie的受信任的根证书颁发机构里)

2.安全证书的日期有效 
(这个日期缺省是10年,可以在openvpn的easy-rsa目录下的openssl.cnf里调整修改,然后重新制作一整套证书(openssl.cnf看起来像拨

号网络的快捷方式,要用记事本,写字板打开修改))

3.“安全证书上的名称无效,或者与站点名称不匹配”
这就是服务器证书的common name填写不正确所致,不过这也没关系,有人好像愿意这样。我是不想看到这个警告框,烦人。

即使有安全警报,你仍能进入网站,看到右下角的小锁头(可靠的SSL128位)


#################################
最后,成功啦!用吧。

我自己把httpd.conf里的listen 80都加#注释了,servername改为10.10.10.10:443,只用https不用http,嗬嗬!!!

posted @ 2006-12-25 17:00 edsonjava 阅读(423) | 评论 (0)编辑 收藏
 
修改httpd.conf中文编码
 
AddDefaultCharset GB2312
DefaultLanguage GB2312
AddLanguage zh-cn .cn
下载 mod_jk2.so 并加载mod_jk2.so
 
LoadModule jk2_module modules/mod_jk2.so
在conf目录新建workers2.properties文件
 
 
[shm]
file=${serverRoot}/logs/shm.file
size=1048576
#The socket channel
[channel.socket:localhost:8009]
port=8009
host=127.0.0.1
#define the worker
[ajp13:localhost:8009]
channel=channel.socket:localhost:8009
#uri mapping
[uri:/*.jsp]
worker=ajp13:localhost:8009

虚拟主机
 
<VirtualHost *:80>
    ServerAdmin wweidong@sina.com
    DocumentRoot "I:/Job/Java"
    ServerName localhost
    ErrorLog logs/sdg.com-error_log
    CustomLog logs/sdg.com-access_log common
</VirtualHost>
posted @ 2006-12-18 17:28 edsonjava 阅读(248) | 评论 (0)编辑 收藏
 
Log4j基本使用方法

  Log4j由三个重要的组件构成:日志级别ERROR、WARN、INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显示内容。

  一、定义配置文件

  其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:

  1.配置根Logger,其语法为:

  log4j.rootLogger = [ level ] , appenderName, appenderName, …

  其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。 appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。

  2.配置日志信息输出目的地Appender,其语法为:

  log4j.appender.appenderName = fully.qualified.name.of.appender.class
  log4j.appender.appenderName.option1 = value1
  …
  log4j.appender.appenderName.option = valueN

  其中,Log4j提供的appender有以下几种:
  org.apache.log4j.ConsoleAppender(控制台),
  org.apache.log4j.FileAppender(文件),
  org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
  org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
  org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

  3.配置日志信息的格式(布局),其语法为:

  log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
  log4j.appender.appenderName.layout.option1 = value1
  …
  log4j.appender.appenderName.layout.option = valueN

  其中,Log4j提供的layout有以下几种:
  org.apache.log4j.HTMLLayout(以HTML表格形式布局),
  org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
  org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
  org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

  Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: %m 输出代码中指定的消息

  %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
  %r 输出自应用启动到输出该log信息耗费的毫秒数
  %c 输出所属的类目,通常就是所在类的全名
  %t 输出产生该日志事件的线程名
  %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
  %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
  %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)

  二、在代码中使用Log4j

  1.得到记录器

  使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:

  public static Logger getLogger( String name)

  通过指定的名字获得记录器,如果必要的话,则为这个名字创建一个新的记录器。Name一般取本类的名字,比如:

  static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () )

  2.读取配置文件

  当获得了日志记录器之后,第二步将配置Log4j环境,其语法为:

  BasicConfigurator.configure (): 自动快速地使用缺省Log4j环境。
  PropertyConfigurator.configure ( String configFilename) :读取使用Java的特性文件编写的配置文件。
  DOMConfigurator.configure ( String filename ) :读取XML形式的配置文件。

  3.插入记录信息(格式化日志信息)

  当上两个必要步骤执行完毕,您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:

  Logger.debug ( Object message ) ;
  Logger.info ( Object message ) ;
  Logger.warn ( Object message ) ;
  Logger.error ( Object message ) ;

posted @ 2006-11-21 17:04 edsonjava 阅读(149) | 评论 (0)编辑 收藏
 

说来奇怪,昨夜调了很久没成功,今天上来第一次就成功了。

这次的实验是为了获得WS提供的一个person复杂对象其中的某一属性。

服务端提供getPerson,getPersons接口。getPerson根据PersonId提供一个Person对象。而Person包含三个属性,personid,firstname,lastname所以说这个实验的主要目的就是去实验一下非纯字符或数字操作的WS服务的客户端怎么做。

具体是这么来做的:

1、在ECLIPSE里新建一个JAVA PROJECT

 

2、在项目名称上点右键,加入add xfire nature将xfire所需要的库导入进来。关于XFIRE的ECLIPSE插件可以参看这篇文章http://xfire.codehaus.org/Eclipse+Plugin

 

3、点击新建->其他,选择xfire的eclipse插件提供的代码生成工具

 

4、填入WSDL的URL以及选择要将代码输出的项目名称

 

5、确定后就能够看到生成的代码了,生成了很多文件,这些都是根据WSDL生成的,因为服务端提供的person是个复杂对象,不是简单的一个加减操作,所以需要对复杂对象进行生成。

这里最关键的是两个文件,PersonWebServiceClient以及PersonWebServicePortType(interface)。

PersonWebServiceImpl是PersonWebServicePortType的实现。

 

6、下面这部当然就是我们自己开始写客户端了,我们写个junit的测试

先将junit库导入到项目中,右键项目名称,选择properties-->java build path-->libraries-->add library-->JUNIT导入JUNIT库,然后就能看到:

准备工作还没有完成,但是先不做了,等一会再回来做。

现在开始写case吧。

我建立了一个命叫motion.test的package,在里面加入文件WebServiceTest.java

package motion.test;

import junit.framework.TestCase;

public class WebServiceTest extends TestCase {

}

现在我们加入test:

 public void testShow(){
  
  PersonWebServiceClient service = new PersonWebServiceClient();
  PersonWebServicePortType client = service.getPersonWebServiceHttpPort();
  
  System.out.print(client.getPerson("1").getFirstName());
  
 }

这个test的目的是想打印出personid为1的那个person的firstname。

现在运行一下,如果不出意外肯定有错误

看下错误怎么说吧:

原来是有些包没导进来。

然后我们再右键点击项目,选xfire那项。

将Commons Codec 和Commons HttpClient前面的勾打上,确定返回,run WebServiceTest.java as junit test

在Console里我们就能看到这么一行:javax.xml.bind.JAXBElement@198cb3d

惊喜了一会突然发现这不是我们要的结果啊,我们是要显示那个personid为1的那个人的firstname,难道他的firstname是这种奇怪的东西吗?

其实这只是xml元素的JAXB的表示形式。如果我们需要得到具体的值,要用getValue()方法,修改刚才的显示语句如下:

System.out.print(client.getPerson("1").getFirstName().getValue());

再次run WebServiceTest.java as junit test

Console里出现:yang

这是便正确的结果。

 

这个实验其实是非常简单的,但是通过这个实验能够看出很多细节是需要去考虑的。

对于选了web service课程或者想了解这方面的内容的人来说,我希望通过这个能够帮助你掌握一些客户端的制作技术。

在工具盛行的今天,做个服务端已经是一项再简单不过的事情了,而客户端要相对麻烦一些。

这篇就是一个入门级的复杂对象客户端的制作过程。我也是刚刚开始学习这方面的知识,所以如果我有什么错误欢迎您来指出,我也很乐意与你讨论这些问题,希望大家都能一点一点的进步。

posted @ 2006-11-13 17:01 edsonjava 阅读(393) | 评论 (0)编辑 收藏
 

常听大家讨论哪个 Java 开发工具比较好,JBuilder,Virtual Cafe ?

今天介绍一个简单易用的 Java 开发,编译,集成,测试工具:Jakarta Ant

他被称为 java 环境里的 make (make 在 C 程序开发里人人皆知)

Java 程序编辑器爱用啥用啥,我喜欢用 UltraEdit。

以 HelloWorld 为例介绍他的使用。

此示例可在此下载:

ftp://cinc.dns2go.com/pub/doc/code/ant/HelloWorld.zip

.下载 ant :(最新版 1.5)

http://jakarta.apache.org/builds/jakarta-ant/release/v1.5/bin/

windows 下请下载 zip 文件

unix 下请下载 tar.gz 文件

.安装 JDK:

安装到 c:/jdk,并确认能找到 c:/jdk/bin/java

.安装 ant:

把 ant 解压到本地硬盘 c:/ant,并确认能找到 c:/ant/bin/ant.bat

.设置环境变量:

JDK_HOME:c:/jdk

ANT_HOME:c:/ant

PATH:在原来的 PATH 开头加上 c:/ant/bin;c:/jdk/bin

注:

windows 下做操作:我的电脑 -> 属性 -> 高级 -> 环境变量 -> 用户变量

每次修改完环境变量,要重新打开 MS-DOS 窗口才能生效

.开始开发:

一个 ant 项目目录结构:

c:/HelloWorld :项目主目录

/src :源程序目录

/classes :编译后的 class 文件目录

/lib :开发项目所需要的类库目录,比如开发数据库时所需要的 jdbc lib(这次没用到)

/jar :打包好的 jar 程序目录(这次没用到)

/build.xml :项目配置文件

1.建立一个项目的主目录

mkdir c:/Helloworld

2.创建项目配置文件:

c:/HelloWorld/builder.xml

-----------------------------------------------------------------------------

-----------------------------------------------------------------------------

3.建立源代码目录,所有的 Java 程序放在里面

mkdir c:/HelloWorld/src

4.创建 HelloWorld.java

c:/HelloWorld/src/HelloWorld.java

-----------------------------------------------------------------------------

/**

* Demo class for run HelloWorld by ANT

*/

public class HelloWorld {

public static void main(String args[]){

System.out.println ("Hello World!");

}

} //EOC

-----------------------------------------------------------------------------

5.编译 HelloWorld

C:/HelloWorld>ant build

Buildfile: build.xml

build:

BUILD SUCCESSFUL

Total time: 2 seconds

说明:

调用 builder.xml 中的 target build

在 c:/HelloWorld/classes 里会出现 HelloWorld.class

此命令会把 src 目录下所有的 java 文件(包括子目录下的)都编译

6.运行 HelloWorld

C:/HelloWorld>ant run

Buildfile: build.xml

build:

run:

[exec] Hello World!

BUILD SUCCESSFUL

Total time: 2 seconds

调用 builder.xml 中的 target run

注意 target run 后的 depends="build",只有 build 成功后 run 才可以被调用

7.清除 class 文件:

C:/HelloWorld>ant clean

Buildfile: build.xml

clean:

[delete] Deleting 1 files from C:/Helloworld/classes

[delete] Deleted 1 directory from C:/Helloworld/classes

BUILD SUCCESSFUL

Total time: 2 seconds

使用 ANT 的好处:

1.相对于其他项目管理工具,只有一个文本配置文件 build.xml,配置简单

2.命令简单,一学就会,ant run, ant clean ...

3.安装简单,占用资源少,只要安装 Jdk, ANT 就可以运行

用 ANT 进行 Java 程序开发,已经成了 Java 界的一个共识。

在此基础上,可以建立更复杂的项目

在以后会继续介绍:

用 ANT 开发 数据库项目

用 ANT 开发 WEB 应用

用 ANT 和 CVS 开发多人项目,并进行项目版本管理。

推荐阅读:

Apache Ant 1.5 Manual

http://jakarta.apache.org/ant/manual/index.html

如果有时间,应该阅读,能发现许多 ant 的强大功能。

posted @ 2006-11-13 16:32 edsonjava 阅读(259) | 评论 (1)编辑 收藏
 
在平时工作中,难免会遇到把 XML 作为数据存储格式。面对目前种类繁多的解决方案,哪个最适合我们呢?在这篇文章中,我对这四种主流方案做一个不完全评测,仅仅针对遍历 XML 这块来测试,因为遍历 XML 是工作中使用最多的(至少我认为)。

  预 备

  测试环境:

  AMD 毒龙1.4G OC 1.5G、256M DDR333、Windows2000 Server SP4、Sun JDK 1.4.1+Eclipse 2.1+Resin 2.1.8,在 Debug 模式下测试。

  XML 文件格式如下:

  <?xml version="1.0" encoding="GB2312"?>
  <RESULT>
  <VALUE>
  <NO>A1234</NO>
  <ADDR>四川省XX县XX镇XX路X段XX号</ADDR>
  </VALUE>
  <VALUE>
  <NO>B1234</NO>
  <ADDR>四川省XX市XX乡XX村XX组</ADDR>
  </VALUE>
  </RESULT>

  测试方法:

  采用 JSP 端调用Bean(至于为什么采用JSP来调用,请参考:http://blog.csdn.net/rosen/archive/2004/10/15/138324.aspx),让每一种方案分别解析10K、100K、1000K、10000K的 XML 文件,计算其消耗时间(单位:毫秒)。

  JSP 文件:

  <%@ page contentType="text/html; charset=gb2312" %>
  <%@ page import="com.test.*"%>

  <html>
  <body>
  <%
  String args[]={""};
  MyXMLReader.main(args);
  %>
  </body>
  </html>

  测 试

  首先出场的是 DOM(JAXP Crimson 解析器)

  DOM 是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准。DOM 是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作。由于它是基于信息层次的,因而 DOM 被认为是基于树或基于对象的。DOM 以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航,而不是像 SAX 那样是一次性的处理。DOM 使用起来也要简单得多。

  另一方面,对于特别大的文档,解析和加载整个文档可能很慢且很耗资源,因此使用其他手段来处理这样的数据会更好。这些基于事件的模型,比如 SAX。

  Bean文件:

  package com.test;

  import java.io.*;
  import java.util.*;
  import org.w3c.dom.*;
  import javax.xml.parsers.*;

  public class MyXMLReader{

  public static void main(String arge[]){
  long lasting =System.currentTimeMillis();
  try{
   File f=new File("data_10k.xml");
   DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
   DocumentBuilder builder=factory.newDocumentBuilder();
   Document doc = builder.parse(f);
   NodeList nl = doc.getElementsByTagName("VALUE");
   for (int i=0;i<nl.getLength();i++){
    System.out.print("车牌号码:" + doc.getElementsByTagName("NO").item(i).getFirstChild().getNodeValue());
    System.out.println(" 车主地址:" + doc.getElementsByTagName("ADDR").item(i).getFirstChild().getNodeValue());
  }
  }catch(Exception e){
   e.printStackTrace();
  }
  System.out.println("运行时间:"+(System.currentTimeMillis() - lasting)+" 毫秒");
  }
  }


  10k消耗时间:265 203 219 172
  100k消耗时间:9172 9016 8891 9000
  1000k消耗时间:691719 675407 708375 739656
  10000k消耗时间:OutOfMemoryError

  接着是 SAX

  这种处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX 还比它的替代者 DOM 快许多。

   选择 DOM 还是选择 SAX ?

  对于需要自己编写代码来处理 XML 文档的开发人员来说,选择 DOM 还是 SAX 解析模型是一个非常重要的设计决策。

  DOM 采用建立树形结构的方式访问 XML 文档,而 SAX 采用的事件模型。

  DOM 解析器把 XML 文档转化为一个包含其内容的树,并可以对树进行遍历。用 DOM 解析模型的优点是编程容易,开发人员只需要调用建树的指令,然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用 DOM 解析器的时候需要处理整个 XML 文档,所以对性能和内存的要求比较高,尤其是遇到很大的 XML 文件的时候。由于它的遍历能力,DOM 解析器常用于 XML 文档需要频繁的改变的服务中。

  SAX 解析器采用了基于事件的模型,它在解析 XML 文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX 对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时,SAX 这种扩展能力得到了更好的体现。但用 SAX 解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。

  Bean文件:

  package com.test;
  import org.xml.sax.*;
  import org.xml.sax.helpers.*;
  import javax.xml.parsers.*;

  public class MyXMLReader extends DefaultHandler {

  java.util.Stack tags = new java.util.Stack();

  public MyXMLReader() {
  super();
  }

  public static void main(String args[]) {
  long lasting = System.currentTimeMillis();
  try {
   SAXParserFactory sf = SAXParserFactory.newInstance();
   SAXParser sp = sf.newSAXParser();
   MyXMLReader reader = new MyXMLReader();
   sp.parse(new InputSource("data_10k.xml"), reader);
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + " 毫秒");
  }

  public void characters(char ch[], int start, int length) throws SAXException {
  String tag = (String) tags.peek();
  if (tag.equals("NO")) {
   System.out.print("车牌号码:" + new String(ch, start, length));
  }
  if (tag.equals("ADDR")) {
  System.out.println(" 地址:" + new String(ch, start, length));
  }
  }

  public void startElement(
  String uri,
  String localName,
  String qName,
  Attributes attrs) {
  tags.push(qName);
  }
  }

  10k消耗时间:110 47 109 78
  100k消耗时间:344 406 375 422
  1000k消耗时间:3234 3281 3688 3312
  10000k消耗时间:32578 34313 31797 31890 30328

  然后是 JDOM http://www.jdom.org/

  JDOM 的目的是成为 Java 特定文档模型,它简化与 XML 的交互并且比使用 DOM 实现更快。由于是第一个 Java 特定模型,JDOM 一直得到大力推广和促进。正在考虑通过“Java 规范请求 JSR-102”将它最终用作“Java 标准扩展”。从 2000 年初就已经开始了 JDOM 开发。

  JDOM 与 DOM 主要有两方面不同。首先,JDOM 仅使用具体类而不使用接口。这在某些方面简化了 API,但是也限制了灵活性。第二,API 大量使用了 Collections 类,简化了那些已经熟悉这些类的 Java 开发者的使用。

  JDOM 文档声明其目的是“使用 20%(或更少)的精力解决 80%(或更多)Java/XML 问题”(根据学习曲线假定为 20%)。JDOM 对于大多数 Java/XML 应用程序来说当然是有用的,并且大多数开发者发现 API 比 DOM 容易理解得多。JDOM 还包括对程序行为的相当广泛检查以防止用户做任何在 XML 中无意义的事。然而,它仍需要您充分理解 XML 以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习 DOM 或 JDOM 接口都更有意义的工作。

  JDOM 自身不包含解析器。它通常使用 SAX2 解析器来解析和验证输入 XML 文档(尽管它还可以将以前构造的 DOM 表示作为输入)。它包含一些转换器以将 JDOM 表示输出成 SAX2 事件流、DOM 模型或 XML 文本文档。JDOM 是在 Apache 许可证变体下发布的开放源码。

  Bean文件:

  package com.test;

  import java.io.*;
  import java.util.*;
  import org.jdom.*;
  import org.jdom.input.*;

  public class MyXMLReader {

  public static void main(String arge[]) {
  long lasting = System.currentTimeMillis();
  try {
   SAXBuilder builder = new SAXBuilder();
   Document doc = builder.build(new File("data_10k.xml"));
   Element foo = doc.getRootElement();
   List allChildren = foo.getChildren();
   for(int i=0;i<allChildren.size();i++) {
    System.out.print("车牌号码:" + ((Element)allChildren.get(i)).getChild("NO").getText());
    System.out.println(" 车主地址:" + ((Element)allChildren.get(i)).getChild("ADDR").getText());
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + " 毫秒");
  }
  }

  10k消耗时间:125 62 187 94
  100k消耗时间:704 625 640 766
  1000k消耗时间:27984 30750 27859 30656
  10000k消耗时间:OutOfMemoryError

  最后是 DOM4J http://dom4j.sourceforge.net/

  虽然 DOM4J 代表了完全独立的开发结果,但最初,它是 JDOM 的一种智能分支。它合并了许多超出基本 XML 文档表示的功能,包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过 DOM4J API 和标准 DOM 接口具有并行访问功能。从 2000 下半年开始,它就一直处于开发之中。

  为支持所有这些功能,DOM4J 使用接口和抽象基本类方法。DOM4J 大量使用了 API 中的 Collections 类,但是在许多情况下,它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是,虽然 DOM4J 付出了更复杂的 API 的代价,但是它提供了比 JDOM 大得多的灵活性。

  在添加灵活性、XPath 集成和对大文档处理的目标时,DOM4J 的目标与 JDOM 是一样的:针对 Java 开发者的易用性和直观操作。它还致力于成为比 JDOM 更完整的解决方案,实现在本质上处理所有 Java/XML 问题的目标。在完成该目标时,它比 JDOM 更少强调防止不正确的应用程序行为。

  DOM4J 是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的 Java 软件都在使用 DOM4J 来读写 XML,特别值得一提的是连 Sun 的 JAXM 也在用 DOM4J。

  Bean文件:

  package com.test;

  import java.io.*;
  import java.util.*;
  import org.dom4j.*;
  import org.dom4j.io.*;

  public class MyXMLReader {

  public static void main(String arge[]) {
  long lasting = System.currentTimeMillis();
  try {
   File f = new File("data_10k.xml");
   SAXReader reader = new SAXReader();
   Document doc = reader.read(f);
   Element root = doc.getRootElement();
   Element foo;
   for (Iterator i = root.elementIterator("VALUE"); i.hasNext();) {
    foo = (Element) i.next();
    System.out.print("车牌号码:" + foo.elementText("NO"));
    System.out.println(" 车主地址:" + foo.elementText("ADDR"));
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + " 毫秒");
  }
  }

  10k消耗时间:109 78 109 31
  100k消耗时间:297 359 172 312
  1000k消耗时间:2281 2359 2344 2469
  10000k消耗时间:20938 19922 20031 21078

  JDOM 和 DOM 在性能测试时表现不佳,在测试 10M 文档时内存溢出。在小文档情况下还值得考虑使用 DOM 和 JDOM。虽然 JDOM 的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM 仍是一个非常好的选择。DOM 实现广泛应用于多种编程语言。它还是许多其它与 XML 相关的标准的基础,因为它正式获得 W3C 推荐(与基于非标准的 Java 模型相对),所以在某些类型的项目中可能也需要它(如在 JavaScript 中使用 DOM)。

  SAX表现较好,这要依赖于它特定的解析方式。一个 SAX 检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。

  无疑,DOM4J是这场测试的获胜者,目前许多开源项目中大量采用 DOM4J,例如大名鼎鼎的 Hibernate 也用 DOM4J 来读取 XML 配置文件。如果不考虑可移植性,那就采用DOM4J吧!

posted @ 2006-11-08 11:47 edsonjava 阅读(182) | 评论 (0)编辑 收藏
 

HTTP 1.1状态代码及其含义

  

下表显示了常见的HTTP 1.1状态代码以及它们对应的状态信息和含义。

应当谨慎地使用那些只有HTTP 1.1支持的状态代码,因为许多浏览器还只能够支持HTTP 1.0。如果你使用了HTTP 1.1特有的状态代码,最好能够检查一下请求的HTTP版本号。

状态代码 状态信息 含义
100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分。(HTTP 1.1新)
101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议(HTTP 1.1新)
200 OK 一切正常,对GET和POST请求的应答文档跟在后面。
201 Created 服务器已经创建了文档,Location头给出了它的URL。
202 Accepted 已经接受请求,但处理尚未完成。
203 Non-Authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP 1.1新)。
204 No Content 没有新文档,浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
205 Reset Content 没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。
206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新)。
300 Multiple Choices 客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。
301 Moved Permanently 客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。
302 Found 类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”。

出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。

注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。

严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。

303 See Other 类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP 1.1新)。
304 Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。
307 Temporary Redirect 和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只能跟随对GET请求的重定向。(HTTP 1.1新)
400 Bad Request 请求出现语法错误。
401 Unauthorized 客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。
403 Forbidden 资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。
404 Not Found 无法找到指定位置的资源。这也是一个常用的应答。
405 Method Not Allowed 请求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)对指定的资源不适用。(HTTP 1.1新)
406 Not Acceptable 指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP 1.1新)。
407 Proxy Authentication Required 类似于401,表示客户必须先经过代理服务器的授权。(HTTP 1.1新)
408 Request Timeout 在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP 1.1新)
409 Conflict 通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP 1.1新)
410 Gone 所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP 1.1新)
411 Length Required 服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP 1.1新)
412 Precondition Failed 请求头中指定的一些前提条件失败(HTTP 1.1新)。
413 Request Entity Too Large 目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP 1.1新)。
414 Request URI Too Long URI太长(HTTP 1.1新)。
416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。(HTTP 1.1新)
500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。
501 Not Implemented 服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。
502 Bad Gateway 服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。
503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。
504 Gateway Timeout 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP 1.1新)
505 HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本。(HTTP 1.1新)
posted @ 2006-11-08 11:25 edsonjava 阅读(222) | 评论 (0)编辑 收藏
 

HttpClient入门

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送

未显示需要 JavaScript 的文档选项


拓展 Tomcat 应用

下载 IBM 开源 J2EE 应用服务器 WAS CE 新版本 V1.1


级别: 高级

金发华 , 软件工程师,IBM CSDL, IBM
陈樟洪 , 软件工程师,IBM CSDL, IBM

2005 年 11 月 10 日

HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。本文首先介绍 HTTPClient,然后根据作者实际工作经验给出了一些常见问题的解决方法。

HttpClient简介

HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient,更多使用 HttpClient 的应用可以参见http://wiki.apache.org/jakarta-httpclient/HttpClientPowered。HttpClient 项目非常活跃,使用的人还是非常多的。目前 HttpClient 版本是在 2005.10.11 发布的 3.0 RC4 。





回页首


HttpClient 功能介绍

以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。

  • 实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
  • 支持自动转向
  • 支持 HTTPS 协议
  • 支持代理服务器等

下面将逐一介绍怎样使用这些功能。首先,我们必须安装好 HttpClient。





回页首


HttpClient 基本功能的使用

GET 方法

使用 HttpClient 需要以下 6 个步骤:

1. 创建 HttpClient 的实例

2. 创建某种连接方法的实例,在这里是 GetMethod。在 GetMethod 的构造函数中传入待连接的地址

3. 调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例

4. 读 response

5. 释放连接。无论执行方法是否成功,都必须释放连接

6. 对得到后的内容进行处理

根据以上步骤,我们来编写用GET方法来取得某网页内容的代码。

  • 大部分情况下 HttpClient 默认的构造函数已经足够使用。
    HttpClient httpClient = new HttpClient();
    

  • 创建GET方法的实例。在GET方法的构造函数中传入待连接的地址即可。用GetMethod将会自动处理转发过程,如果想要把自动处理转发过程去掉的话,可以调用方法setFollowRedirects(false)。
    GetMethod getMethod = new GetMethod("http://www.ibm.com/");
    

  • 调用实例httpClient的executeMethod方法来执行getMethod。由于是执行在网络上的程序,在运行executeMethod方法的时候,需要处理两个异常,分别是HttpException和IOException。引起第一种异常的原因主要可能是在构造getMethod的时候传入的协议不对,比如不小心将"http"写成"htp",或者服务器端返回的内容不正常等,并且该异常发生是不可恢复的;第二种异常一般是由于网络原因引起的异常,对于这种异常 (IOException),HttpClient会根据你指定的恢复策略自动试着重新执行executeMethod方法。HttpClient的恢复策略可以自定义(通过实现接口HttpMethodRetryHandler来实现)。通过httpClient的方法setParameter设置你实现的恢复策略,本文中使用的是系统提供的默认恢复策略,该策略在碰到第二类异常的时候将自动重试3次。executeMethod返回值是一个整数,表示了执行该方法后服务器返回的状态码,该状态码能表示出该方法执行是否成功、需要认证或者页面发生了跳转(默认状态下GetMethod的实例是自动处理跳转的)等。
    //设置成了默认的恢复策略,在发生异常时候将自动重试3次,在这里你也可以设置成自定义的恢复策略
    getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
        		new DefaultHttpMethodRetryHandler()); 
    //执行getMethod
    int statusCode = client.executeMethod(getMethod);
    if (statusCode != HttpStatus.SC_OK) {
      System.err.println("Method failed: " + getMethod.getStatusLine());
    }
    

  • 在返回的状态码正确后,即可取得内容。取得目标地址的内容有三种方法:第一种,getResponseBody,该方法返回的是目标的二进制的byte流;第二种,getResponseBodyAsString,这个方法返回的是String类型,值得注意的是该方法返回的String的编码是根据系统默认的编码方式,所以返回的String值可能编码类型有误,在本文的"字符编码"部分中将对此做详细介绍;第三种,getResponseBodyAsStream,这个方法对于目标地址中有大量数据需要传输是最佳的。在这里我们使用了最简单的getResponseBody方法。
    byte[] responseBody = method.getResponseBody();
    

  • 释放连接。无论执行方法是否成功,都必须释放连接。
    method.releaseConnection();
    

  • 处理内容。在这一步中根据你的需要处理内容,在例子中只是简单的将内容打印到控制台。
    System.out.println(new String(responseBody));
    

下面是程序的完整代码,这些代码也可在附件中的test.GetSample中找到。


												
														package test;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

public class GetSample{
  public static void main(String[] args) {
  //构造HttpClient的实例
  HttpClient httpClient = new HttpClient();
  //创建GET方法的实例
  GetMethod getMethod = new GetMethod("http://www.ibm.com");
  //使用系统提供的默认的恢复策略
  getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler());
  try {
   //执行getMethod
   int statusCode = httpClient.executeMethod(getMethod);
   if (statusCode != HttpStatus.SC_OK) {
    System.err.println("Method failed: "
      + getMethod.getStatusLine());
   }
   //读取内容 
   byte[] responseBody = getMethod.getResponseBody();
   //处理内容
   System.out.println(new String(responseBody));
  } catch (HttpException e) {
   //发生致命的异常,可能是协议不对或者返回的内容有问题
   System.out.println("Please check your provided http address!");
   e.printStackTrace();
  } catch (IOException e) {
   //发生网络异常
   e.printStackTrace();
  } finally {
   //释放连接
   getMethod.releaseConnection();
  }
 }
}

												
										

POST方法

根据RFC2616,对POST的解释如下:POST方法用来向目的服务器发出请求,要求它接受被附在请求后的实体,并把它当作请求队列(Request-Line)中请求URI所指定资源的附加新子项。POST被设计成用统一的方法实现下列功能:

  • 对现有资源的注释(Annotation of existing resources)
  • 向电子公告栏、新闻组,邮件列表或类似讨论组发送消息
  • 提交数据块,如将表单的结果提交给数据处理过程
  • 通过附加操作来扩展数据库

调用HttpClient中的PostMethod与GetMethod类似,除了设置PostMethod的实例与GetMethod有些不同之外,剩下的步骤都差不多。在下面的例子中,省去了与GetMethod相同的步骤,只说明与上面不同的地方,并以登录清华大学BBS为例子进行说明。

  • 构造PostMethod之前的步骤都相同,与GetMethod一样,构造PostMethod也需要一个URI参数,在本例中,登录的地址是http://www.newsmth.net/bbslogin2.php。在创建了PostMethod的实例之后,需要给method实例填充表单的值,在BBS的登录表单中需要有两个域,第一个是用户名(域名叫id),第二个是密码(域名叫passwd)。表单中的域用类NameValuePair来表示,该类的构造函数第一个参数是域名,第二参数是该域的值;将表单所有的值设置到PostMethod中用方法setRequestBody。另外由于BBS登录成功后会转向另外一个页面,但是HttpClient对于要求接受后继服务的请求,比如POST和PUT,不支持自动转发,因此需要自己对页面转向做处理。具体的页面转向处理请参见下面的"自动转向"部分。代码如下:
    String url = "http://www.newsmth.net/bbslogin2.php";
    PostMethod postMethod = new PostMethod(url);
    // 填入各个表单域的值
    NameValuePair[] data = { new NameValuePair("id", "youUserName"),				
    new NameValuePair("passwd", "yourPwd") };
    // 将表单的值放入postMethod中
    postMethod.setRequestBody(data);
    // 执行postMethod
    int statusCode = httpClient.executeMethod(postMethod);
    // HttpClient对于要求接受后继服务的请求,象POST和PUT等不能自动处理转发
    // 301或者302
    if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 
    statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
        // 从头中取出转向的地址
        Header locationHeader = postMethod.getResponseHeader("location");
        String location = null;
        if (locationHeader != null) {
         location = locationHeader.getValue();
         System.out.println("The page was redirected to:" + location);
        } else {
         System.err.println("Location field value is null.");
        }
        return;
    }
    

完整的程序代码请参见附件中的test.PostSample





回页首


使用HttpClient过程中常见的一些问题

下面介绍在使用HttpClient过程中常见的一些问题。

字符编码

某目标页的编码可能出现在两个地方,第一个地方是服务器返回的http头中,另外一个地方是得到的html/xml页面中。

  • 在http头的Content-Type字段可能会包含字符编码信息。例如可能返回的头会包含这样子的信息:Content-Type: text/html; charset=UTF-8。这个头信息表明该页的编码是UTF-8,但是服务器返回的头信息未必与内容能匹配上。比如对于一些双字节语言国家,可能服务器返回的编码类型是UTF-8,但真正的内容却不是UTF-8编码的,因此需要在另外的地方去得到页面的编码信息;但是如果服务器返回的编码不是UTF-8,而是具体的一些编码,比如gb2312等,那服务器返回的可能是正确的编码信息。通过method对象的getResponseCharSet()方法就可以得到http头中的编码信息。
  • 对于象xml或者html这样的文件,允许作者在页面中直接指定编码类型。比如在html中会有<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>这样的标签;或者在xml中会有<?xml version="1.0" encoding="gb2312"?>这样的标签,在这些情况下,可能与http头中返回的编码信息冲突,需要用户自己判断到底那种编码类型应该是真正的编码。

自动转向

根据RFC2616中对自动转向的定义,主要有两种:301和302。301表示永久的移走(Moved Permanently),当返回的是301,则表示请求的资源已经被移到一个固定的新地方,任何向该地址发起请求都会被转到新的地址上。302表示暂时的转向,比如在服务器端的servlet程序调用了sendRedirect方法,则在客户端就会得到一个302的代码,这时服务器返回的头信息中location的值就是sendRedirect转向的目标地址。

HttpClient支持自动转向处理,但是象POST和PUT方式这种要求接受后继服务的请求方式,暂时不支持自动转向,因此如果碰到POST方式提交后返回的是301或者302的话需要自己处理。就像刚才在POSTMethod中举的例子:如果想进入登录BBS后的页面,必须重新发起登录的请求,请求的地址可以在头字段location中得到。不过需要注意的是,有时候location返回的可能是相对路径,因此需要对location返回的值做一些处理才可以发起向新地址的请求。

另外除了在头中包含的信息可能使页面发生重定向外,在页面中也有可能会发生页面的重定向。引起页面自动转发的标签是:<meta http-equiv="refresh" content="5; url=http://www.ibm.com/us">。如果你想在程序中也处理这种情况的话得自己分析页面来实现转向。需要注意的是,在上面那个标签中url的值也可以是一个相对地址,如果是这样的话,需要对它做一些处理后才可以转发。

处理HTTPS协议

HttpClient提供了对SSL的支持,在使用SSL之前必须安装JSSE。在Sun提供的1.4以后的版本中,JSSE已经集成到JDK中,如果你使用的是JDK1.4以前的版本则必须安装JSSE。JSSE不同的厂家有不同的实现。下面介绍怎么使用HttpClient来打开Https连接。这里有两种方法可以打开https连接,第一种就是得到服务器颁发的证书,然后导入到本地的keystore中;另外一种办法就是通过扩展HttpClient的类来实现自动接受证书。

方法1,取得证书,并导入本地的keystore:

  • 安装JSSE (如果你使用的JDK版本是1.4或者1.4以上就可以跳过这一步)。本文以IBM的JSSE为例子说明。先到IBM网站上下载JSSE的安装包。然后解压开之后将ibmjsse.jar包拷贝到<java-home>\lib\ext\目录下。
  • 取得并且导入证书。证书可以通过IE来获得:

    1. 用IE打开需要连接的https网址,会弹出如下对话框:



    2. 单击"View Certificate",在弹出的对话框中选择"Details",然后再单击"Copy to File",根据提供的向导生成待访问网页的证书文件



    3. 向导第一步,欢迎界面,直接单击"Next",



    4. 向导第二步,选择导出的文件格式,默认,单击"Next",



    5. 向导第三步,输入导出的文件名,输入后,单击"Next",



    6. 向导第四步,单击"Finish",完成向导



    7. 最后弹出一个对话框,显示导出成功



  • 用keytool工具把刚才导出的证书倒入本地keystore。Keytool命令在<java-home>\bin\下,打开命令行窗口,并到<java-home>\lib\security\目录下,运行下面的命令:

    																
    																		keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
    
    																
    														

    其中参数alias后跟的值是当前证书在keystore中的唯一标识符,但是大小写不区分;参数file后跟的是刚才通过IE导出的证书所在的路径和文件名;如果你想删除刚才导入到keystore的证书,可以用命令:

    																
    																		keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
    
    																
    														

  • 写程序访问https地址。如果想测试是否能连上https,只需要稍改一下GetSample例子,把请求的目标变成一个https地址。
    GetMethod getMethod = new GetMethod("https://www.yourdomain.com");
    

    运行该程序可能出现的问题:

    1. 抛出异常java.net.SocketException: Algorithm SSL not available。出现这个异常可能是因为没有加JSSEProvider,如果用的是IBM的JSSE Provider,在程序中加入这样的一行:

     if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null)
     Security.addProvider(new IBMJSSEProvider());
     

    或者也可以打开<java-home>\lib\security\java.security,在行

    security.provider.1=sun.security.provider.Sun
    security.provider.2=com.ibm.crypto.provider.IBMJCE
    

    后面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider

    2. 抛出异常java.net.SocketException: SSL implementation not available。出现这个异常可能是你没有把ibmjsse.jar拷贝到<java-home>\lib\ext\目录下。

    3. 抛出异常javax.net.ssl.SSLHandshakeException: unknown certificate。出现这个异常表明你的JSSE应该已经安装正确,但是可能因为你没有把证书导入到当前运行JRE的keystore中,请按照前面介绍的步骤来导入你的证书。

方法2,扩展HttpClient类实现自动接受证书

因为这种方法自动接收所有证书,因此存在一定的安全问题,所以在使用这种方法前请仔细考虑您的系统的安全需求。具体的步骤如下:

  • 提供一个自定义的socket factory(test.MySecureProtocolSocketFactory)。这个自定义的类必须实现接口org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory,在实现接口的类中调用自定义的X509TrustManager(test.MyX509TrustManager),这两个类可以在随本文带的附件中得到
  • 创建一个org.apache.commons.httpclient.protocol.Protocol的实例,指定协议名称和默认的端口号
    Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
    

  • 注册刚才创建的https协议对象
    Protocol.registerProtocol("https ", myhttps);
    

  • 然后按照普通编程方式打开https的目标地址,代码请参见test.NoCertificationHttpsGetSample

处理代理服务器

HttpClient中使用代理服务器非常简单,调用HttpClient中setProxy方法就可以,方法的第一个参数是代理服务器地址,第二个参数是端口号。另外HttpClient也支持SOCKS代理。


												
														httpClient.getHostConfiguration().setProxy(hostName,port);

												
										





回页首


结论

从上面的介绍中,可以知道HttpClient对http协议支持非常好,使用起来很简单,版本更新快,功能也很强大,具有足够的灵活性和扩展性。对于想在Java应用中直接访问http资源的编程人员来说,HttpClient是一个不可多得的好工具。





回页首


参考资料

  • Commons logging包含了各种各样的日志API的实现,读者可以通过站点http://jakarta.apache.org/commons/logging/得到详细的内容

  • Commons codec包含了一些一般的解码/编码算法。包含了语音编码、十六进制、Base64和URL编码等,通过http://jakarta.apache.org/commons/codec/可以得到详细的内容

  • rfc2616是关于HTTP/1.1的文档,可以在http://www.faqs.org/rfcs/rfc2616.html上得到详细的内容,另外rfc1945是关于HTTP/1.0的文档,通过http://www.faqs.org/rfcs/rfc1945.html可以得到详细内容

  • SSL――SSL 是由 Netscape Communications Corporation 于 1994 年开发的,而 TLS V1.0 是由 Internet Engineering Task Force(IETF)定义的标准,它基于 SSL V3.0,并且在使用的加密算法上与其有些许的不同。例如,SSL 使用 Message Authentication Code(MAC)算法来生成完整性校验值,而 TLS 应用密钥的 Hashing for Message Authentication Code(HMAC)算法。

  • IBM JSSE提供了SSL(Secure Sockets Layer)和TLS(Transport Layer Security)的java实现,在http://www-03.ibm.com/servers/eserver/zseries/software/java/jsse.html中可以得到详细的信息

  • Keytool是一个管理密钥和证书的工具。关于它详细的使用信息可以在http://www.doc.ic.ac.uk/csg/java/1.3.1docs/tooldocs/solaris/keytool.html上得到

  • HTTPClient的主页是http://jakarta.apache.org/commons/httpclient/,你可以在这里得到关于HttpClient更加详细的信息
posted @ 2006-11-08 11:21 edsonjava 阅读(269) | 评论 (0)编辑 收藏
 


//以下部分为通过AJAX读取商品信息
var req;

function Change_Select()
{
    var useTypeID=document.getElementById("msgtype1").value;
 var flowerTypeID=document.getElementById("msgtype2").value;
 var flowerPriceID=document.getElementById("msgtype3").value;
 var url="/12580/jsp/ShowProductServlet?msgtype1="+ useTypeID +"&msgtype2="+flowerTypeID+"&msgtype3="+flowerPriceID;
     if(window.XMLHttpRequest)
        {
            req=new XMLHttpRequest();
        }else if(window.ActiveXObject)
        {
            req=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        if(req)
        {
            req.open("GET",url,true);
            req.onreadystatechange = callback;
            req.send(null);
        }
}

function callback()
{
    if(req.readyState == 4)
    {
        if(req.status == 200)
        {
           var dataTableBody=document.getElementById("tbProduct");
           dataTableBody.innerHTML=req.responseText;
        }else{
            alert("Not able to retrieve description:"+req.statusText);
        }
    }
}

 


//--------------------------------------------
//以下部分为通过AJAX动态显示小窗口内容

function distChange(htmlStr){
 distHelp.innerHTML = htmlStr;
}

var req1;

function Change_Select1()

    var cityID=document.getElementById("stage2").value.substr(0,document.getElementById("stage2").value.indexOf("/"));
 var url="/12580/jsp/SelectCityChangeServlet?isFront=1&id="+cityID;
     if(window.XMLHttpRequest)
        {
            req1=new XMLHttpRequest();
        }else if(window.ActiveXObject)
        {
            req1=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        if(req1)
        {
            req1.open("GET",url,true);
            req1.onreadystatechange = callback1;
            req1.send(null);
        }
}

function callback1()
{
    if(req1.readyState == 4)
    {
        if(req1.status == 200)
        {
           var dataTableBody=document.getElementById("distHelp");
           dataTableBody.innerHTML=req1.responseText;
        }else{
            alert("Not able to retrieve description:"+req1.statusText);
        }
    }
}

posted @ 2006-10-27 23:55 edsonjava 阅读(590) | 评论 (0)编辑 收藏
 
1.用keytool生成server.keystore文件:
C:\Documents and Settings\new>keytool -genkey -alias tc-ssl -keyalg RSA -keystor
e c:\server.keystore -validity 3650

生成完后放入C:\jboss-3.2.6\server\default\conf中。

2。配置server.xml文件
<!-- SSL/TLS Connector configuration using the admin devl guide keystore-->
      <Connector port="8443" address="${jboss.bind.address}"
           maxThreads="100" minSpareThreads="5" maxSpareThreads="15"
           scheme="https" secure="true" clientAuth="false"
           keystoreFile="${jboss.server.home.dir}/conf/server.keystore"
           keystorePass="111111" sslProtocol = "TLS" />
3。访问https://localhost:8443/jmx-console/index.jsp,可以了。
同时http://localhost:8080/jmx-console/index.jsp也可以访问,
如果将http转向到https: 修改WEB应用的web.xml
=========web.xml==================
<security-constraint>
     <web-resource-collection>
       <web-resource-name>HtmlAdaptor</web-resource-name>
       <description>An example security config that only allows users with the
         role JBossAdmin to access the HTML JMX console web application
       </description>
       <url-pattern>/</url-pattern>
       <!-- <http-method>GET</http-method>
       <http-method>POST</http-method> -->
     </web-resource-collection>
     <!--<auth-constraint>
       <role-name>JBossAdmin</role-name>
     </auth-constraint> -->
     <user-data-constraint>
            <description>Protection should be CONFIDENTIAL</description>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
   </security-constraint>
posted @ 2006-07-20 23:15 edsonjava 阅读(856) | 评论 (0)编辑 收藏
 
     摘要: 这些天稍微玩了一下 Axis ,以前做 WebServices 都使用 JBuilder ,感觉做 WebServices 如此简单,现在自己手动做,原来也是如此简单。高兴之余写一个简单的初学手册,就算是学习成果吧。当然对 Axis ...  阅读全文
posted @ 2006-07-14 17:23 edsonjava 阅读(245) | 评论 (0)编辑 收藏
 

设计模式大概分为三种:1)创建型2)结构型3)行为型.
创建型模式:(如何创建对象)
1.工厂模式(FACTORY METHOD PATTERN)提供了一个简单的决策类,他根据提供的数据返回一个抽象基类的多个子类中的一个.
2.抽象工厂模式(ABSTRACT FACTORY PATTERN)提供了一个创建并返回一系列相关对象的接口.他的主要目的是隔离要生成的具体类,这些类的实际类名隐藏才工厂类,客户端根本不需要知道.
3.单件模式(SINGLETON PATTERN)是指某个类只能有一个实例,他提供了一个访问该实例的全局访问点.
4.生成器模式(BUILDER PATTERN)将一个复杂对象的构建与他的表示分开,这样就能根据程序的需要创建不同的表现形式.他有点类似抽象工厂模式,两者都返回由许多方法和对象组成的类,差别在于,生成器是根据提供给他的数据一步一步地创建一个复杂对象.
5.原型模式(PROTOTYPE PATTERN)是先实例化一个类,然后拷贝或克隆该类来创建新的实例,可以用公有方法进一步修改.他能根据需要克隆类,在运行时添加或删除类,问题在于如果类早就存在,则不能改变他们来增加需要的克隆方法,另外,间接引用其他类的类也不能被真正克隆.

结构型模式:(如何组合对象)
1.适配器模式(ADAPTER PATTERN)可以使一个类的接口匹配另外一个接口.
2.组合模式(COMPOSITE PATTERN)对象的组合,他允许你构建一棵由各种相关类组成的树,其中任何一个对象既可以是一个组合,也可以是一个叶子.
3.代理模式(PROXY PATTERN)通常是一个简单对象,他代替一个比较复杂的稍后会调用的对象,而且实例化该对象代价比较高.
4.享元模式(FLYWEIGHT PATTERN)用于共享对象,其中的每一个实例都不包含自己的状态,而是将状态存储在外部.把一部分的类数据移到类外部,在执行方法时将数据传递进来.
5.外观模式(FACADE PATTERN)对客户屏蔽了复杂的子系统组件,并为一般用户提供了一个简单的程序接口,但他并没有限制高级用户在需要时使用深层的复杂的类.
6.桥接模式(BRIDGE PATTERN)将对象的接口和他的实现分离,这样就可以独立的改变他们.
7.装饰模式(DECORATOR PATTERN)他可以动态地给对象添加功能.他和外观模式的区别在于外观模式是将复杂系统隐藏在一个简单接口,而装饰模式通过包装一个类来增加功能.

行为型模式:(如何在对象之间通信)
1.职责链模式(CHAIN OF RESPONSIBILITY PATTERN)把请求从链中的一个对象传递到下一个对象,直到请求被响应.链中的每个对象都是"自治"的,他对其他对象一无所知,只判断他本身能不能满足请求,不能的话就转到下一个,但你必须知道链中的最后一个对象是哪一个.
2.命令模式(COMMAND PATTERN)用简单的对象表示软件命令的执行.他把一个申请特定操作的请求封装到一个对象,并给这个对象一个众所周知的接口.
3.解释器模式(INTERPRETER PATTERN)提供一种如何把语言元素包含在程序中的定义.
4.迭代器模式(ITERATOR PATTERN)提供一种顺序访问一个类中的一系列数据的方式,他允许使用一个标准接口顺序访问一个数据列表或集合,而又不需知道数据内部表示细节.
5.中介者模式(MEDIATOR PATTERN)定义了如何用一个对象简化对象之间的通信,使对象之间不必互相了解.
6.备忘录(MEMENTO PATTERN)定义了如何保存一个类实例的内容以便以后能恢复他
7.观察者模式(OBSERVER PATTERN)定义了一种把改动通知给多个对象的方式.
8.状态模式(STATE PATTERN)允许一个对象在其内部状态改变时修改他的行为.
9.策略模式(STRATEGY PATTERN)将算法封装到类里.
10.模板方法模式(TEMPLATE METHOD PATTERN)提供了算法的一个抽象定义.
11.访问者模式(VISITOR PATTERN)在不改变类的前提下,为类添加多种操作.

posted @ 2006-07-14 17:08 edsonjava 阅读(208) | 评论 (0)编辑 收藏
 

【摘 要】Axis本质上就是一个SOAP引擎,提供创建服务器端、客户端和网关SOAP操作的基本框架。Axis目前版本是为Java编写的,不过为C++的版本正在开发中。

 什么是SOAP?

  SOAP是一个基于XML的用于应用程序之间通信数据编码的传输协议。最初由微软和Userland Software提出,随着不断地完善和改进,SOAP很快被业界广泛应用。

  什么是Axis?

  Axis本质上就是一个SOAP引擎,提供创建服务器端、客户端和网关SOAP操作的基本框架。Axis目前版本是为Java编写的,不过为C++的版本正在开发中。

  配置webservice具体步骤:

  1.安装weblogic。

  2.配置一个web应用,如“\\lijunjie\e$\software\bea\user_projects\domains\lijjdomain\applications\ cntapws “。

  3.从apache网站上下载axis-bin-1_2_1.tar.gz,解压后copy到cntapws下面。

  4.打开“http://localhost:7001/console“,配置cntapws 的web应用。

  5.axis1.2和weblogic8.1有冲突,原因是weblogic 本身携带的webservice.jar使用的旧的javax.xml.soap.*,而不是像axis使用J2EE1.4新的版本。解决方案是在WEB -INF/weblogic.xml文件中如下的配置:

<weblogic-web-app>
<container-descriptor>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
</weblogic-web-app>

  6.按 http://localhost:8080/axis/happyaxis.jsp地址,可以显示你发布的webservice是否配置成功,如果显示必须的包缺失,则你的webservice不能正常运行。

  开发webservice流程

  1.写普通的java类,该类中有你要发布到webservice上的有关方法。

  2.配置server-comfig.xml, 把你要发布的方法写入该文件。具体内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<globalConfiguration>

<parameter name="adminPassword" value="admin"/>

<parameter name="attachments.Directory" value="C:\eclipse\workspace\bookstore\WEB-INF\attachments"/>

<parameter name="attachments.implementation" value="org.apache.axis.attachments.AttachmentsImpl"/>

<parameter name="sendXsiTypes" value="true"/>

<parameter name="sendMultiRefs" value="true"/>

<parameter name="sendXMLDeclaration" value="true"/>

<parameter name="axis.sendMinimizedElements" value="true"/>

<requestFlow>

<handler type="java:org.apache.axis.handlers.JWSHandler">

<parameter name="scope" value="session"/>

</handler>

<handler type="java:org.apache.axis.handlers.JWSHandler">

<parameter name="scope" value="request"/>

<parameter name="extension" value=".jwr"/>

</handler>

</requestFlow>

</globalConfiguration>

<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>

<handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>

<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>

<service name="Version" provider="java:RPC">

<parameter name="allowedMethods" value="getVersion"/>

<parameter name="className" value="org.apache.axis.Version"/>

</service>

<!--要配置的内容start,注意每一个要发布成webservice的类都要配置成一个service如下à

<service name="webserviceproxy" provider="java:RPC">

<parameter name="className" value="com.copia.webservice.server.proxy.WebServiceProxy"/>

<parameter name="allowedMethods" value="*"/>

<!—具体某个方法说明-->

<operation name="login" returnType="ns:LoginOutServiceBean">

<parameter name="branchNo" type="xsd:string"/>

<parameter name="userId" type="xsd:string"/>

<parameter name="password" type="xsd:string"/>

<parameter name="certificateNo" type="xsd:string"/>

</operation>

<!—如果该方法的参数有基本类型和对象类型,则不要声明,webservice能够找到相关的类及方法-->

<!—声明涉及到的所有类及其父类,引用类,还有它们的相关类,类似与对象树遍历,某个类声明缺失也会导致运行失败-->

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:LoginOutServiceBean"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.module.security.login.LoginOutServiceBean" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:User"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.client.User" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:GetBatchListInServiceBean"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.module.batch.service.GetBatchListInServiceBean" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:GetBatchListOutServiceBean"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.module.batch.service.GetBatchListOutServiceBean" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:BatchJobDefBean"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.module.batch.service.BatchJobDefBean" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:ValueObjectPage"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.voa.ValueObjectPage" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:ValueObjectColInfo"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.voa.ValueObjectColInfo" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:ValueObjectList"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.voa.ValueObjectList" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:PageInfo"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.voa.PageInfo" xmlns:ns="webserviceproxy"/>

<typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns:ValueObjectMetaData"

serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"

type="java:com.copia.core.voa.ValueObjectMetaData" xmlns:ns="webserviceproxy"/>

</service>

<!--要配置的内容endà

<service name="AdminService" provider="java:MSG">

<parameter name="allowedMethods" value="AdminService"/>

<parameter name="enableRemoteAdmin" value="false"/>

<parameter name="className" value="org.apache.axis.utils.Admin"/>

<namespace>http://xml.apache.org/axis/wsdd/</namespace>

</service>

<transport name="local">

<responseFlow>

<handler type="LocalResponder"/>

</responseFlow>

</transport>

<transport name="http">

<requestFlow>

<handler type="URLMapper"/>

<handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>

</requestFlow>

</transport>

</deployment>

  3.在地址栏输入http://localhost:7001/cntapws/services/webserviceproxy?wsdl,如没有什么错误,则表示你的webservice发布成功了。

  Java webservice客户端编程

  1.启动你配置好的weblobic。

  2.进入dos界面,在你的临时目录(任意目录)下根据wsdl产生客户文件,如下指令:

E:\kk>java -Djava.ext.dirs=E:\software\bea\user_projects\domains\lijjdomain\appl

ications\axis\WEB-INF\axislib org.apache.axis.wsdl.WSDL2Java http://localhost:7001/cntapws/services/webserviceproxy?wsdl即可在kk目录下产生有关文件。

  3.把产生的文件copy到你的工程中,再写一个webservice调用测试类,如下:

public class TestWs {

public static void main(String[] args) {

WebServiceProxyService service = new WebServiceProxyServiceLocator();

try {

WebServiceProxy serviceProxy = service.getwebserviceproxy();

User user = new User();

user.setBranchNo("000001");

user.setClerkNo("1004");

GetBatchListInServiceBean in = new GetBatchListInServiceBean();

in.setCurPageNo(1);

String serviceName = "GetBatchListService";

GetBatchListOutServiceBean out = (GetBatchListOutServiceBean)serviceProxy.getBatchListService(user, in, serviceName);

BatchJobDefBean[] defBeans = out.getBatchJobDefBeans();

for(int i = 0 ; i < defBeans.length ; i++){

System.out.println(defBeans[i].getBatchName());

}

}catch(Exception ex){

ex.printStackTrace();

}

}

}

posted @ 2006-07-14 10:41 edsonjava 阅读(927) | 评论 (1)编辑 收藏
 
  Eclipse IDE 变得如此热门,以至于出现在许多工作简历中。但是,也正因为它的功能繁多,使得难于入手了。在这篇文章中, Sam Brodkin 分享了他最常用的一些Eclipse功能。他也提到了那些会让你的手指动的飞快的热键组合。在工作的时候,人们会怀疑你是在打电玩。

  Eclipse最妙的地方是它的功能是如此的丰富。如果你找不到某项功能,要么是你没有好好的去找,要么有一个插件可以实现它。Eclipse最糟的地方也是它的功能如此的丰富。拥挤的工具条,臃肿的菜单,以及配置对话框都让我想起了拉比林特斯迷宫(译者注:Minotau’s labyrinth,希腊神户中的人身牛头怪,住在Labyrinth迷宫中,吃人们供奉的童男童女,最后被Theseus杀死)(尽管配置对话框在Eclipse3.1中已经好了很多)。甚至是有经验的Eclipse程序员也是慢吞吞,笨手笨脚的。我观察过无数的同事,当试图在Eclipse的navigation面板里找一个类的时候,不假思索的按下“Ctrl + Shift + T”(打开型别(open type))。其实,只要有意识的投入一点点时间,你也会不在笨手笨脚的,而成为热键高手。

  精通Eclipse不单提高了你的效率,(我知道,按小时赚钱的签约人更喜欢文本编辑器),对你的求职简历也有好处。既然Eclipse已经越来越多的出现在Java工作说明上,那么在你的求职简历上也绝对是个好词。我把它放在我的个人简历上,就在POJO(简单初始Java对象)和POJI(简单初始Java接口)的中间。越来越多的公司转向Eclipse,因为它免费,而且为大多数程序员所接受。(除了那些少有的IntelliJ和Emacs的崇拜者)

  因此,你的简历上并不都是帮助你提高编程效率的大话,在本文中,我将给你几条Eclipse高效的建议。

  Eclipse的快捷键组合功能

  使用Eclipse的快捷键组合功能,让你看起来像是在打电玩。特别是让人想起《真人快打》(Mortal Combat)的感觉。要成为《真人快打》的高手,你必须记住大量的按键顺序。比如,要使出Sub-Zero Skeleton Rip,你必须依次按下和X键。这里的控制杆就是Ctrl,Alt,Shift,字母键,数字键和功能键的组合。

  让我们按照使用频率来看看我最爱用的一些热键组合。(注:以下内容在Eclipse3.02及一上版本通过测试)

  1. Control-Shift-T: 打开类型(Open type)。如果你不是有意磨洋工,还是忘记通过源码树(source tree)打开的方式吧。

  2. Control-Shift-R: 打开资源(不只是用来寻找Java文件)。小提示:利用Navigator视图的黄色双向箭头按钮让你的编辑窗口和导航器相关联。这会让你打开的文件对应显示在导航器的层级结构中,这样便于组织信息。如果这影响了速度,就关掉它。

  3. F3: 打开申明(Open declaration)。或者,利用Declaration Tab(在Java视图模式下,选择Windows --> Show View -- > Declaration)。当你选中代码中的一个方法,然后按这个按键,它会把整个方法在申明方框里显示出来。

  4. Alt-left arrow: 在导航历史记录(Navigation History)中后退。就像Web浏览器的后退按钮一样,在利用F3跳转之后,特别有用。(用来返回原先编译的地方)

  5. Alt-right arrow: 导航历史记录中向前。

  6. Control-Q: 回到最后依次编辑的地方。这个快捷键也是当你在代码中跳转后用的。特别是当你钻的过深,忘记你最初在做什么的时候。

  7. Control-Shift-G: 在workspace中搜索引用(reference)。这是重构的前提。对于方法,这个热键的作用和F3恰好相反。它使你在方法的栈中,向上找出一个方法的所有调用者。一个与此相关的功能是开启“标记”功能(occurrence marking) 。选择Windows->Preferences->Java-> Editor-> Mark Occurrences,勾选选项。这时,当你单击一个元素的时候,代码中所有该元素存在的地方都会被高亮显示。我个人只使用“标记本地变量”(Mark Local Variables)。注意:太多的高亮显示会拖慢Eclipse。

  8. Control-Shift-F: 根据代码风格设定重新格式化代码。我们的团队有统一的代码格式,我们把它放在我们的wiki上。要这么做,我们打开Eclipse,选择WindowPreferencesJavaCode Style,然后设置Code Formatter,Code Style和Organize Imports。利用导出(Export)功能来生成配置文件。我们把这些配置文件放在wiki上,然后团队里的每个人都导入到自己的Eclipse中。

  9. Control-O: 快速概要(quick outline)。通过这个快捷键,你可以迅速的跳到一个方法或者属性,只需要输入名字的头几个字母。

  10. Control-/: 对一行注释或取消注释。对于多行也同样适用。

  11. Control-Alt-down arrow: 复制高亮显示的一行或多行。

  12. Alt-down arrow: 将一行或多行向下移动。Alt-up arrow会向上移动。

  其他的热键在菜单里有。你可以通过按下Control-Shift-L(从3.1版本开始),看到所有快捷键的列表。按下Control-Shift-L两次,会显示热键对话框(Keys Preferences dialog),你可以在这里自己设置热键。我欢迎你在Talkback部分发表你的Eclipse提示。
他的Eclipse窍门

  我总结了几个相关的小窍门:

  锁定命令行窗口:在命令行视图中(Window Show View Other Basic Console),试试看用滚动锁定按钮来锁定控制台输出不要滚屏。

  使用Ant视图:在我的Java或Debug模式下,我喜欢显示出Ant视图,这样我就可以迅速的运行Ant任务。通过Window  Show View  Other  Ant可以找到该视图。把Ant视图放在屏幕的一角, 通过“添加编译文件(Add Buildfiles)”按钮来添加build.xml文件。在3.1版本中,甚至支持Ant调试脚本语言。

  自动遍历一个集合:for + Control-Space: 如果你还不知道,那么你应该记住Control-Space是自动完成功能。在Eclipse中,你还可以自动完成结构。在一个数组或集合范围内,试试看输入“for”然后按下Control-Space键。Eclipse会问你你想要遍历哪一个集合然后自动完成循环代码。

  使用分级布局:在包浏览视图(Package Explorer view)中默认的布局(扁平式)方式让我困惑,它把包的全名显示在导航树(navigation tree)中。我更喜欢我源码的包和文件系统视图,在Eclipse中叫做分级布局(Hierarchical Layout)。要切换到这种模式,点击包浏览视图中向下的按钮,选择布局(Layout),然后选择分级(Hierarchial)。

  一次显示多个文件:你可以一次浏览多个文件。把不在激活状态的编辑窗口拖到激活窗口的底部或侧边的滚动条上,就可以打开该编辑窗口。这是我能描述该窍门的最好方式了。

  同时打开两个Eclipse:要将改动从一个CVS分支上合并到另外一个上,我喜欢通过同时打开两个工作目录(Workspace)不同Eclipse来实现。这样我可以通过比较CVS上的最新版本看到所有的变化(右键单击工程,然后选择Compare with  Lastest from HEAD)然后把每一个变化都合并到另外一个CVS分支上。启动多个Eclipse的最简单的方法是利用Eclipse Launcher。

  Implementors插件:安装一个能够跳到一个接口的实现的插件。如果你是个dependency injection 粉丝,或者正在基于编写优良的接口工作,那么你需要一个这样的插件来加速代码导航。 你可以在SourceForge找到这个插件。

  就像Modest Mouse乐队的歌曲“Out of Gas”唱的,“意见就像小猫,我已经送给别人”。这些是我的Eclipse小猫,希望你能给它找一个温暖的家。


posted @ 2006-07-14 10:35 edsonjava 阅读(363) | 评论 (1)编辑 收藏
 
Web Service是最近几年比较火的一个东西,它带来了一大堆的新名词,所以显得比较炫。看透其华而不实的表面,它也就不再神奇。下面的讨论均以Java为参考。

1 访问一个Web Service实际上可以看作调用一个函数,唯一不同的就是这个函数是远程的,这么一说,它和RMI就没有什么本质的区别了。
既然是一个函数,当然要有函数的声明了,完成这个工作的就是WSDL,它详细的定义函数的原型,包括函数名、入口参数、出口参数,这就是WSDL中opertion完成的工作。
既然是一个远程的函数,还要涉及与远程地址的一个绑定,这是WSDL中service的任务。
Axis是一个可以通过WSDL生成相应访问代码的开发包,JBuilder中将它集成了进去,通过Wizard的方式简化了原本需要在命令行中手工完成的操作。

2 既然是远程访问,就一定要有一个访问协议,Web Service的访问协议就是SOAP,SOAP建立在XML之上,不同的就是对XML原本没有限制的格式加上了一些限制,需要有envelope,在envelope中,还要分header和body。
如果利用SOAP开发Web Service的程序,那就需要根据WSDL的定义来自行组装SOAP包,这显然要比利用WSDL直接面向Web Service开发要复杂一些。
JAXM是一个利用SOAP进行通信的开发包,它简化了SOAP消息的打包过程。

3 SOAP是建立在XML之上的,那么显然XML的开发包同样适合于SOAP。
在这个层次上开发Web Service,除了要完成上一层的工作外,还要自行按照SOAP的格式组装SOAP消息,这显然又增加了工作量。
XML的开发工具就比较多了,从最简单的SAX和DOM到DOM4J、JDOM,还有不少XML到对象绑定的工具,如JAXB、Castor等等。
其实,不考虑Web Service,完全用XML做通信协议的情况也并不少见。知晓XML-RPC的存在,就不难理解了XML做通信的含义了。

截至到这里所讨论的内容,Sun提供了JWSDP(Java Web Service Developer Pack),其中包含从XML解析到WSDL生成的全套解决方案。

4 上面讨论的所有东西实际上都还停留在传递消息的内容上,并未涉及通信的过程。不要一看到Web Service的Web就想当然认为它只能通过HTTP来传输。前面的讨论可以看出,所有的消息内容与传输并无直接关系,所以,无论是以HTTP传输,还是SMTP或是自定义的协议都没有问题。
在这个层次上开发Web Service,前面的种种险阻之外,还要完成对XML的手工解析工作。
这里还是以最常见的HTTP方式来讨论。
HTTP的开发就将Server和Client区别对待,Server的实现通常的选择是Servlet,让Web Server替我们完成HTTP协议的解析可以省去我们很多的作。Client的实现可以选择JDK自带的Http Client,Apache的Jakarta项目下的Commons子项目也提供了一个HttpClient。

5 无论是HTTP还是SMTP,抑或是自定义协议,归根结底都是应用级的协议,底层的实现都是由Socket完成。到了这个层次基本就是原始时代了,什么都没有,一切都要手工完成。
在这个层次上开发Web Service,所有前面的困难都要一一经历,此外,还有协议的开发等待着不幸至此的人们。
到了这里,也不需要其它的工具了,JDK自带的Socket可以保打天下。

6 还想往下吗?再往下就是操作系统的实现了,Java的平台无关就失去了意义,也超出了我目前所了解的范围,到此打住吧!

前面所提及应该算是Web Service的一个基本知识结构,这里并没有讨论UDDI等等的内容,一来我对它并不了解,二来那应该属于应用,不应该算Web Service实现中。

虽然我们可能不会从最下层开发Web Service,但遇到底层的问题的情况却在所难免。
我就曾经在开发一个Web Service应用的时候,被人抓住HTTP头中的SOAPAction大小写与某个所谓的规范不同,我查了半天HTTP规范和SOAP规范,知道了HTTP是区分大小,而SOAPAction就是应该这么写,据理力争,指出所谓规范的错误。

经过前面的讨论,我们可以看出,Web Service并没有什么神秘可言,所有的东西都是建立在已有东西的基础之上。技术的发展不会是无中生有,只会是一个更好的解决方案而已,在追新求变之前,一个比较牢固的基础才是最重要

posted @ 2006-07-12 21:19 edsonjava 阅读(177) | 评论 (0)编辑 收藏
 
Jboss自带均衡器的配置

将文件夹%jboss%\docs\examples\varia\loadbalancer\loadbalancer.sar拷贝到%jboss%\ server\all\deploy下,并且修改loadbalancer.sar\loadbalancer.sar\META-INF\jboss- service.xml,在<host>标签中类出所有节点,在<sticky-session>标签中指定是否使用粘性 session。配置完成。

该均衡器的缺点是负载能力相对不高,配置参数太少,比如无法指定不同节点的负载加权,所以后面都以mod_jk为例,不再讲解jboss自带的负载均衡器的内容。


负载均衡的配置基本完成,启动jboss,其中过程中会列出DefaultPatition中所有的节点:

run.bat -c all
2、session sticky配置

apache 应该会以粘性session的方式分发请求。部署一个应用测试一下,你会发现粘性session没有起作用。因为我们还没有给jboss配置jvm路由 ( jvmRoute),apache就无法知道究竟哪些session是属于哪个节点的。我们继续往下:

修改server1机器上的jboss的配置文件:%jboss%\server\all\deploy\jbossweb-tomcat55.sar\ META-INF\ jboss-service.xml

在110行有:<attribute name="UseJK">false</attribute>,将它改为true。值得注意的是在这行标签上面有一段注释,要求你在server.xml中必须有:
Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost"

请注意这里有一个气死人不偿命的小bug,jboss的官方文档把 jvmRoute写成了jmvRoute,就是v和m两个字母的颠倒让我郁闷了三天,翻遍了jboss.com和theserverside.com。都是直接拷贝的错,吐血吐到脱水啊。

下面需要修改server1上的%jboss%\server\all\deploy\jbossweb-tomcat55.sar\ server.xml,在32行左右有:


<Engine name="jboss.web" defaultHost="localhost">

给它增加一个jvmRoute属性:


<Engine jvmRoute="server1" name="jboss.web" defaultHost="localhost">

请注意,jvmRoute的值必须和mod_jk中的节点名字正确对应,否则无法正确路由。Cluster中的所有节点都应该做相应的配置。

Jboss的配置完成了,下面需要在你的web应用中修改配置文件,让它支持集群。

在WEB-INF\web.xml中加入属性: <distributable/>

Ok,基于用户的cluster完成了,每个用户会绑定都某个节点上进行交互。这种绑定是如何完成的呢?原来apache把客户分发到节点后,该节点会在用户的session id后面加上此节点的路由名称,变成这个样子:

Efdfxxd98daja87daj76da2dka**,server1

有了这个标志,就能分辨该session属于哪个节点。

3、session replication配置

下面要做的是基于request的cluster,也就让各个节点之间互相复制session状态。有两种复制模式,同步与异步。使用同步的方式, jboss会把session复制的操作和对request的响应放到一个应用事务(application transaction),session 复制完成后才去处理request。异步复制则发送session复制的消息后马上处理request,session复制则会稍有延迟。但是在多框架的 web页面中,这样的集群方式会有问题。由于frame在同一时间发出多个request,会造成一些混乱,这也是采用基于用户的集群方式的原因之一。

JBoss 4.0.2 中采用了Jboss cache来实现session复制,实际上就是一个分布式缓存,由于session id中包含了jvm route,所以能够分辨session属于哪个节点。Session的更新类似于hibernate中的乐观锁,有了更新之后就让session的版本号增加,其他节点通过对比版本号来决定是否同步session状态。

配置session replication首先需要编辑
%jboss% server\all\deploy\jbossweb-tomcat55.sar\META-INF\ jboss-service.xml,88行左右有:


<attribute name="SnapshotMode">instant</attribute>

这就是刚才提到的复制模式,instant为立即复制,如果设为interval 那么系统会在延迟一段时间再进行复制,时间长度在< attribute name="SnapshotInterval">2000</attribute>中指定,单位是毫秒。

单独配置这一个地方还不够,在%jboss% server\all\deploy\ tc5-cluster-service.xml中有:


<attribute name="CacheMode">REPL_ASYNC</attribute>

这里才真正决定复制是同步的还是异步的,可以指定为REPL_ASYNC(异步)或者REPL_SYNC(同步)。

在这个文件下面一点,还有一个config标签,里面指定了各个节点在进行session复制的时候如何通信,有udp和tcp两种可选,如果使用udp方式,那么应该将udp的lookback属性指定为true,因为windows上有一个叫做media sense的东西会影响 udp multicast。注意如果你不了解multi address的ip规则,请不要随便修改mcast_addr的值。如果采用tcp方式的话,应该指定bind_addr的值为本机ip,并且在TCPPING标签的initial_hosts属性中列出所有节点,格式是”机器名[端口号]”,比如在我们的例子中,就应该这样配置tcp(以其中一个节点为例):


<config><TCP bind_addr="172.16.0.116" start_port="7810" loopback="true"/><TCPPING initial_hosts="172.16.0.116[7810],172.16.32.88[7810]" port_range="3" timeout="3500"num_initial_members="3" up_thread="true" down_thread="true"/><MERGE2 min_interval="5000" max_interval="10000"/><FD shun="true" timeout="2500" max_tries="5" up_thread="true" down_thread="true" /><VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />   <pbcast.NAKACK down_thread="true" up_thread="true" gc_lag="100"       retransmit_timeout="3000"/>   <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />   <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="false"print_local_addr="true" down_thread="true" up_thread="true"/><pbcast.STATE_TRANSFER up_thread="true" down_thread="true"/></config>

JBoss的clustering版主建议尽量使用udp。不过在Sobey内部,建议使用tcp方式,经测试可能有不明物体在影响udp通信,导致Timeout异常。

在%jboss%\ server\all\deploy\ cluster-service.xml中也有关于udp和tcp的配置信息,在4.0以前版本的jboss中,会以这个文件为主配置,4.0以后都以tc5-cluster-service.xml为主配置。

Jboss的配置完成了,最后需要在web应用中增加配置信息,控制session复制的粒度。在WEB-INF\ jboss-web.xml中增加以下内容:


<replication-config>   <replication-trigger>SET_AND_NON_PRIMITIVE_GET</replication-trigger>   <replication-granularity>SESSION</replication-granularity> </replication-config>

其中replication-trigger是指定哪些操作引发session的版本更新,它的取值有:


SET_AND_GET     SET_AND_NON_PRIMITIVE_GETSET

replication-granularity是复制粒度,可以取session或attribute。如果取为attribute有可能导致复制失败,这是目前版本的jboss cache的一个bug,等待修正。

部署项目,测试,如果配置没有问题,可以在%jboss%\0server\all\log\server.log中发现类似于这样的信息:


DEBUG [org.jboss.web.tomcat.tc5.session.JBossCacheManager] check to see if needs to store and replicate session with id Im9-qpuaXppMS+xXwE3M+Q**.server1 DEBUG [org.jboss.web.tomcat.tc5.session.ClusteredSession] processSessionRepl(): session is dirty. Will increment version from: 20 and replicate.

Session replication配置的成功率比较低,情况也很复杂,请仔细操作。
posted @ 2006-07-12 14:53 edsonjava 阅读(448) | 评论 (0)编辑 收藏
 
     摘要: 在Oracle中存取BLOB对象实现文件的上传和下载 最近做一个 J2EE 项目,需要在 JSP 页面实现对文...  阅读全文
posted @ 2006-07-03 17:59 edsonjava 阅读(204) | 评论 (0)编辑 收藏
 
     摘要: 在Oracle中存取BLOB对象实现文件的上传和下载 最近做一个 J2EE 项目,需要在 JSP 页面实现对文...  阅读全文
posted @ 2006-07-03 17:59 edsonjava 阅读(1418) | 评论 (0)编辑 收藏
 
DB2的DRIVER分為四種
以下文章有介紹
http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/0402chenjunwei/0402chenjunwei.html

不建議使用db2java.zip裡的COM.ibm.db2.jdbc.app.DB2Driver這個驅動
原因在於使用此驅動時,必須要先行安裝DB2 Administrator Client,然後才有能力去Catalog DB進來,所以連結的URL就如下
jdbc:db2:資料庫名稱

建議使用db2jcc.jar裡的com.ibm.db2.jcc.DB2Driver這個驅動
不需要另外安裝DB2 Client即可進行資料庫連線,連結URL如下
jdbc:db2://DB2主機HOST:50000/資料庫名稱

實際測試上也證實db2jcc.jar的驅動比較OK
但是使用db2jcc.jar有一點必須十分注意
就是class path裡必須要多一個JAR
這個JAR的檔名為db2jcc_license_cu.jar
主要是用於辯識DB2的授權,沒有它也是無法驅動的

說到這裡...似乎還沒有提到DB2的DRIVER要去那裡下載
其實它不用下載,就在安裝DB2的INSTANCE底下
例如: DB2安裝在C:\IBM\SQLLIB
這個目錄下有個名為java的目錄
相信它有您需要的所有JAR檔

舉UNIX的例子來說
/home/db2inst1/sqllib/java/下就是放這些JAR檔的路徑
posted @ 2006-06-23 09:26 edsonjava 阅读(436) | 评论 (0)编辑 收藏
 

jscript.encode这个是一个流传已经很久的加密方式,下面的html页面,不解密的话你可以看懂么?

提供一个在线解密加密的页面:

http://www.aspxuexi.com/forfun/functions/jsencode.htm

如下就是原文件了。如果在线无法进行加密解密的话,可以将下面的html文件另存为html文件后执行。

参考javascript加密和解密:http://www.aspxuexi.com/script/2006-5-20/javascript_encode.htm

<HTML><HEAD><TITLE>JScript.Encode在线加密解密|asp学习网提供</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=gb2312">
<script language="JScript.Encode">
#@~^khQAAA==@!eOR@#@&6Ex1YbGx,/^Dx^G9+`kSV*@#@&PnUm{xh~b^Ob\n(}8LmOcr?m.raYrxT Ax^KNDJ*i@#@&DY;D PnU1R2 ^W9+?1.rwDsbVn`E r_s~kS!BVQE1Dk2Or#I@#@&N@#@&@#@&@#@&\lMPUPb:2mZ}nem&1n`PP{PFZT@#@&7lMPjK)PA{]2z9SA1~x,F!q@#@&\CD,j:bPA{G2Z}fA~',FT @#@&-CMP?:)KA{jg3jZznAPxPqT2@#@&@#@&-lMP2r13{nU1W[k L,'~ +SPbMDmz`@#@&q~, S~Z~P8SPy~PZS~ BPZ~~!S~y~~!B~ BPqS,!~~+BPT~@#@&8~~Z~, ~,!B~FBPq~, S~Z~PZSPy~P8S~!BPy~~!S~Z~~ B@#@&8~~qBP!S~y~~!B~y~~Z~,F~,!B~FBPq~, S~Z~P8SPZ~PyS@#@&8~,!SP+S,!SP8SP8~~+BP!S~Z~~FB~8~~y~,!~,FB~!BP+@#@&#@#@&@#@&\m.PMlhGCOl,',xnh~)MDCXv@#@&Z6*B!6f{B!avOS,!al!B!6F2BT6yZSPZ6++B!6l)~Z6vlS~!Xcz~T6*XB!aGyS@#@&!a8~!afz~T6l$BPTX*A~!XGOS!Xv~,!aXG~!XX,B!6FXSPZ6lAS!a+F~T6W/~@#@&TaW ~TaFvS!X*l~~Z6+!~Z6+f~Z6{vBPTay&~Zavy~!X+)~,!XvX~TaWfS!X*&B@#@&TX*sSTX*q~Za2&S,!XG2B!XX&B!acy~~TXcsBT6l ~Za+!BPZ6X STX T~Zav2~@#@&Z6G)SZ6+vBTXc)BPZ6 8~Za*W~T6lbS~Z6c+S!XGFBTa&R~,!a TSZ6+ABT6F,S@#@&!6+B!av+SZ6fy~,!6+&BT6ybS!X*{S,!6y)~Z6*RST6+ZBPT6{B!aGwS!X $S@#@&!a*F~T6F$B!aWvBP!X lS!X&T~Z6X+BP!X+ZB!62qS!Xcw~~!a+O~T6+/~Z6f9B@#@&Ta+,S!X*O~TXGZ~PZ62o~Z6fsB!afw~PZa F~!X{0~Z6FASPTa2sS!XfsB!afw~@#@&TXv{~ZalsSZ6lF~,!XfsB!a&w~Ta2s~,T6+ ~Za+,B!XG)~~TXcq~Za W~TaF2~@#@&Z6XbBTX oB!X&ABPZav+~T62,STXcGB~!X& BTa&2~Z6*FS~Z6{&BT6+sSTXGGS@#@&!acGSZ6+8~Z6*+~,T6W&S!XGXSZ6*wSPZ6G8ST6y%B!a S,!a&OS!Xc+SZ6G0S@#@&T6F/B!aWvB!6+2B~!X*f~Z6*)B!6+*~,!6W0S!X*;~T6{*BPT62q~Z6*0B!6{B@#@&!X{y~TX&+~!XGGSPZ62B!a*~~!X%BP!X{T~Z6FfS!afl~~!X*,B!aXG~!a+y~@#@&Za2sSZ6+b~Z6lX~,!ac~~Tal!~Za&z~PZabB!Xv1~Ta+!SPZa A~Tay&~Ta+bS@#@&TXGoB!X!,B!X{FBPT6y%STXG!BT6+s~,Ta&l~Z6*STXc1~,T6FfSTXGcSTX*/~@#@&Z6+W~Z6 ;~Za*G~~!X 9SZ6GFS!X GB~T6lcB!ac*SZ6X,B~!X&{SZ6&oSZ6+*B@#@&!aFAB!6+fBT6FZSPZ6f9B!6F/~Z6 2S~!Xv;~T6*fB!avGSPZ6f*B!6f0B!a RS@#@&TXvG~!X*AS!X&q~,!a*A~!XXAB!621SPZ6yAS!aA~T6Fo~,!afZ~!aXF~T62B@#@&Z6+s~Z6W/~Z6XcBPTaFc~Za&W~!Xf*~,!Xv$~TaF S!X BPTaWZ~Tay*S!X*A~@#@&!X&&B!XXvB!a&Z~~TX*vBT6F&~ZaX2BPZ6fbSTXv0~ZaG2~~TXG%STX*X~ZaZ,S@#@&Z6*F~ZacF~T6WAS~Z6GFS!X& BTav8~,!a&$SZ6f*BT6ycS~Z6c*SZ6+2BTXc9B@#@&!6ysBT6+cS!Xv$S,!6l1~Z6cwST6WcBPT6*XB!a&~S!X qS,!6X/B!a GSZ6fF~@#@&!XvRS!Xcq~Z6XfBP!XfvB!6+qS!X*R~~!aXR~T6F)~Z6*0BP!a{O~T6y+B!ay2B@#@&Z6Z1~Z6!B!aXZ~PZaGl~!X$~Z6yfSPTa2%S!X*2B!a+O~PTal*S!XfG~TX&w@#@&*@#@&@#@&7l.PDDCUk0WMh+9P',Unh,bMDCXcb@#@&WWM~`7l.~b'!I~b@!fi,r3_b,YMlxk0K.:N,kYPx~ +h,)DMlXvb@#@&6WMPc\C.,kx&8IPb@!xqyvi~r3_bP6GMPc7lMPL{!p~L@!&IPN_Qb,YDmU/6WDsn[$NT]DCh9CDl,`bR&8#~M,&PQ~NTDP{~vkx{&8#PQPO~),k@#@&@#@&-CMPNbLkD/P{~U+SPzD.lzc*@#@&0K.Pv\C.,k'TI,k@! +I,kQ3#@#@& @#@&9robYd$rbE 1tlM/W9+bDcT#3kYPxPr@#@&NrobO/]JCEcmtC.;W[+zOv!b3kYP',k3+v@#@&N@#@&0G.,`\m.Pb'!p~r@!8!pPr_Qb,NrobO/]JTEcmtC.;W[+zOv!b3kYP',k3X @#@&[kTkOd]!6y8T,'P++@#@&9kTkO/,TX WT,xP+&@#@&@#@&0;U1YrW ~!xnkmmw+vm4CD*@#@& @#@&-CMP+k^la+/,x~J[[eeyJ@#@&7l.Pdmmwn[,'PEwM-U@!@*@$r@#@&@#@&b0Pvm4CDcm4lMZG[bYvT#,@*P8+#,DY;DU~1tCD@#@&k6Pcnkml2nkRrx9nXrWvm4lD*PexP FbPM+O;MxPdmmw+9 dE(/DDc+d^mwn/crx9+a66`m4CM#SP8b@#@&.Y!Dx,JQE@#@&8@#@&@#@&W; mYbGx,N+1G[+~lk+ccdDDrxTb@#@& @#@&7lD~-mV~',T@#@&-mV,_',`9robYd$kY.r oRk;4kYDvTSF*R1tCD/G9+)YvT#YP@!@!, #@#@&7lsP3x,`[bobY/]/D.k o /!4dOM`FBq#cmtm./W9+zYc!bD,@*@*PWb@#@&\Cs,_'~c9kLkDd]/OMk oRkE(dYM`q~8# ^4lD;GNbYvTbT,[,!a0b~@!@!~Fy@#@&7ls~3'Pcc9kLkDd]/OMk oRkE(dYM`+~8# ^4lD;GNbYvTbT,@*@*P+#~@!@!P0#@#@&\mV~Q{P`c[borYk,kY.bxTR/!4kODv SF*R^4mDZK[+zY`ZbDPLPZ6f#~@!@!P+ *@#@&7ls~3'Pc[borYk,kY.bxTR/!4kODv&SF*R^4mDZK[+zY`ZbDP@!@!,F#@#@&M+OEMUP7ls@#@&8@#@&@#@&0;x1ObWU,/DDNmvnx1W[k ojOMkxTb@#@& @#@&@#@&\mD,:CDVnMPxPr:@$?7E@#@&\l.~kY.k L&x[6,'PZ@#@&-lMPdmMk2O&xNaP{PO8@#@&\mD,EU2U^KNrxT(x9+a~{P!@#@&7l.P14mD~{P EV^@#@&-lMPnx1W[r oSUoDtP{~;xAx1W[kUL^+UoD4P{PT@#@&\l.~kYCY~{Pj:b:2{;rhe{&1Kj:@#@&-mDP!U2 mW9rUoUYMkUo~x,JE@#@&-lMP.nBPl..@#@&@#@&S4bVnv/DlY#@#@& @#@&dhbY^4,`/DCY#@#@&`@#@&1lk+~`jPzK3{;6nI{(HhjKb~=@#@&/1.bwO&x9+6,',nx1W[k ojOMkxT k N+X6W`slM3nDS~kY.k Lq Nna*@#@&rW,`dmMraY( N6Pe',RF*@#@& @#@&;UAxmK[k o?D.rxTP3'~+U^KNrxTjYMkULc/E8dDDrxTckY.bxTqx9+XSPkm.kaY(U9+6*@#@&kmDb2Oq N6~_x~sl.3.R^+ULDt@#@&dDlO+,x,?PzKA{IAbGJ2g@#@&8@#@&nsk+@#@&`@#@&/YMrUo&x9+aPx~kY.k Lq Nna{'!~_,!~),dDDr o&xN6@#@&E 2UmKNrUT?YMrxTP_{~nx1W9kUojOMkUocdE(/O.v/Y.r o(x9nX~~x1WNbxTjYMkUocVnUTYt*@#@&kYlDn~',!@#@&N@#@&8M+C3@#@&@#@&mCdP`jPzK3{"3zfJA1*P)@#@&UmKNrxTSnUTYt,xPxmK[rxT?DDrxL kE8/D.`km.raYqU[6SP+b@#@&; 2 mW9k LVxLY4Px~9+mK[+~l/*`x1W[kULd+UoD4#@#@&d^MkwO( Nn6,Q{Pc+P3PJ{'r VxLY4#@#@&kYlDnP{P?:)P2|fAZ6f3@#@&4.+mV@#@&@#@&^m/+~cUK)KAmG2/}fA#P=@#@&r0,`ZE 2U^KNk LVxoD4b@#@& @#@&dY.r o(x9n6,'~d1Dk2O&x[+X~3PEGpTbbz'{?a?@$ER^+ULDt@#@&;xAxmK[rxTq Nn6~x,!@#@&kOlD+~x,?K)PA{/rhe|qHhj:@#@&(DC3@#@&N@#@&m4CMP',nx1WNbUL?DDbxLRd;(/ODvdmMk2O&xNnaBPq#@#@&b0~vm4lD,'{~J@$JbPkYCOP',jKzK2|iH2UZzn3@#@&n^/n@#@&`@#@&kW~vmtC.cm4lM/KNnzYv!#,@!,T6wsb@#@& @#@&!x2 ^W9kxTjODbxTPQ'~jDDrxT 0MWh/4lD/G9+cYMC /WKDs+N]wb^3|+UmKNrUT$E 3x1WNbULq N6Yv*DY$^tm.R1tC.;WNn)D`T#Yb@#@&; 2 mW9k Lq Nn63_@#@&)@#@&s/@#@&P@#@&E 2 mGNrUT?ODbUo,_x~1tl.@#@&8~@#@&d1DraY&xN63Q@#@&EU2 mG[bxo^nxTYt R@#@&(DlV@#@&N@#@&@#@&1C/PjPzK2mig2jZzKA)@#@&E 2x1W9rxT?ODbxL~3'P!U+kmlanc+ mKNrxLjDDrxT /!4dOM`_Qd1DrwD( NnX~,F#*@#@&dmMk2Y&x[nX__p~E 2x1G[k o^+UoO4,Ox @#@&E 2U^KNkUL&x[+XQ3@#@&kYmY+,',jKzK3{G2/6G2@#@&8Dl3@#@&N@#@&8@#@&@#@&.n,'~xAP"+L3Xw`Ecx?^Db2Du#~/1DkaY* + mGNJS~ro:bE#@#@&h4rs+vlMD~'~.Rn6^`!x3U1WNrUT?ODbUT#b,E 2x1W9rxT?ODbxL~{PIL2XwR^nWY;W Yn6O~3P]+T36aRyq,_P]nT2awc.bo4DZKxY6D@#@&M+OEMx~; 2x1GNbxoUO.k o@#@&N@#@&@#@&z&O @*emwFAA==^#~@
</script>
<body style="font:14px;text-align:center">
<div style="text-align:left;width:666px;margin:auto">
<h1 style="color:#123;font-size:16px;text-align:center">JScript.Encode脚本在线加密和解密</h1>
使用本页面可以加密javascript函数,将script标记中的内容复制后进行加密(可多次加密),然后将标记修改为&lt;script language="JScript.Encode"&gt;即可。<br />
                    <BR>
                    使用方法:<BR>
                    &nbsp;&nbsp;&nbsp; <FONT color=#ff0000>加密</FONT>:源代码粘贴在下面的框内,注意:<FONT
color=#ff0000>请在加密前先备份您的网页</FONT><BR>
                    &nbsp;&nbsp;&nbsp; <FONT
color=#ff0000>解密方法</FONT>:将引号内的乱码贴入按解密即可</P>
              
                <form>
                <center>
                    <textarea style="" name="codeinput" rows="20" cols="65"></TEXTAREA>
                    <BR />                  
                    <input  type="button" onclick="this.form.codeinput.value=screncode(this.form.codeinput.value,'JS')" value="加密">
                    <input type="button" onclick="this.form.codeinput.value=strdec(this.form.codeinput.value)" value="解密" />
      </form>
                    <br />
                  </center>
                  <P >说明:加密时应只加密脚本部分,不加密脚本标记&lt;script
                    language="javascript"&gt;,并且加密后脚本标记应改为:<BR>
                    &lt;script language="JScript.Encode"&gt;</P>
                  <P>特别注意:本页面以及您加密后的jscript一般只可以在ie中进行。</P>
      <p><a href="http://www.aspxuexi.com">返回asp学习网首页</a></p>
              
</DIV>
</BODY></HTML>

posted @ 2006-06-13 14:48 edsonjava 阅读(1449) | 评论 (0)编辑 收藏
 
消除从MySQL数据库中读出中文的乱码

    分为两个目标: step1: 从MySQL客户端查看中文正常, step2: 从网页中查看中文正常;

  1: 在用SQL脚本创建数据库前,设置MySQL的默认编码方式为“gb2312”,
   此步可以在mysql-administrator-1.1.0-rc-win.msi中完成,或者是MySQL Server向导;
   这样设置完毕后再用SQL 脚本创建数据库, 注意如果SQL脚本中有设置字符集为latin1或utf-8的语句,一定要删除。
         已发现目前版本《在线书店》中的SQL脚本中带有设置默认字符集为latin1,一定要删除!

       创建数据库完毕后,用MySQL CC或MySQL Admin可检查所创建的数据库的字符集,如果正确,则用MySQL CC或MySQL Admin都可以正常查看中文;

  2:  一般情况下,到这一步,在网页中已经基本能正常显示中文了,但凡事总有例外,可以在数据库连接串处再特别说明一下:

 String url ="jdbc:mysql://localhost/BookStore?useUnicode=true&characterEncoding=gb2312";
       conn=DriverManager.getConnection(url,"root","");

   一般情况下用 String url ="jdbc:mysql://localhost/BookStore"应该就能正常显示中文。

   总结:修改MySQL Server数据库默认字符集为“GB2312”,从MySQL读出中文可正常显示;

posted @ 2006-06-07 00:10 edsonjava 阅读(998) | 评论 (0)编辑 收藏
 

MySQL 4.1 对多语言的支持有了很大变化 (这导致了问题的出现);
尽管大部分的地方 (包括个人使用和主机提供商),MySQL 3 仍然占主导地位;但 MySQL 4.1 是 MySQL 官方推荐的数据库,已经有主机提供商开始提供并将会越来越多;
许多 PHP 程序以 MySQL 作为默认的数据库管理软件,但它们一般不区分 MySQL 4.1 与 4.1 以下版本的区别,笼统地称“MySQL 3.xx.xx 以上版本”就满足安装需求了;
因为 latin1 在许多地方 (下边会详细描述具体是哪些地方) 作为默认的字符集,成功的蒙蔽了许多 PHP 程序的开发者和用户,掩盖了在中文等语言环境下会出现的问题;
简单的说,MySQL 自身的变化和使用 MySQL 的 PHP 程序对此忽略,导致了问题的出现和复杂化,而由于大部分用户使用的是英文,使这种问题不被重视。这里提到的 PHP 程序,主要就 WordPress 而言。

[separator]



MySQL 4.1 字符集支持的原理MySQL 4.1 对于字符集的指定可以细化到一台机器上安装的 MySQL,其中的一个数据库,其中的一张表,其中的一栏,应该用什么字符集。但是,传统的 Web 程序在创建数据库和数据表时并没有使用那么复杂的配置,它们用的是默认的配置,那么,默认的配置从何而来呢?

编译 MySQL 时,指定了一个默认的字符集,这个字符集是 latin1;
安装 MySQL 时,可以在配置文件 (my.ini) 中指定一个默认的的字符集,如果没指定,这个值继承自编译时指定的;
启动 mysqld 时,可以在命令行参数中指定一个默认的的字符集,如果没指定,这个值继承自配置文件中的;
此时 character_set_server 被设定为这个默认的字符集;
当创建一个新的数据库时,除非明确指定,这个数据库的字符集被缺省设定为 character_set_server;
当选定了一个数据库时,character_set_database 被设定为这个数据库默认的字符集;
在这个数据库里创建一张表时,表默认的字符集被设定为 character_set_database,也就是这个数据库默认的字符集;
当在表内设置一栏时,除非明确指定,否则此栏缺省的字符集就是表默认的字符集;
这个字符集就是数据库中实际存储数据采用的字符集,mysqldump 出来的内容就是这个字符集下的;
简单的总结一下,如果什么地方都不修改,那么所有的数据库的所有表的所有栏位的都用 latin1 存储,不过我们如果安装 MySQL,一般都会选择多语言支持,也就是说,安装程序会自动在配置文件中把 default_character_set 设置为 UTF-8,这保证了缺省情况下,所有的数据库的所有表的所有栏位的都用 UTF-8 存储。

当一个 PHP 程序与 MySQL 建立连接后,这个程序发送给 MySQL 的数据采用的是什么字符集?MySQL 无从得知 (它最多只能猜测),所以 MySQL 4.1 要求客户端必须指定这个字符集,也就是 character_set_client,MySQL 的怪异之处在于,得到的这个字符集并不立即转换为存储在数据库中的那个字符集,而是先转换为 character_set_connection 变量指定的一个字符集;这个 connection 层究竟有什么用我不大明白,但转换为 character_set_connection 的这个字符集之后,还要转换为数据库默认的字符集,也就是说要经过两次转换;当这个数据被输出时,又要由数据库默认的字符集转换为 character_set_results 指定的字符集。

一个典型的环境典型的环境以我自己的电脑上安装的 MySQL 4.1 为例,我自己的电脑上安装着 Apache 2,PHP 5 和 WordPress 1.5.1.3,MySQL 配置文件中指定了 default_character_set 为 utf8。于是问题出现了:

WordPress 按照默认情况安装,所以所有的表都用 UTF-8 存储数据;
WordPress 默认采用的浏览字符集是 UTF-8 (Options->Reading 中设置),因此所有 WP 页面的 meta 中会说明 charset 是 utf-8;
所以浏览器会以 utf-8 方式显示所有的 WP 页面;这样一来 Write 的所有 Post,和 Comment 都会以 UTF-8 格式从浏览器发送给 Apache,再由 Apache 交给 PHP;
所以 WP 从所有的表单中得到的数据都是 utf-8 编码的;WP 不加转换的直接把这些数据发送给 MySQL;
MySQL 默认设置的 character_set_client 和 character_set_connection 都是 latin1,此时怪异的事情发生了,实际上是 utf-8 格式的数据,被“当作 latin1”转换成……居然还是转换成 latin1,然后再由这个 latin1 转换成 utf-8,这么两次转换,有一部分 utf-8 的字符就丢失了,变成 ??,最后输出的时候 character_set_results 默认是 latin1,也就输出为奇怪的东西了。
最神奇的还不是这个,如果 WordPress 中设置以 GB2312 格式阅读,那么 WP 发送给 MySQL 的 GB2312 编码的数据,被“当作 latin1”转换后,存进数据库的是一种奇怪的格式 (真的是奇怪的格式,mysqldump 出来就能发现,无论当作 utf-8 还是当作 gb2312 来读都是乱码),但如果这种格式以 latin1 输出出来,居然又能变回 GB2312!

这会导致什么现象呢?WP 如果使用 MySQL 4.1 数据库,把编码改用 GB2312 就正常了,可惜,这种正常只是貌似正常。

如何解决问题如果你已经不耐烦了 (几乎是肯定的),google 一下,会发现绝大部分的解答是,query 之前先执行一下:SET NAMES 'utf8',没错,这是解决方案,但本文的目的是说明,这为什么是解决方案。

要保证结果正确,必须保证数据表采用的格式是正确的,也就是说,至少能够存放所有的汉字,那么我们只有两种选择,gbk 或者 utf-8,下面讨论 utf-8 的情况。

因为配置文件设置的 default_character_set 是 utf8,数据表默认采用的就是 utf-8 建立的。这也应该是所有采用 MySQL 4.1 的主机提供商应该采用的配置。所以我们要保证的只是客户端与 MySQL 交互之间指定编码的正确。

这只有两种可能,客户端以 gb2312 格式发送数据,或者以 utf-8 格式发送数据。

如果以 gb2312 格式发送:

SET character_set_client='gb2312'
SET character_set_connection='utf8' 或者
SET character_set_connection='gb2312'

都是可以的,都能够保证数据在编码转换中不出现丢失,也就是保证存储入数据库的是正确的内容。

怎么保证取出的是正确的内容呢?考虑到绝大部分客户端 (包括 WP),发送数据的编码也就是它所希望收到数据的编码,所以:

SET character_set_results='gb2312'

可以保证取出给浏览器显示的格式就是 gb2312。

如果是第二种情况,客户端以 utf-8 格式发送 (WP 的默认情况),可以采用下述配置:

SET character_set_client='utf8'
SET character_set_connection='utf8'
SET character_set_results='utf8'

这个配置就等价于 SET NAMES 'utf8'。

WP 应该作什么修改还是那句话,客户端要发给数据库什么编码的数据,数据库是不可能确切知道的,只能让客户端自己说明白,所以,WP 是必须发送正确的 SET... 给 MySQL 的。怎么发送最合适呢?台湾的 pLog 同仁给出了一些建议:

首先,测试服务器是否 >= 4.1,编译时是否加入了 UTF-8 支持;是则继续
然后测试数据库以什么格式存储 ($dbEncoding);
SET NAMES $dbEncoding
对于第二点,WP 的情况是不同的,按照上面的典型配置,只要用 WP,肯定数据库是用 UTF-8 存储的,所以要根据用户设置的以 GB2312 还是 UTF-8 浏览来判断 (bloginfo('charset')),但这个值是要连接数据库以后才能得到的,所以效率最高的方式是连接数据库之后,根据这个配置设置一次 SET NAMES,而不必每次查询之前都设置一遍。

我的修改方式是这样的,在 wp_includes/wp-db.php 中增加:

function set_charset($charset)
{
// check mysql version first.
$serverVersion = mysql_get_server_info($this->dbh);
$version = explode('.', $serverVersion);
if ($version[0] < 4) return;

// check if utf8 support was compiled in
$result = mysql_query("SHOW CHARACTER SET like 'utf8'",
$this->dbh);
if (mysql_num_rows($result) < = 0) return;

if ($charset == 'utf-8' || $charset == 'UTF-8')
$charset = 'utf8';;
@mysql_query("SET NAMES '$charset'", $this->dbh);
}

在 wp-settings.php 的 require (ABSPATH . WPINC . '/vars.php'); 后增加:

$wpdb->set_charset(get_bloginfo('charset'));


1. MySQL 4.1 在文字上有很大改进,它有了 Character Set 与 Collation 的慨念。

2. 在 MySQL 4.0 ,一般的程式都会将文字以拉丁文 ( latin) 来储存,就算我们输入中文字,结果仍是放在以拉丁文设置的文字栏里头,这对 MySQL 4.0 与以 MySQL 4.0 为基楚的程式来说,并不会有问题。

3. 可是 MySQL 4.1 的系统编码是预设用 UTF-8 的,当要 restore MySQL 4.0 的 backup 档到 MySQL 4.1 时,乱码就出现了。原因在于 MySQL 4.1 将 latin 码转换过来,而后转换是并不完全完美的,这导致了出现少量文字出现乱码现象。

4. 要解决这乱码问题并不难。首先,在 MySQL 4.0 备份时,先将所有文字栏变成 binary 类型,然后进行正常备份。第二步,可在 MySQL 4.1 里将刚才的备份 restore。最后,将较早前所变更到 binay 类型的文字栏,再次复原到文字类型。这样中文编码的问题就应该可以完全解决。

5. 将文字栏变更到 binay 类型时,必需设定 binary 栏的长度大过或等于 (>=) 文字栏的长度,否则资料会失去。

6. 另外,经这样升级的 MySQL 数据库,在 MySQL 4.1 里将会正常工作,就算是怎样 backup 与 restore 都不会再有乱码问题。
作者: MySQL 发布日期: 2005-12-14
mysql4.1是比较烦人,支持多语言的细化设置,再加上phpmyadmin2.6也比较笨,默认就是改不动的utf8,怎么弄都乱码。
好了,废话少说,我们来一步步解决这个问题:
1.修改/etc/my.cnf文件,改成这样:
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
default-character-set=utf8

[mysql.server]
user=mysql
basedir=/var/lib

[mysqld_safe]
err-log=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

注意:就是加入了一句default-character-set=utf8。

2./etc/init.d/mysqld restart 重新启动mysql;
3.打开phpmyadmin,选择lang为"Chines simplifies(zh-utf-8)",选择"MySQL 连接校对"为"utf8_general_ci "点“显示 MySQL 的运行信息”--“变量”,可以看到:
character set client utf8 utf8
character set connection utf8 utf8
character set database utf8 utf8
character set results utf8 utf8
character set server utf8 utf8
character set system utf8 utf8
collation connection utf8_general_ci utf8_general_ci
collation database utf8_general_ci utf8_general_ci
collation server utf8_general_ci utf8_general_ci
从这里可以看到character全部变成utf8了。
有人要问,为什么都要改成utf8呢?改成GB2312不行吗?
解释如下:
我也不想改成utf8,只是phpmyadmin2.6在mysql4.1的时候只会用utf8,连其他页面的charset也都是utf8,改成gb2312一定会乱码,我们只能凑phpmyadmin了。
只有在mysql3.23的时候,phpmyadmin才会多一个gb2312的页面charset,这时候是正常的。
3.将以前的mysql3的库文件导入mysql4.1的库
有两种情况:
一是从phpmyadmin上导入,这时候你要注意的是在选择库文件的页面左下脚有个“文件的字符集:”,默认是utf8,要改成gb2312,否则导进去乱码;
二是在linux下导入,这时候你需要先在库文件的头部加一行:
SET NAMES 'gb2312';; 注意最后也是;号,别漏了。
然后执行mysql -u用户名 -p密码 xxx.sql > 库名
导入完成以后再用phpmyadmin打开看,里面的中文字就是正确的。
4.从mysql4.1里导出库文件
一.用phpmyadmin导出
导出倒是问题不大,如果phpmyadmin的浏览页面里显示的中文是正常的,那么导出肯定也是正常的
二.在linux上导出
如果用mysqldump导出出现了乱码也没有关系,可以运行iconv来转换一下
iconv -c -f UTF-8 -t GB2312 库文件名 > 新的gb2312的库文件名

综上所述,你要注意:
1。尽量在需要导入的库文件的开头加入SET NAMES 'gb2312';;告诉mysql你要导入的是一个gb2312的文件;
2。可能你需要这个:
SET NAMES 'utf8';;
在登陆到mysql后用,把character的一些默认参数改到utf8上,有时可以减少一些困扰,不过也不是必须的。
在mysql上使用:
SHOW VARIABLES LIKE 'character_set_%';;
用来查看当前的状态。
3.如果出现乱码也不要怕,一是你要注意留存原有的备份,二是用iconv来进行转化。
在正常使用之前注意做导入导出的测试,确保万无一失。

最后加一句:www.quicklinux.org原创文章,转载请注明出处。呵呵
邮件:support@quicklinux.org
作者: MySQL 发布日期: 2005-12-14
我升级了MYSQL到4.1.2,phpmyadmin用的是2.6.2。数据表里面有中文的字段中文都变成了乱码,导出数据也是乱码。我用以前的2.5.7没有问题,想问一下,应该在phpmyadmin的那个文件里改哪个设置一下才能显示出来的是正常的中文字?

和字符相关的变量中这几个和sql很有关系:
character_set_client
character_set_connection
character_set_results
此外就是数据库中对相应字段设置的charact set,如果没有对字段设置,缺省是table的charact set,table也没有指定则缺省使用database的。
上面3个变量的作用是这样的,client表示客户端发送过来的字符集,results表示发送到客户端的字符集(这两个分开是因为发送过来和发送过去的不一定是同一个客户端),connection则在客户端和数据库起一个连接作用。
具体是这样:比如我在mysql命令行设置client为gbk,connection为utf8,results为gbk,数据库为big5,
当我发送一个insert语句的时候,这个语句作为gbk代码,先转为utf8代码(connection),再转为big5(database)插入数据库。
而运行一个select语句的时候,从数据库得到的结果则相反的过程,由big5转为utf8,再转为gbk,你得到gbk的结果。
因此最主要的是让client和results和你使用的客户端一致。比如你的网页是utf8编码,你就要设置这两个为utf8。
而在mysql命令行的时候,我用的是2000,需要设置为gbk
而我们用的set names XXX,实际上就是同时设置这3个变量为XXX。
在这样的情况下,我们可以把一个数据库中的不同表或不同字段设为不同的字符集,只要上面3个设置正确,就可以在数据库中同时使用不同的字符集。
注意要保证你的数据库中的字符已经使用了正确的字符集,比如如果一开始你设置错误,插入数据后,本身数据的编码就是不正确的,然后即使设置改回来,也不可能得到正确的显示了。
还有一个是编码互相之间的兼容性,如果一个字符在gbk中有,在utf8中没有,那么在gbk-》utf8-》gbk的过程中,它就变成了“?”
再说一下具体解决的办法。
首先要指定你的升级后的database及table及field的character set,一般来说我们用gb2312或者utf8的,如果不同时使用多种编码,只要指定database就可以,可以在建库的sql语句加上相应的character set,在phpMyAdmin里也可以修改。
然后是导入旧数据。首先要确定自己的数据文件的编码。如果用phpMyAdmin导入,在界面上有文件编码的选项,一定要和数据文件的编码一致。
如果从mysql的命令行导入,就要自己设置上面说到的3个变量,set names xxx。
使用其它的客户端程序一样要注意。
这样就可以让旧数据转入新数据库后的编码才是正确的,如果这一步错了,后面不可能得到正确的显示。
然后是自己的程序,在连接后就可以执行一次set names xxx,根据你的网页编码而定。
这样基本就可以保证编码正确了。
你很有可能是导入的数据编码已经不对了
posted @ 2006-06-06 23:38 edsonjava 阅读(149) | 评论 (0)编辑 收藏
 
前言   有同事已经通过艰苦的工作完成了长达好几百页的关于Linux的文档。事实上,在网上还有超过好几万页的各种关于Linux的技术精华,还不包括英文的。有数万程序员和网络专家在为这个软件努力工作。这就是自由的魅力。   我使用Linux有不少时间了,期间也经历过无数的挫折,同时也得到过很多高手的帮助。我一向喜欢言简意赅的文字。通过下面的简单句子和代码,我希望能够表达出我使用Linux的体会。也许本文的结构比较凌乱,但我希望它的每一句话都是对你有用的。当然,你要知道一些更详细的东西,请参阅我同事写的文档。   当然,能够通过查阅这个文档,使你的工作顺利是我最大的心愿。 一、关于Linux的误区   1、Linux干什么用?   首先,很少有人用Linux做桌面系统,因为Windows桌面系统已经足够好了。我们主要把Linux用作网络服务器的操作系统。如果你和你的应用符合下面的几条,那么你合适使用Linux:   *不愿意花钱在操作系统和网络应用上。   *用D版系统受良心谴责或害怕承担责任。   *想得到和花钱的系统差不多的性能。   *在用户面前显得水平较高。   *认识几个弄Linux的高手,或者知道在哪里能够找到问题答案。   2、每种Linux发行包差别很多吗?   如果你在上世纪80年代末到95年之前这段时间大量使用PC机的话,那对于Linux你应该很好理解:它就象一个有着32位抢先多任务核心的DOS,其区别在于外表看起来像是一种UNIX。Linux是好多人用智慧堆砌起来的,初学者经常问:“RedHat好还是Slackware好?”。这个人同时还面临上百种选择,这的确让人头痛。大部分Linux发行包实际上很简单,几乎所有的发行包都有相同的内核(也许是不同版本),外部命令和数据文件的差别也仅仅局限于在文件系统中的存放位置不同(这导致了某些兼容性问题,因为在某些地方找不到特定的文件嘛)。另外就是有些发行包为了显示自己的卓而不群,在发行包中增加了一些自己的实用程序。实际上动手修改内核的厂商不算太多,因为这样会内核官方版本的不断更新而手忙脚乱。当然有一些嵌入式系统开发商在Linux基础上做了自己的系统另说。   拥有众多的发行包是Linux的一个很讨厌的问题,在这一点上,我更喜欢FreeBSD,因为它只有一个官方发行版本,也运行得比较稳定。可给Linux做软件的程序员太多了,Linux上面的资料也更多。记住:当一个家伙跟你说用哪个发行版本无所谓的时候,那他就是专家。   3、Linux业余爱好者的东西吗?   这样的宣传给了很多人这样的印象。其实,Linux是一个严肃的操作系统,拥有一切操作系统共有的特性。它可以用在很多严肃的场合。   4、Linux性能比Windows更好吗?   这里的答案会让你失望,据我以往的实际测试,2.2版本的Linux核心的性能比windows2000要低。值得欣慰的是2.4版本的核心性能有大幅度提高基本上接近了Windows2000。但Windows2000复杂的图形界面占用了相当的系统资源。   5、Linux比Windows更复杂更专业吗?   从复杂度上说,Windows2000要比Linux复杂很多。在核心技术上,Windows也更先进。   6、Linux更可靠吗?   应该从两个角度评价系统的可靠性。(1)系统自身的可靠性,两者象差不多。人们普遍认为的Windwos不如Linux可靠的印象来源于Windows众多的良莠不齐的软件。当然,Linux比Windows结构简单,在一定程度上也可以提高可靠性。(2)从安全性角度来说,Linux远远高于Windows系统,道理很简单:树大招风嘛。另外,Linux的源代码开放机制使得漏洞发现和消灭得很快。而Windows相对就显得比较官僚了。这里补充一点,我用过的最可靠的系统是BSD类的OS,可靠性和安全性都可圈可点。   7、Linux真的不用花一分钱吗?   大部分的发行包需要花少量的钱购买。当然你也可以从网络上DOWN。其实这些发行包未必符合你的应用,一般都需要经过一些改动和定制才能运行好。当然,花的钱跟Windows没法比。 二、Linux安装注意事项   现在的大部分发行包都很容易安装,基本上都是图形向导式的,每个发行包都不太相同,也无法一一描述,如果你连安装都不明白,那就别用Linux了。这里仅仅说明几个注意的问题。   1、如果要和Windows混装,一定要先装Windows,后装Linux。   2、分区的时候,根据计划安装的软件灵活决定分区。一般分区有:/,/root,/usr,/var和swap分区,其中swap分区类型和其他不同需要专门选择一下,它的空间一般是物理内存的两倍即可。/root分区是紧急情况root用的,有64兆就行了。   3、当然,为了方便,你可以安装发行包给你做好的软件。但我安装Linux的习惯做法是开始就安装一个最小系统,然后安装所有的编译工具。安装编译工具的方法一般都通过你用的发行包的包管理工具实现,这样最方便。比如在TurboLinux中,运行/usr/sbin/Turbopkg就可以了。在管理工具中选择所有关于Developoment相关的选项或gcc相关选项,一般就可以了。   4、lilo一定要装。否则一旦出现分区问题都不好办。   5、如果和Windows混装的时候,装完了系统起不来了。可以用98启动盘启动,然后用FDISK /MBR回复主引导分区来保住Windows分区。   在安装了最小系统和全套的编译器后,我们应该得到一个比较干净的系统。在这个系统上,可以下载和安装各种应用软件了。 三、基本使用   1、文件系统   无论有多少个分区,整个文件系统都是一棵完整的树结构。经常用到的目录有:   (1)/usr:存放各种应用程序文件用。其中的/usr/local用来存放后安装的软件   (2)/var:用来存放所有的数据文件   (3)/proc:里面的文件用来表示系统内核的各种配置和状态。这一块不是真正的文件系统,而是内存里面的各种数据。可以从这里得到一些常用的系统信息。比如CPU内存什么的。   (4)/etc:这里是放置所有的系统配置文件。一般情况下,后安装的软件的配置文件不会放到这里。除非你是用RPM安装的随发行包配的软件或者故意这样做。我不喜欢把不同软件的文件混在一起,因此通常让不同的软件的配置文件放在各自的目录中。   2、常用的命令   这里我列出一些经常用的命令,具体的用法可以参见相关手册页(希望你的英文不象我这么差)。   vi(一定要会用这个你非常讨厌的东西,如果你屈服了,可以选择另一个叫做pico的编辑器,它有点象DOS的edit)   head(用来看很长的文件用)   tail(同上)   netstat(看网络状态)   tar(解开.tar.gz的压缩包)   ps(察看进程)   kill(干掉进程)   top(看系统状况)   shutdown(关闭系统)   cat(看文件内容)   ping (看网络连通状况)   ftp(传输文件)   man(手册)   上面这些命令都是最最常用的,一定要把基本用法记牢。事实上,每人能记住全部命令或一个命令的全部参数,记住几个常用的就行了。其他的查书。   3、内核升级   如果你不愿意冒险,可以跳过这个部分。不过根据我实际使用的经验,2.4.x版本的内核性能比2.2.x的性能至少提高40%,还是很值得一试的。现在最新的发行包几乎都带有新的2.4内核,但是版本号想对保守。这里,我的建议是在决定升级任何部分的时候,一定要首先看一下所谓的“currect version release note”信息,里面告诉你本次版本升级都有些什么改动。如果改动的内容不涉及你当前的环境或需求,只是增加了一些邪门设备的驱动,你大可不必急着升级。补充一句话:Linux的内核版本号的中间一位是奇数的是非稳定版本,比如2.3,是偶数的才是稳定版本。   升级内核的步骤如下:   (1)首先在网上找到你打算升级的内核文件,一般名字是:linux-2.x.xx.tar.gz,把这个文件复制到/usr/src。(不知道在哪里找?看看:http://www.kernel.org ,最好找到.tar.gz格式的,比如这里:http://www.kernel.org/pub/linux/kernel/v2.x/)。下面的升级举例是从2.2.18升级到2.5.7(这是最新的内核了,你使用其它的版本别忘了改变版本号)   (2)解压缩:tar zxvf linux-2.5.7.tar.gz后生成一个目录:linux-2.5.7。   (3)进入/usr/src,用ls –l 命令可以看到src里面有一个连接,类似于:linux -> linux-2.2.18/ (连接指向你当前用的内核版本)。首先删除这个连接(rm linux),用ln -s linux-2.5.7 linux 命令重建连接。我想你一定不会连这里的版本号都照抄吧?   (4)进入linux-2.5.7 目录,如果不是第一次编译这个内核,最好用命令:make mrproper 删掉上次编译产生的.o文件等,当然以前保存的配置也丢了。   (5)用命令:make menuconfig命令调整内核配置以适应你目前的环境,记住,不懂的配置不要乱动。首要任务是调整各种硬件的环境,比如SCSI卡:SCSI support/SCSI low-level drivers,不知道现在的SCSI卡型号?在/proc/scsi中可以找到。还有网卡,在Network Device Support 里面。不知道现在的网卡型号?在这个文件里面写着呢:/proc/net/PRO_LAN_Adapters/eth0.info或/proc/pci文件中也可以找到 。在menuconfig中用空格键改变选项状态,前面的<*>表示该功能编译在内核中,主要是运行速度快。< >表示不需要该功能。表示把该功能编译成模块,通常为了减小尺寸,和更换方便而把某些驱动编译成模块。   (6)下面的事情比较程序化,照着做:make dep (检查文件的完整性,过程很复杂)   (7)make bzImage 注意大小写(真正开始编译了耶!我觉着这个最过瘾了,屏幕“哗啦哗啦”的,这时候你会感觉到,多少不知名的程序员在这个复杂的系统中贡献了自己的一份力量)   (8)make modules (编译那些被标记为的功能模块或驱动程序)   (9)make modules_install(把编译好的模块复制到指定地点,一般是:/lib/modules/ 。注意:不同版本号的模块是完全分开在不同目录中的,因为modules跟内核是紧密相关的,混用容易导致系统崩溃)   (10)cd /usr/src/linux-2.5.7/arch/i386/boot ,用命令:cp /usr/src/linux2.5.7/arch/i386/boot/bzImage /boot/vmlinuz-2.5.7 把文件bzImage文件改名成vmlinuz-2.5.7复制到/boot/   (11)cp /usr/src/linux-2.5.7/System.map /boot/System.map-2.5.7   (12)进入/boot目录,rm System.map   (13)在/boot目录中运行ln System.map-2.5.7 System.map 10-13两个步骤每次重新编译都要做。   (14)我开始提醒过的,但愿你安装了lilo (否则你就高唱:“神啊,救救我吧!”),编辑文件:/etc/lilo.conf ,做下面的编辑:   boot=/dev/sda   map=/boot/map   install=/boot/boot.b   prompt   timeout=50   lba32   default=linux-2.5.7   image=/boot/vmlinuz label=linux initrd=/boot/initrd read-only root=/dev/sda5   image=/boot/vmlinuz-2.5.7 label=linux-2.5.7 initrd=/boot/initrd read-only root=/dev/sda5   黑体字部分是后加的和修改的。万一你没有安装lilo,你只能修改/boot下面的连接指向你新的System.map和vmlinuz , 而不用修改lilo了。这样也能启动,但是一旦出现问题,你的机器就启动不了了。   (15)执行命令:lilo (用来更新lilo数据),注意输出的结果:带星号的是缺省的。   (16)祈祷,然后reboot 系统重新启动后,用uname –a 可看到新的内核起作用了。如果出现任何问题不能引导了,你需要在lilo的引导界面选择原来的内核启动,进入系统后重新改变内核参数,调整硬件或其他配置。然后重复前面所有过程。 【发表回复】【查看论坛原帖】【添加到收藏夹】【关闭】 -------------------------------------------------------------------------------- lsw 回复于:2002-12-23 11:39:14 四、主要的软件安装  前面说过,我的习惯是,装系统的时候就安装一个内核和编译系统,得到了一个干净的系统后,再安装其他的软件就比较清晰。另一点,除非搞不到源代码版本,一般情况下我都会下载官方版本的源代码文件,来重新编译之。同样的,选择软件版本要根据该软件的Release Note来决定,并非越新越好。这就是为什么很多软件都是几个版本齐头并进的发展之原因。这些软件不同版本之间的安装方法会有少许不同,安装前一定要仔细阅读目录中的README文件和INSTALL文件,否则这些少许不同会造成不少得麻烦。下面描述的安装过程都核版本号紧密相关。另外,所有的软件都把程序文件的包拷贝到/usr/local/src目录中再解压缩,这是习惯。因为在Linux中习惯于把后来的软件装在/usr/local中。   (1)安装DNS服务器   1、从www.isc.org下载bind 域名服务器软件。我们这里用的是bind8.3.0,bind8分支和bind9分支是并行发展的。根据自己的实际情况选择合适的bind版本。   2、mkdir /usr/src/bind83   3、下载的文件叫bind-src.tar.gz,复制到/usr/local/src/bind83目录下。   4、cd /usr/local/src/bind83   5、tar zxvf bind-src.tar.gz   6、cd src   7、make stdlinks   8、make clean   9、make depend   10、make all   11、make install   12、编辑配置文件:/etc/named.conf内容如下,需要更改的部分用黑体标出:   /*    * This is a worthless, nonrunnable example of a named.conf file that has    * every conceivable syntax element in use. We use it to test the parser.    * It could also be used as a conceptual template for users of new features.    */   /*    * C-style comments are OK    */   // So are C++-style comments   # So are shell-style comments   // watch out for ";" -- it‘s important!   options { directory "/var/named";       // use current directory named-xfer "/usr/libexec/named-xfer";       // _PATH_XFER dump-file "named_dump.db";       // _PATH_DUMPFILE pid-file "/var/run/named.pid";       // _PATH_PIDFILE statistics-file "named.stats";       // _PATH_STATS memstatistics-file "named.memstats";       // _PATH_MEMSTATS check-names master fail; check-names slave warn; check-names response ignore; host-statistics no; deallocate-on-exit no;       // Painstakingly deallocate all       // objects when exiting instead of       // letting the OS clean up for us.       // Useful a memory leak is suspected. // Final statistics are written to the // memstatistics-file. datasize default; stacksize default; coresize default; files unlimited; recursion yes; fetch-glue yes; fake-iquery no; notify yes;      // send NOTIFY messages. You can set // notify on a zone-by-zone // basis in the "zone" statement // see (below) serial-queries 4;      // number of parallel SOA queries // we can have outstanding for master // zone change testing purposes auth-nxdomain yes; // always set AA on NXDOMAIN. // don‘t set this to ‘no‘ unless // you know what you‘re doing -- older // servers won‘t like it. multiple-cnames no;    // if yes, then a name my have more // than one CNAME RR. This use // is non-standard and is not // recommended, but it is available // because previous releases supported // it and it was used by large sites // for load balancing. allow-query { any; }; allow-transfer { any; }; transfers-in 10;      // DEFAULT_XFERS_RUNNING, cannot be    // set > than MAX_XFERS_RUNNING (20) transfers-per-ns 2; // DEFAULT_XFERS_PER_NS transfers-out 0; // not implemented max-transfer-time-in 120; // MAX_XFER_TIME; the default number // of minutes an inbound zone transfer // may run. May be set on a per-zone // basis. transfer-format one-answer; query-source address * port *;   /* * The "forward" option is only meaningful if you‘ve defined * forwarders. "first" gives the normal BIND * forwarding behavior, i.e. ask the forwarders first, and if that * doesn‘t work then do the full lookup. You can also say * "forward only;" which is what used to be specified with * "slave" or "options forward-only". "only" will never attempt    * a full lookup; only the forwarders will be used.    */ forward first; forwarders { };     // default is no forwarders topology { localhost; localnets; };     // prefer local nameservers listen-on port 53 { any; };     // listen for queries on port 53 on    // any interface on the system    // (i.e. all interfaces). The    // "port 53" is optional; if you    // don‘t specify a port, port 53    // is assumed. /* * Interval Timers */ cleaning-interval 60;     // clean the cache of expired RRs    // every ‘cleaning-interval‘ minutes interface-interval 60;     // scan for new or deleted interfaces    // every ‘interface-interval‘ minutes statistics-interval 60;     // log statistics every    // ‘statistics-interval‘ minutes maintain-ixfr-base no;     // If yes, keep transaction log file for IXFR max-ixfr-log-size 20;     // Not implemented, maximum size the    // IXFR transaction log file to grow   };   /*    * Control listeners, for "ndc". Every nameserver needs at least one.    */   controls { inet * port 52 allow { any; }; // a bad idea unix "/var/run/ndc" perm 0600 owner 0 group 0;        // the default   };   zone "rd.21vianet.com" in { type master;       // what used to be called "primary" file "rd.21vianet.com.db"; check-names fail; allow-update { none; }; allow-transfer { any; }; allow-query { any; }; // notify yes;       // send NOTIFY messages for this // zone? The global option is used // if "notify" is not specified // here. also-notify { };       // don‘t notify any nameservers other // than those on the NS list for this // zone   };   zone "223.99.211.in-addr.arpa" in { type master; // what used to be called "secondary" file "211.99.223.db"; };   zone "0.0.127.in-addr.arpa" in { type master; file "127.0.0.db";   };   zone "." in { type hint;        // used to be specified w/ "cache" file "named.root";   };   logging { /* * All log output goes to one or more "channels"; you can make as * many of them as you want. */ channel syslog_errors {       // this channel will send errors or syslog user;       // or worse to syslog (user facility) severity error; }; category parser { syslog_errors; // you can log to as many channels default_syslog;      // as you want }; category lame-servers { null; };      // don‘t log these at all channel moderate_debug { severity debug 3;      // level 3 debugging to file file "foo";      // foo print-time yes;     // timestamp log entries print-category yes;      // print category name print-severity yes;      // print severity level /* * Note that debugging must have been turned on either * on the command line or with a signal to get debugging * output (non-debugging output will still be written to * this channel). */ }; /* * If you don‘t want to see "zone XXXX loaded" messages but do * want to see any problems, you could do the following. */ channel no_info_messages { syslog; severity notice; }; category load { no_info_messages; }; /* * You can also define category "default"; it gets used when no * "category" statement has been given for a category. */ category default { default_syslog; moderate_debug; }; };   13、在/var/named/中生成/etc/named.conf中标记的文件:rd.21vianet.com.db,内容如下,需要修改和调整相应部分:   ;Authoriative data for rd.21vianet.com   ;   $TTL 3600   @ IN SOA compaq.rd.21vianet.com. tandongyu.rd.21vianet.com. (     20020101 ;Serial     3600 ;Refresh 1 hour     900 ;Retry 15 mins     604800 ;Expire 7 days     86400) ;Mini 24 hours   ;Name server NS records   @ IN NS compaq.rd.21vianet.com.   ;Mail Exchange (MX) records   rd.21vianet.com. IN MX 0 compaq   ;Address (A) records.   localhost IN A 127.0.0.1   compaq IN A 211.99.223.9   tls65 IN A 211.99.223.8   fbsd IN A 211.99.223.7   14、在/var/named/中生成/etc/named.conf中标记的文件:211.99.223.db,内容如下,你需要修改相应部分:   ;   ;   $TTL 3600   @ IN SOA compaq.rd.21vianet.com. tandongyu.rd.21vianet.com. (     20020101 ;Serial     3600 ;Refresh     900 ;Retry 15 mins     604800 ;Expire 7 days     86400) ;Mini 24 hours   ;NameServer (NS) records   @ IN NS compaq.rd.21vianet.com.   ;Address Point to Name (PTR) records   9 IN PTR compaq.rd.21vianet.com.   8 IN PTR tls65.rd.21vianet.com.   7 IN PTR fbsd.rd.21vianet.com.   15、在/var/named/中生成/etc/named.conf中标记的文件:127.0.0.db,内容如下,你需要修改相应部分:   ; 0.0.127.in-addr.arpa   $TTL 3600   @ IN SOA compaq.rd.21vianet.com. tandongyu.rd.21vianet.com. (      20020101;serial 3600 ;refresh 1800 ;retry 604800 ;expiration 3600 ) ;minimum IN NS compaq.rd.21vianet.com.   1 IN PTR localhost.   16、在/var/named/中生成/etc/named.conf中标记的文件:named.root,内容大致如下。该文件标记了14个域名服务器。可以从ftp.rs.internic.net获得该文件的最新样本:named.hosts,然后改名成你需要的名字,比如:named.root   ; This file holds the information on root name servers needed to   ; initialize cache of Internet domain name servers   ; (e.g. reference this file in the "cache . "   ; configuration file of BIND domain name servers).   ;   ; This file is made available by InterNIC registration services   ; under anonymous FTP as   ; file /domain/named.root   ; on server FTP.RS.INTERNIC.NET   ; -OR- under Gopher at RS.INTERNIC.NET   ; under menu InterNIC Registration Services (NSI)   ; submenu InterNIC Registration Archives   ; file named.root   ;   ; last update: Aug 22, 1997   ; related version of root zone: 1997082200   ;   ;   ; formerly NS.INTERNIC.NET   ;     . 3600000 IN NS A.ROOT-SERVERS.NET.     A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4   ;   ; formerly NS1.ISI.EDU   ;     . 3600000 NS B.ROOT-SERVERS.NET.     B.ROOT-SERVERS.NET. 3600000 A 128.9.0.107   ;   ; formerly C.PSI.NET   ;     . 3600000 NS C.ROOT-SERVERS.NET.     C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12   ;   ; formerly TERP.UMD.EDU   ;     . 3600000 NS D.ROOT-SERVERS.NET.     D.ROOT-SERVERS.NET. 3600000 A 128.8.10.90   ;   ; formerly NS.NASA.GOV   ;     . 3600000 NS E.ROOT-SERVERS.NET.     E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10   ;   ; formerly NS.ISC.ORG   ;     . 3600000 NS F.ROOT-SERVERS.NET.     F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241   ;   ; formerly NS.NIC.DDN.MIL   ;     . 3600000 NS G.ROOT-SERVERS.NET.     G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4   ;   ; formerly AOS.ARL.ARMY.MIL   ;     . 3600000 NS H.ROOT-SERVERS.NET.     H.ROOT-SERVERS.NET. 3600000 A 128.63.2.53   ;   ; formerly NIC.NORDU.NET   ;     . 3600000 NS I.ROOT-SERVERS.NET.     I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17   ;   ; temporarily housed at NSI (InterNIC)   ;     . 3600000 NS J.ROOT-SERVERS.NET.     J.ROOT-SERVERS.NET. 3600000 A 198.41.0.10   ;   ; housed in LINX, operated by RIPE NCC   ;     . 3600000 NS K.ROOT-SERVERS.NET.     K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129   ;   ; temporarily housed at ISI (IANA)   ;     . 3600000 NS L.ROOT-SERVERS.NET.     L.ROOT-SERVERS.NET. 3600000 A 198.32.64.12   ;   ; housed in Japan, operated by WIDE   ;     . 3600000 NS M.ROOT-SERVERS.NET.     M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33   ; End of File   17、我们还需要配置/etc/resolv.conf、/etc/hosts、/etc/hosts.conf文件以适应新的状况。   18、一切都结束后,用/usr/sbin/ndc start命令启动bind,同样的可用stop、restart、reload等命令参数操作。   19、启动后用nslookup命令(有的系统推荐使用dig命令)检验是否正确。如果出现错误,该命令将不能启动。一般的错误都是数据库文件或配置文件笔误所至。比如少个“.”或者文件明不正确等等。   (2) 安装sendmail服务器   1、从www.sendmail.org下载最新的版本(这个snedmail倒是有必要升级为最新的版本,因为它的升级主要是安全漏洞问题)。这里说明的是用的sendmail-8.12.2.tar.gz   2、cd /usr/local/src/   3、把文件下载到:/usr/local/src中   4、tar zxvf sendmail-8.12.2.tar.gz   5、cd /usr/local/src/sendmail-8.12.2   6、chmod go-w / /etc /etc/mail /usr /var /var/spool /var/spool/mqueue   7、chown root / /etc /etc/mail /usr /var /var/spool /var/spool/mqueue   8、cd /usr/local/src/sendmail-8.12.2/sendmail   9、sh Build   10、cd /usr/local/src/sendmail-8.12.2/cf/cf   11、建立文件sendmail.mc内容如下,你可根据需要修改相应部分。   divert(-1)   dnl This is the macro config file used to generate the /etc/sendmail.cf   dnl file. If you modify thei file you will have to regenerate the   dnl /etc/sendmail.cf by running this macro config through the m4   dnl preprocessor:   dnl m4 /etc/sendmail.mc > /etc/sendmail.cf   dnl You will need to have the Sendmail-cf pacage installed for this to work.   include(`/usr/local/src/sendmail-8.12.2/cf‘) define(`confDEF_USER_ID‘,`8:12‘) OSTYPE(`linux‘) undefine(`UUCP_RELAY‘) undefine(`BITNET_RELAY‘) define(`confTO_CONNECT‘, `1m‘) define(`confTRY_NULL_MX_LIST‘,true) define(`confDONT_PROBE_INTERFACES‘,true) define(`PROCMAIL_MAILER_PATH‘,`/usr/bin/procmail‘) define(`SMART_HOST‘,compaq.rd.21vianet.com)     <---这条用于(非HUB)缺省使用HUB发送邮件 MASQUERADE_AS(`rd.21vianet.com‘) <------------------------- FEATURE(`masquerade_entire_domain‘)      <---这三条用于邮件地址伪装 FEATURE(`masquerade_envelope‘)       <------------------------- FEATURE(`smrsh‘,`/usr/sbin/smrsh‘) FEATURE(`mailertable‘,`hash -o /etc/mail/mailertable‘) FEATURE(`virtusertable‘,`hash -o /etc/mail/virtusertable‘) FEATURE(redirect) FEATURE(always_add_domain) FEATURE(use_cw_file) FEATURE(local_procmail) FEATURE(`access_db‘) FEATURE(`blacklist_recipients‘) FEATURE(`accept_unresolvable_domains‘) MAILER(smtp) MAILER(procmail) dnl We strongly recommend to comment this one out if you want to protect dnl yourself from spam. However, the laptop and users on computers that do dnl not hav 24x7 DNS do need this. dnl FEATURE(`relay_based_on_MX‘)   12、sh Build install-cf   13、groupadd smmsp   14、useradd smmsp   15、cd cd /usr/local/src/sendmail-8.12.2/sendmail   16、sh Build install   17、cd /usr/local/src/sendmail-8.12.2/makemap   18、sh Build clean   19、sh Build all   20、sh Build install   21、cd /usr/local/src/sendmail-8.12.2/   22、在本域DNS主数据库文件中增加MX纪录:   rd.21vianet.com. IN MX 0 compaq   注意修改相应部分。那个0是有几个邮件集中器的时候用于标记先后顺序的。当有好几个MX的时候,建议顺序写为10、20、30…   23、在/etc/mail目录下创建access文件,内容类似如下: 127.0.0.1 RELAY 211.99.223 RELAY 211.99.221.238 RELAY   然后:makemap hash access.db < access   24、创建文件/etc/mail/local-host-names,其内容为本机的拥有的域名信息。   rd.21vianet.com   compaq.rd.21vianet.com   25、创建文件/etc/mail/aliases,内容类似:   MAILER-DAEMON: postmaster postmaster: root bin: root daemon: root nobody: root   运行newaliases创建数据库。   创建别名文件的意义之一在于当邮件发往域中其他邮件服务器的用户而不是mail HUB用户的时候用。   比如增加一条:   atan: atan@fbsd   则导致邮件发往mail HUB的时候自动转发到atan@fbsd.rd.21vianet.com   26、启动sendmail: /usr/sbin/sendmail -bd -q30m   排错:如果有问题导致启动不了,大部分问题和DNS配置有关,可以使用nslookup检查DNS是否正常。挨个检查/etc/mail中的文件内容也是排错的好办法。另外,修改配置,不建议直接编辑sendmail.cf文件,建议使用m4宏编译工具,因为有些带有安全漏洞或过时的宏在编译的时候会有提示,这样以免造成相关安全问题。 -------------------------------------------------------------------------------- lsw 回复于:2002-12-23 11:40:34 (3)安装DHCP服务器  1、源程序下载地址是:ftp://ftp.isc.org/isc/dhcp/dhcp-3.0.tar.gz ,我们的版本是:dhcpd-3.0   2、复制到:/usr/local/scr后解压:tar zxvf dhcp-3.0.tar.gz   3、cd /usr/local/src/dhcp-3.0.tar.gz   4、./configure   5、make(如果不是第一次编译,先make clean)   6、make install   7、cp ./server/dhcpd.conf /etc   8、编辑这个文件,看起来类似下面这样:(要更改的地方用粗体标出)   # dhcpd.conf   #   # Sample configuration file for ISC dhcpd   #   # option definitions common to all supported networks...   option domain-name "rd.21vianet.com";   option domain-name-servers compaq.rd.21vianet.com;   default-lease-time 86400;   max-lease-time 172800;   ddns-update-style ad-hoc;   # If this DHCP server is the official DHCP server for the local   # network, the authoritative directive should be uncommented.   #authoritative;   # Use this to send dhcp log messages to a different log file (you also   # have to hack syslog.conf to complete the redirection).   log-facility local7;   # No service will be given on this subnet, but declaring it helps the   # DHCP server to understand the network topology.   # This is a very basic subnet declaration.   subnet 211.99.223.0 netmask 255.255.255.224 {     range 211.99.223.2 211.99.223.6;     option routers 211.99.223.1;   }   # This declaration allows BOOTP clients to get dynamic addresses,   # which we don‘t really recommend.   #subnet 10.254.239.32 netmask 255.255.255.224   {     # range dynamic-bootp 10.254.239.40 10.254.239.60;     # option broadcast-address 10.254.239.31;     # option routers rtr-239-32-1.example.org;   #}   # A slightly different configuration for an internal subnet.   #subnet 10.5.5.0 netmask 255.255.255.224 {   # range 10.5.5.26 10.5.5.30;   # option domain-name-servers ns1.internal.example.org;   # option domain-name "internal.example.org";   # option routers 10.5.5.1;   # option broadcast-address 10.5.5.31;   # default-lease-time 600;   # max-lease-time 7200;   #}   # Hosts which require special configuration options can be listed in   # host statements. If no address is specified, the address will be   # allocated dynamically (if possible), but the host-specific information   # will still come from the host declaration.   #host passacaglia {   # hardware ethernet 0:0:c0:5d:bd:95;   # filename "vmunix.passacaglia";   # server-name "toccata.fugue.com";   #}   # Fixed IP addresses can also be specified for hosts. These addresses   # should not also be listed as being available for dynamic assignment.   # Hosts for which fixed IP addresses have been specified can boot using   # BOOTP or DHCP. Hosts for which no fixed address is specified can only   # be booted with DHCP, unless there is an address range on the subnet   # to which a BOOTP client is connected which has the dynamic-bootp flag   # set.   #host fantasia {   # hardware ethernet 08:00:07:26:c0:a5;   # fixed-address fantasia.fugue.com;   #}   # You can declare a class of clients and then do address allocation   # based on that. The example below shows a case where all clients   # in a certain class get addresses on the 10.17.224/24 subnet, and all   # other clients get addresses on the 10.0.29/24 subnet.     #class "foo" {   # match if substring (option vendor-class-identifier, 0, 4) = "SUNW";   #}   #shared-network 224-29 {   # subnet 10.17.224.0 netmask 255.255.255.0 {   # option routers rtr-224.example.org;   # }   # subnet 10.0.29.0 netmask 255.255.255.0 {   # option routers rtr-29.example.org;   # }   # pool {   # allow members of "foo";   # range 10.17.224.10 10.17.224.250;   # }   # pool {   # deny members of "foo";   # range 10.0.29.10 10.0.29.230;   # }   上面这个文件中后面都是注释,是原来系统给你的配置做的例子,可以不要。当然除非你用到它们,并且清楚它们的含义。   9、建立空文件:touch /var/state/dhcp/dhcpd.leases空文件   10、检查内核编译选项:Networking options-----Packet socket: mmapped IO和Socket Filtering选项是否被选中(不知道怎么看?看看前面的文章,用make menuconfig呀),如果没有就需要重新编译内核了。   11、route add -host 255.255.255.255 dev eth0   12、roote add -host localhost dev eth0   13、在/etc/rc.d/rc.local最后增加一行route add -host 255.255.255.255 dev eth0   14、reboot系统   15、用dhcpd命令启动DHCP,用其他的机器试试看能不能自动配置网络了。 (4) 安装apache+mysql+php+gd+png+zlib+jpeg+freetype+sslmod   这么一大堆东西为什么要一块说呢?因为他们中间是有紧密联系的,尤其是那个PHP,用到其他所有的模块。要想PHP功能全,只能一步步来了。   1、首先安装MYSQL:从http://www.mysql.com/downloads/index.html下载自己觉得合适的版本。这里用3.23.42版本。   2、进入目录/usr/local/src(这个以后就不用说了吧?)解压缩:tar zxvf mysql-3.23.42.tar.gz   3、cd mysql-3.23.42/   4、configure --prefix=/usr/local/mysql   5、make   6、make install   7、useradd mysql ; groupadd mysql ; su mysql ;(建立命名为mysql的用户和组,并切换为mysql身份)   8、scripts/mysql_install_db (建立数据库结构)   9、cd /usr/local/mysql/bin   10、./safe_mysqld & (启动了)   11、./mysqladmin -u root password "new-password"(你的新密码,记住MYSQL的密码核系统密码是独立的,缺省是没有密码)   12、用mysql程序试试看。   下面的步骤都很程式化,我尽量简洁地写出。   13、下载相关的软件包:地址表格如下   软件     版本     地址 jpegsrc.v6b.tar.gz  6b ftp://ftp.uu.net/graphics/jpeg zlib.tar.gz 1.1.3 ftp://ftp.uu.net/graphics/png/src freetype-2.0.8.tar.gz 2.0.8 http://www.freetype.org libpng-1.0.10.tar.gz 1.0.10 ftp://ftp.uu.net/graphics/png/src gd-1.8.4.tar.gz 1.8.4 http://www.boutell.com/gd apache_1.3.23.tar.gz 1.3.23 http://www.apache.org php-4.1.1.tar.gz 4.1.1 http://www.php.net number4.tar.gz 4 http://www.php.net/extra rsaref20.tar.Z 2.0 ftp://ftp.ai.mit.edu/pub/deberg openssl-engine-0.9.6c.tar.gz 0.9.6c http://www.openssl.org/source mod_ssl-2.8.6-1.3.23.tar.gz 2.8.6 http://www.modssl.org   下齐了就放在/usr/local/src目录下,除了number4.tar.gz和rsaref20.tar.Z以外,其他一律用tar zxvf XXXX.tar.gz解压缩。(过瘾吧?)   特殊的,rsaref20.tar.Z 需要先建立一个rsaref-2.0目录,然后把rsaref20.tar.Z复制到改目录下用tar zxvf rsaref30.tar.Z释放压缩。Number4.tar.gz需要复制到php-4.1.1目录内,然后用tar zxvf number4.tar.gz解压缩。   下面的描述我想简单一点,只写出进入目录后执行的命令。那些目录都是释放的时候自己建立的,如果你这些操作都在/usr/local/src下面执行,所有的目录就都在这里面。最好按照顺序执行。再罗嗦一句:如果不是第一次编译,一般情况请执行一次make clean后再编译。   14、[jpeg-6b]:生成JPEG图像的函数库。   ./configure   make   make test   make install   15、[zlib-1.1.3]:压缩算法库,是PNG和JPEG需要的   make test   make install   16、[freetype-2.0.8]:字体库,在图像上使用字体的时候需要用到,   ./configure --prefix=/usr/local   make   make install   17、[libpng-1.0.10]:生成PNG图像的函数库   cp scripts/makefile.linux makefile   make test   pngtest pngnow.png   (如果都通过测试了,请继续。否则,看看错误提示,改正错误)   make install   18、[gd-1.8.4]:GD库,用来动态生成图像用,这里主要是配合PHP生成图像。   编辑Makefile 文件:   把下列行取消注释符号(#):   CFLAGS=-O -DHAVE_LIBXPM -DHAVE_LIBPNG -DHAVE_LIBJPEG   -DHAVE_LIBFREETYPE -DHAVE_LIBTTF   LIBS=-lgd -lpng -lz -ljpeg -lfreetype -lm -lttf   注释下列行(增加#):  CFLAGS=-O -DHAVE_LIBPNG -DHAVE_LIBJPEG   LIBS=-lgd -lpng -lz -lm   修改下列行:  INCLUDEDIRS=-I. -I/usr/local/include/freetype2 -I/usr/include/X11     -I/usr/X11R6/include/X11 -I/usr/local/include      -I/usr/include/freetype   保存后执行:   make   make install   19、[apache_1.3.23]:步骤一 ./configure --prefix=/usr/local/apache   20、[php-4.1.1]:   如果不是第一次安装,请删除:config.cache文件,然后执行先执行make clean   接下来配置环境,输入一个比较长的命令:   ./configure     --with-apache=/usr/local/src/apache_1.3.23     --with-config-file-path=/local/apache/conf     --with-enable-versioning     --with-mysql=/usr/local/mysql     --with-ftp     --with-gd=/usr/local/src/gd-1.8.4     --with-enabled-bcmath=yes     --with-disable-debug     --enable-memory-limit=yes     --enable-track-vars     --with-zlib     --with-jpeg-dir=/usr/local/src/jpeg-6b     --with-png-dir=/usr/local/src/libpng-1.0.10     --with-freetype-dir=/usr//local/src/freetype-2.0.8   当然你也可以实现准备一个含有上述命令的sh文件,这样改起来方便些。我就是这样做的。   make   make install   21、[rsaref-2.0]:RSA加密模块   前面说过,这个目录的释放是特殊的,先见目录后释放,希望你是这样做的。   cp -rp install/unix local   cd local   make   mv rsaref.a librsaref.a   22、[openssl-engine-0.9.6c]:openssl引擎   ./config -prefix=/usr/local/ssl -L`pwd`/../rsaref-2.0/local/ rsaref –fPIC   make   make test   make install   23、[mod_ssl-2.8.6-1.3.23]   ./configure --with-apache=../apache_1.3.23   24、[apache_1.3.23]步骤二   ./configure --prefix=/usr/local/apache --enable-shared=ssl      --enable-module=ssl --activate-module=src/modules/php4/libphp4.a   make   make certificate TYPE=custom(回答一些问题)  make install   25、收尾工作:   编辑/usr/local/apache/conf/httpd.conf文件,增加一行:   AddType application/x-httpd-php .php   26、用命令/usr/local/apache/bin/apachectl configtest检查一下配置文件的正确性。   27、用/usr/lcoal/apache/bin/apachectl start启动apache服务器   28、在/usr/local/apache/htdocs建立一个test.php文件包含下列内容:       phpinfo();   >   29、在别的机器上用浏览器看一下这个机器:http://xxx.xxx.xxx.xxx/test.php会出现php的配置总汇,看看前面编译的模块都在不在。   30、检验ssl很麻烦,需要用apachectl startssl启动apache然后还要生成一大堆证书。这里就不讨论了。   31、把/usr/local/apache/bin/apachectl文件复制到/etc/rc.d/init.d中,然后在/etc/rc.d/rd3.d中建立个符号连接指向/etc/rc.d/init.d中的上述文件:   ln -s ../init.d/apachectl S70apachectl   这样系统启动的时候就可以自动启动apache和她的哪一大堆模块了。   呼~~~~(喘口气)到这里,就基本上完成了一个功能很全的WEB服务器系统了。   (5)安装 ftp 服务器   (6)安装 pop3服务器   有的邮件服务器自己带pop3功能(比如Qmail、Xmail),如果你用这几种邮件服务器软件,当然就不用安装pop3了。目前,常用的邮件服务器里面不带pop3的好象只有sendmail和postfix等少数几种。这几种邮件服务器软件使用的都是系统用户,而我们安装的pop3也只是对系统用户的邮件进行弹出操作的。这里我们使用的版本和下载地址是:   qpopper4.0.3.tar.gz     http://www.eudora.com/qpopper_general/   同样的,把文件下载到/usr/local/src中,按照下列步骤安装:   tar zxvf qpopper4.0.3.tar.gz   cd qpopper4.0.3   ./configure   make   make install   完成后,qpopper安装在/usr/local/sbin中(如果你愿意把它放在其他地方,可以在前面用./configure –prefix=/your_path指定),文件名叫作popper。通常情况下,popper是作为一个inetd(超级网络服务进程)的子进程加载的。这样需要编辑文件/etc/inetd.conf并做下面改动: ...   # POP3 mail server   #pop-3 stream tcp nowait root /usr/sbin/tcpd ipop3d   pop-3 stream tcp nowait root /usr/local/sbin/popper qpopper –s   ...   增加黑体的这一行,注释掉原来的(上面这一行),注意,如果你的安装路经不同,记着改过来呀。然后,用下面的命令得到inetd的进程号:   ps –ax | grep inetd   得到结果类似:   248 ? S 0:00 inetd   什么?哪个是进程号?就是最前面那个数字。然后用下面的命令重新启动inetd:   kill –HUP 248   用下面的命令看看pop3是不是启动了。   netstat -ln|grep 110   如果结果是这样,就恭喜了。   tcp 0 0 0.0.0.0:110 0.0.0.0:* LISTEN   这里还要补充一点,少数情况下,pop3进程需要独立的监听端口,也就是不作为inetd的子进程装载,上面过程需要做如下改动:   ./configure的时候加上--enable-standalone参数。   编辑/etc/inetd.conf不增加黑体一行,但需要注释掉原来的pop-3一行(如果有的话)。   启动的时候,直接运行/usr/local/sbin/popper xxx.xxx.xxx.xxx:110即可。后面的参数是本地需要几监听的IP地址和端口。注意,没有特殊需要,端口必须是110。最后要说的是:其实最简单的办法就是用你使用的Linux发行包带的pop3软件,安装的时候直接选上即可。 -------------------------------------------------------------------------------- lsw 回复于:2002-12-23 11:43:32 (7)安装imap服务器  几乎所有的发行包都带有imap服务器软件(一般是华盛顿大学版本),最好的办法是用你的发行包安装IMAP服务器。事实上,我个人觉得IMAP服务器很少被使用。下载最常规的IMAP服务器源码地址是:ftp://ftp.cac.washington.edu/imap/imap.tar.Z   tar zxvf imap.tar.Z   cd imap-2001a   make slx   (如果不行依次试验一下其他你觉得可能的系统类型,用vi看一下Makefile中列的名称)   然后配置/etc/inetd.conf,让inetd允许imap服务。[本节内容没有试验成功,编译总是过不去]   (8)安装squid服务器   SQUID是一个优秀的代理服务器软件,它可以很灵活的被配置为各种应用形式,其中包括正向代理,反向加速模式和透明代理等。但是SQUID目前只能代理HTTP协议,代理FTP协议需要配置浏览器仿真主动FTP协议。下面的步骤是安装SQUID的过程。   1、下载反向代理服务器软件采用squid,下载地址:http://www.squid-cache.org/   下载后存放在/usr/local/src目录里,文件名是squid-2.4.STABLE2-src.tar.gz   2、tar zxvf squid-2.4.STABLE2-src.tar.gz 解压缩   3、cd /usr/local/src/squid-2.4.STABLE2 进入目录   4、./configure --prefix=/usr/local/squid --enable-heap-replacement --disable-internal-dns建立环境,把squid安装/usr/local/squid中。第二个参数是指定使用更先进的缓冲算法。第三个参数是取消内部DNS解析(如果使用在远程高速缓存模式,比如GSLB,需要增加选项:--disable-internal-dns,目的是关闭内部DNS.否则内部DNS不理睬你在etc/hosts中的设定,直接寻找域名服务器,这样就会造成转发循环。就是cache发送给物理服务器的更新请求(因为用域名)也会被用户的GSLN设备解析回来而形成循环。)   5、make 开始编译   6、make install 安装到刚才--prefix=指定的路径当中   7、安装完成后,会在您指定的安装路径里产生一个squid目录,squid目录下有四个目录:bin/ etc/ libexec/ logs/。其中etc里面是配置文件,bin里面是执行文件,logs里面是日志文件。   8、安装结束后就是调试服务器,使其按照您的要求工作。Squid的配置文件只有一个,在etc目录里,名字是squid.conf,所有的配置选项都在这个文件里面。而且每个配置项目都有注释说明。首先,在squid文件里面找到下列配置项:   cache_mem ---这里可以添上您准备给squid作为高速缓存使用的内存大小。注意,如果您的机器有N兆内存,那么,推荐您在这里添的数字是N/3。   cache_dir /usr/local/squid/cache 100 16 256 这里的第一个数字100是您准备给squid作为cache使用的硬盘空间大小,单位是兆。如果您想划100M空间当作cache,那么这里就写100。   cache_mgr webmaster@test.com.cn这里填写cache管理员的Email地址,系统出错会自动提醒cache管理员。   cache_replacement_policy和memory_replacement_policy的参数由于使用了--enable-heap-replacement编译参数,就不能使用缺省的lru算法了,你可以在下列三种里面选一个:   heap GDSF : Greedy-Dual Size Frequency   heap LFUDA: Least Frequently Used with Dynamic Aging   heap LRU : LRU policy implemented using a heap   例如:   cache_replacement_policy heap LRU   memory_replacement_policy heap LRU   下面设定ACL访问控制列表:为了简单起见,我们在这里开放所有的权限。ACL分为两个部分:ACL定义部分,和http_access部分。在access_http部分用到前面的定义。前面定义了:   acl all src 0.0.0.0/0.0.0.0   我们注释掉所有的http_access行加一句:   http_access allow all ---注意:all是前面acl定义的。   这样就开放了所有的权限。以后有需要的,再继续往上加各种限制即可。   9、以root身份创建组nogroup :   %su root (如果当前不是root)  #groupadd nogroup   #useradd nobody (如果没有这个用户)   10、进行目录/usr/local ,以root身份执行下面的命令,改变整个Squid目录的所有者为nobody.nogroup   #cd /usr/local   #chown nobody.nogroup -R squid   11、su 作为nobody,进行/usr/local/squid/bin目录,执行#squid -z 创建cache交换目录   #su nobody   $ cd /usr/local/squid/bin   $./squid -z   12、成功后,测试一下:/usr/local/squid/bin/squid -NCd1 该命令正式启动Squid。如果一切正常,你会看到一行输出 :Ready to serve requests.   13、用ctrl-c退出前台测试。   14、把squid作为守护进程启动就直接运行:/usr/local/squid/bin/squid   15、检查状态用squid –k check   16、停止squid用squid -k shutdown   如果是配置基本的正向代理,上面的已经可以使用了。下面的步骤用于配置支持多域名的反向代理服务器。还好,SQUID的所有配置都在/usr/local/squid/etc下面,和反向代理有关的几项介绍如下:   17、http_port 80 “http_port”参数指定Squid监听浏览器客户请求的端口号。   18、icp_port 0 “icp_port”参数指定Squid从邻居(neighbour)服务器缓冲内发送和接收ICP请求的端口号。这里设置为0是因为这里配置Squid为内部Web服务器的加速器,所以不需要使用邻居服务器的缓冲。   19、emulate_httpd_log on 打开“emulate_httpd_log”选项,将使Squid仿照Web服务器的格式创建访问记录。如果希望使用Web访问记录分析程序,就需要设置这个参数。   20、redirect_rewrites_host_header off 缺省地,Squid将改写任何重定向请求的主机头部。若系统运行Squid为加速器模式,则这时不需要重定向特性。该参数在负载过重的情况下要旁路重定向器时才打开   21、httpd_accel_host vartual 此处设置反向代理的主机名,如果对后面多个域名进行缓冲,请使用虚拟主机模式(如此)。   22、httpd_accel_port 80 此处设置反向代理的WEB服务端口号。   23、#httpd_accel_with_proxy off 把这行注释掉,此处设置开反向代理的同时,是否开普通代理缓存服务。如果这行不注释掉,就没有高速缓存功能。   24、定义访问控制列表:   acl port80 port 80   acl accel_host1 dstdomain .test.com   acl accel_host2 dstdomain .test.net   … …   http_access allow accel_host1 port80   http_access allow accel_host2 port80   http_access deny all;   25、完成后,用squid –k reconfigure重新装载配置文件。   (9)安装SSH   ---   (10)配置Linux为路由器   ---   (11)配置Linux网关和安装ipchains/iptables防火墙   在Linux上面的防火墙,最最常用的是ipchains,而且通常情况下是作为网关的附加部分安装的。Ipchains的规则是很复杂的,灵活性也很强,可以配制成各种五花八门的样子。这些都需要和你自己的实际情况相结合。这里,我们只介绍一种基于网关的简单配置。   安装IPCHAINS一般都不用你操心,因为几乎所有的Linux发行包都把该软件作为必须安装的却省配置。另一个原因是ipchains是跟内核(kernel)有很大的关系,因此最好在安装系统的时候选上相关选项(如果有)。在标题里面我们还提到了iptables,这个工具在表面上等同于ipchains,只不过是用于2.4内核的(2.4内核在这方面的代码几乎是重新写过的,功能有了长足的提高)。关于iptables工具的配置我们在后面介绍。你只需要记住2.2核心下使用ipchains,2.4核心下使用iptables即可。   首先,你的服务器需要两块网卡(或更多),这种机器叫做“多宿主主机”,是专门的用于做网关或路由器的。这里插一句:一般情况下,作为普通服务器的主机即使负载再重就需要一块网卡就够了,只有做网关或路由器的时候才需要多宿主主机。这并不象一般人认为得那样增加一块网卡可以增加一份带宽,事实上,一块网卡就可以提供足够的带宽。并且,还有人错误的把两块网卡接在同一个交换机上分配两个地址,这更是错误的,因为这样产生了额外的循环路由,会产生大量的内部警告错误,某些系统就会报警。   1、制作一个双界面(双宿主)的主机。   通常,现在的发行包的安装程序都可以识别两块网卡,这样就省事了。但是也有不少发行包只识别第一块网卡(也许是出于前面说的原因),或者你要在一台在用的机器上加一块网卡(因为你不愿意重装系统),那么就按照下面的办法处理。   1.1、我们就说PCI网卡。安装前首先看看网卡芯片,记住芯片的型号(希望你自己攒过机器)。   1.2、安装MAN手册(用发行包)   1.3、在/usr/doc/HOWTO/english/txt/Ethernet-HOWTO文件(如果是压缩的就释放)中搜索你的网卡型号,找到对应的驱动模块名称。   1.4、如果模块没有,还需要重新编译核心。在menuconfig网络设备一栏选中你的型号然后标记为*或M,编译完核心后别忘了编译模块:make modules;make modules_install。(不会的话再仔细温习一下前面编译内核的部分)   1.5、用depmod –a命令建立/etc/modules.conf(如果已经有就不用了),也有的发行包叫做conf.modules   1.6、编辑该文件加上一行:alias eth1 XXXX,其中XXXX是你刚才查到的模块名称。一般该模块文件位于:/lib/modules/内核版本号/net…中的XXXX.o文件。这个文件是你编译内核模块的时候产生的,你在内核配置的时候,凡是标记为M的都会被编译成.o文件放在这里。同样的,你选择的网卡驱动也是会编译成模块在这里的。   1.7、运行modprobe eth1使模块有效。   1.8、修改/etc/sysconfig/network文件中需要修改的部分。   1.9、建立或修改/etc/sysconfig/network-script/ifcfg-eth1文件(照抄那个eth0的即可),设定地址是你的真实情况,比如设为eth1要接的网段。这两个文件(eth0/eth1是启动的时候的脚本参数文件)   1.10、重新启动一下网络:/etc/rc.d/init.d/network restart   1.11、用ifconfig看看是否eth0/eth1都启动了。   1.12、大功告成   2、调整和编译核心:如果作为网关,有些核心选项需要配置。注意:这里的内核编译选项仅仅适合于2.2.X版本,2.4版本完全不同。   2.1、在/usr/src/linux中运行make menuconfig配置核心下列选项:   Networking options中 [*] Network firewalls   [*] IP: advanced router   [*] IP: firewalling   [*] IP: firewall packet netlink device   [*] IP: transparent proxy support   [*] IP: masquerading   [*] IP: ICMP masquerading   [*] IP: masquerading special modules support    IP: ipautofw masq support (EXPERIMENTAL)    IP: ipportfw masq support (EXPERIMENTAL)    IP: ip fwmark masq-forwarding support (EXPERIMENTAL)   [*] IP: masquerading virtual server support (EXPERIMENTAL)   (12) IP masquerading VS table size (the Nth power of 2)   具体选项不同版本的内核不尽相同,你看着差不多就可以了。编译成模块或编进核心[*]可以自由选择。   2.2、按照前面说的编译内核,重新启动后就具有网关功能了。启动后验证一下文件:/proc/sys/net/ipv4/ip_forward内容是否为1。(别告诉我不知道怎么看8-))   3、设置ipchains   本来这项工作是非常复杂的,但是我们这里讲的简单,让网关通了就行。   3.1、先看一下目前我们目前的假想环境:你的机器有两块网卡,一块连接在内网交换机配内部网络地址,另一块在外网交换机配公网地址。在这个机器ping 内网和外网的机器应该都能通。并且在内网的机器ping 该机器的外网地址也通,在外网的机器ping该机器内网的地址也同样通。这说明转发功能是有效的。   3.2、再试验一下:在内部网络把网关设为你的机器的内网地址,然后ping 外网的任一台机器(不是这台机器的外网地址),结果是不通。原因是ICMP包发出去后外网的机器不知道怎么回答,那里没有合适的路由,因为这个包的回应地址是内网。   3.3、现在设置ipchains:(ipchains软件适用2.2.X的内核,在2.4.X的内核中不适用。2.4.X的内核推荐使用iptables,关于该软件的用法在下面介绍)   ipchains –A forward –s 192.168.1.0/24 –j MASQ   黑体部分是内网地址。这句话的意思是,把所有内网发送的包都伪装成外部网的地址(那个机器的外网地址),这样,外部其他机器回应的时候就会发送到这台机器,经过转发就回来内网了。好了,一个简单的网关做好了。如果你需要实现防火墙(包过滤功能),则需要配置更加复杂的过滤规则,这些规则应该同时作用于input/output/forward链。   3.4、自动启动:设定了复杂的链,需要保存。在/etc/rc.d中建立一个文件:rc.ipfwadm把你的链脚本加进去就可以了。最后给这个文件+x属性(chmod +x rc.ipfwadm)。   4.在2.4.X内核中使用iptables建立Linux防火墙(网关)。   4.1 说明和下载   前面已经提到过2.4 内核中要使用iptables来做类似于ipchains在2.2内核中实现的事情。事实上,在2.4内核中的iptables包含了两个完全不同的部分:包过滤和地址转换。这两个功能在逻辑上是分开的,考虑到操作习惯上的问题才集成在一个配置程序(iptables)上。如果你安装了带有2.4内核的发行包,那么一般都带有iptables,并且在内核配置上也配置好了。如果要编译新的内核或者原来的内核没有支持iptables,需要选上相关的选项。使用make menuconfig 配置内核的时候操作:进入:Networking Options至少选择上:   [*] Network packet filtering (replaces ipchains)   [*] Network packet filtering debugging (NEW)   进入:IP: Netfilter Configuration --->   [*] Connection tracking (required for masq/NAT) (NEW) FTP protocol support (NEW) IRC protocol support (NEW)   等等……   把下面的选项全部标记<*>   逐级返回上面菜单后保存配置,然后按照编译内核的那一套程序进行即可(参见前面的内容)。这里需要说明的是,内核支持了iptables功能,还需要有外部程序才行。如果你是直接安装的带有2.4内核的发行包,应该已经带有iptables实用程序了。但是如果你是从2.2核心的发行包直接升级到2.4内核,尽管按照上面的做法配置的核心,但是还不能使用iptables。还必须下载和编译iptables外部程序才能使用iptables功能。下载的地址是:http://www.netfilter.org ,下载的文件是iptables-1.2.6a.tar.bz2或更新的版本。   4.2 安装:   这个.bz2文件比较奇怪吧?把文件复制到/usr/local/src中,然后用下列命令释放编译和安装: bzip2 -dc iptables-1.1.2.tar.bz2 |tar vxf - (最后那个‘- ’别忘了)   你的系统可能没有bzip2工具,如果没有,你就去下一个:   进入新生成的目录 :cd iptables-1.2.6a 编译和安装:make ; make install   4.3 配置   如果你需要配置一个简单的网关,你仅仅需要配置一个转发加地址转换功能即可,我提供的参考脚本如下:   #begin   echo 1 > /proc/sys/net/ipv4/ip_forward   modprobe iptable_nat   modprobe ip_conntrack   modprobe ip_conntrack_ftp   modprobe ip_nat_ftp   iptables -F INPUT   iptables -F FORWARD   iptables –F OUTPUT   iptables -F POSTROUTING -t nat   iptables -t nat -F   iptables -P FORWARD DROP   iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT   iptables -A FORWARD -i eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT   iptables -t nat -A POSTROUTING -o eth1 -s 192.168.1.0/24     -j SNAT --to 211.99.223.2   #end   其中3行modprobe是当你在内核中把iptables相关功能编译成模块(选成)的时候才用到的(也可能不止3行)。其中插入模块(modprobe)的几行比较重要,有时候当你作完NAT后,发现FTP不好用了,就要手工插入ip_nat_ftp模块(语法是:modprobe ip_nat_ftp)。黑体的地址部分可能是需要根据你网络的实际情况来决定的。最后一句话的意思是把凡是源地址是192.168.1.x的包做地址转换,转换为网关外侧地址211.99.223.2。好了,你已经建立起一个基本的网关了。其实iptables的功能及其强大,配置也非常灵活。其中的防火墙功能通过-t filter参数实现,地址转换功能通过-t nat实现(就像上面一样)。防火墙功能(packet filter)主要是3个链:INPUT,FORWARD,OUTPUT,地址转换功(nat)主要是3个链:PREROUTING,POSTROUTING,OUTPUT。观察当前的状态可以使用下面的命令:iptables –L –t filter 或 iptables –L –t nat。
posted @ 2006-06-01 15:39 edsonjava 阅读(212) | 评论 (0)编辑 收藏
 
 文件与目录操作命令
1.1文件内容查询命令 grep、fgrep、egrep
语法:grep[选项][查找模式][文件名1,文件名2,……]
选项: -E 每个模式作为一个扩展的正则表达式对待
-F 每个模式作为一组固定字符串对待,而不作为正则表达式
-i 比较时不区分大小写
-l 显示首次匹配匹配串所在的文件名并用换行符将其分开。当在文件中多次出现匹配串时,不重复显示次文件名;
-x 只显示整行严格匹配的行
1.2文件查找命令 find、locate
语法:find 起始目录 寻找条件 操作
以名称和文件属性查找
-name‘字串‘ 查找文件名匹配所给字串的所有文件,字串内可用通配符*、?、[]。
-lname‘字串‘ 查找文件名匹配所给字串的所有符号链接文件,字串内可用通配符*、?、[]。
-gid n 查找属于ID号为n的用户组的所有文件。
-uid n 查找属于ID号为n的用户的所有文件。
-group‘字串‘ 查找属于用户组名为所给字串的所有的文件。
-user‘字串‘ 查找属于用户名为所给字串的所有的文件。
-path‘字串‘ 查找路径名匹配所给字串的所有文件,字串内可用通配符*、?、[]。
-perm 权限 查找具有指定权限的文件和目录,权限的表示可以如711、644。
-type x 查找类型为 x的文件,
语法:locate 相关字
1.3文件的复制、删除和移动命令文件复制命令cp
[选项] 源文件或目录 目标文件或目录
选项:
- a 通常在拷贝目录时使用
-d 拷贝时保留连接
-f 删除已经存在的目标文件而不提示
-i 和f选项相反
-p 此时cp除复制源文件内容外,还将其修改的时间和访问权限也复制到新文件中
-r 若给出的源文件是一目录文件,此时cp将递归复制该目录下的所有的子目录和文件,此时目标文件必须为一个目录名;
-l 不作拷贝,只是链接文件
文件移动命令 mv
[选项] 源文件或目录 目标文件或目录
-i 交互式操作
-f 禁止交互式操作
文件删除命令 rm
[选项] 文件…
-f 忽略不存在的文件,从不给出提示
-r 指示rm将参数中列出的全部目录和子目录均递归地删除
-i 进行交互式删除
1.4文件链接命令ln
[选项] 目标 [链接名] 或 ln [选项] 目标 目录
选项:- s 建立符号链接
1.5目录的创建和删除命令
mkdir 创建一个目录
语法:mkdir [选项] dirname
选项:- m 对新建目录设置存取权限
-p 可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录。
rmdir 删除空目录
语法:rmdir [选项] dirname
选项: - p 递归删除目录dirname,当子目录删除后其父目录为空时,也一同被删除。
1.6改变工作目录,显示目录内容命令
1、改变工作目录:cd [directory]
2、显示当前工作的目录的绝对路径: pwd
3、显示目录内容:ls [选项] [目录或是文件]
选项:
- a 显示指定目录下所有子目录与文件,包括隐藏文件;
-c 按照文件的修改时间排序
-C 分成多列显示各项
-d 如果参数是目录,只显示其名称而不显示其下的个文件
-F 在目录名后面标记“/”,可执行文件后面标记“*”,符号链接后面标记“@”,管道(或FIFO)后面标记“|”,socket文件后面标记“=”。
-l 以长格式来显示文件的详细信息
-L 若指定的名称为一个符号链接,则显示链接所指向的文件
-t 显示时按修改时间而不是名字排序
-u 显示时按文件上次存取的时间而不是名字排序
4、改变文件或目录的访问权限命令
chmod [who] [+| - | = ] [mode] 文件名?
Who选项:
- u表示用户,即文件和目录的所有者
-g表示同组用户
-o(other)表示其他用户
-a (all)表示所有用户
操作符号:
+ 添加某个权限 - 取消某个权限
= 赋予给定权限并取消其他所有的权限(如果有的话)
mode 选项:
- r 可读
-w 可写
-x 可执行
chgrp [-R] group filename? 改变目录或文件所属的组
chown [-Rv] 用户或组 文件
1.7备份与压缩命令
1、tar命令 为文件和目录创建档案
语法:tar [主选项+辅助选项] 文件或者目录
u 主选项
c 创建新的档案文件。如果用户想备份一个目录或是一些文件,则选此项
r 把要存档的文件追加到档案文件的末尾
t 列出档案文件的内容,查看已经备份了哪些文件;
u 更新文件
x 从档案文件中释放文件;
u 辅助选项
b 该选项为磁带机设定的,其后跟一位数字,用来说明区块的大小,系统预设值为20
f 使用档案文件或设备,这个选项通常是必选的
k 保存已经存在的文件。例如把某个文件还原,在还原的过程中,遇到相同的文件,不会进行覆盖;
m 在还原文件时,把所有文件的修改时间设定为现在;
M 创建多卷的档案文件,以便在几个磁盘中存放;
v 详细报告tar处理的文件信息
w 每一步都要求确认
z 用gzip来压缩/解压缩文件
2、gzip命令 压缩/解压缩命令
语法:gzip [选项] 压缩(解压缩)的文件名
选项:-c 将输出写到标准输出上,并保留原有文件
-d 将压缩文件解压
-l 显示每个压缩文件的详细信息
-r 递归式地查找指定目录并压缩其中的所有文件或者是解压缩
-t 测试、检查压缩文件是否完整
-v 对每一个压缩和解压的文件,显示文件名和压缩比
3、unzip 命令
用MS windows下的压缩软件winzip压缩的文件在linux系统下展开
语法:unzip [选项] 压缩文件名.zip
选项: -x 文件列表 解压缩文件,但不包括指定的file文件
-v 查看压缩文件目录,但不解压
-t 测试文件有无损坏 ,但不解压
-d 目录 将压缩文件解到指定目录下
-z 只显示压缩文件的注解
-n 不覆盖已经存在的文件
-o 覆盖已经存在的文件且不要求用户确认
-j 不重建文档的目录结构,把所有文件解压到同一目录下
1.8在LINUX环境下运行DOS命令
linux系统提供了一组称为mtools的可移植工具,可以让用户轻松地从标准的DOS软盘上读写文件和目录。
mcd 目录名 改变MSDOS目录
mcopy 源文件 目标文件 在MSDOS和UNIX之间复制文件;
mdel 目录名 删除MSDOS目录
mdir 目录名 显示MSDOS目录
mformat 驱动器号 在低级格式化的软盘上创建MSDOS文件系统
rnlabel 驱动器号 产生MSDOS卷标
mmd 目录名 删除MSDOS目录
mren 源文件 目标文件 重新命名已存在的MSDOS文件
mtype 文件名 显示MSDOS文件的内容
2 设备管理命令
linux采用下面的形式定义一个IDE硬盘:/dev/hd[drive][partition]
SCSI硬盘使用同样的机制表示: /dev/sd[drive][partition]
对于一般的LINUX分区,可以用mkfs将其格式化并生成文件系统,命令如下:
mk2fs -c <partition> <size>
装载文件系统:mount -t ext2 [-o optioms] partition mountpiont
其中,-t为指定装载文件系统的类型;-o指定一些选项,如只读ro,可读可写rw等等;partition定义分区名称;mountpiont定义文件系统被装载的目录名称。
装载CD-ROM文件系统:mount -t iso9660 -r /dev/cdrom /mnt/cdrom
装载软驱文件系统: mount -t msdos -rw /dev/fd0 /dev/mnt/floppy
卸载文件系统 umount /mnt/cdrom
磁带设备的安装要注意以下几点:
1、 首先要选择一个唯一的SCSI ID号,然后再将设备链接到适当的位置
2、 选择驱动程序。
3、 生成设备文件。SCSI磁带设备的主要设备号是9,次要设备号是0。设备文件名通常是/dev/nrst0(不支持回绕的磁带设备)或/dev/nst0(支持回绕的磁带设备)
用 ls /dev/*rst*
检查磁带设备文件是否存在,如果不存在,用
mknod -m 666 /dev/nrst0 c 9 9
mknod -m 666 /dev/rst0 c 9 0 生成
4、 可以对块长度、缓存、磁带密度等参数进行一些设置,例如
mt setblk 20 将块长度指定为20
mt setblk 0 指定块程度没有限制
5、 通过检查系统的启动信息可以确定系统是否识别了新的磁带设备。用dmesg命令,查看是否有以下类似的信息:
aha274x:target 4now synchronous at 4.4Mb/s
Vendor:TANDBERG Model:TDC 3800 Rev: =05:
Type: Sequential-Access ANSI SCSI revision: 02
Detected scsi tape st0 at scsi0, id4, lun0
Scsi : detected 1 SCSI tape 1 SCSI crom 1 SCSI disk total
3 软件包管理命令
3.1软件安装的步骤
在LINUX系统上安装软件的步骤有:
1、 查找所要安装软件的源文件
2、 把源文件解开放到一个目录中,命令如下:
tar zxvf <源文件名>
3、 针对本操作系统配置源文件。可以是编辑make文件或其他文件,也可能是运行该软件自带的自动配置工具,如./configure
4、 make源文件,通常是运行make命令,即执行 make
5、 安装二进制文件和其他支撑文件,运行命令: make install
6、 最后,完成所有其他必须的配置
3.2软件包管理命令
rpm -ivh <软件包>
// 安装指定的软件包,并在安装过程中用#表示安装的进度
rpm -Uvh <软件包>
// 更新一个已经存在的或还没安装好的软件包,并删除所有该软件包的旧版本。
rpm -e
//卸载一个rpm软件包
rpm -qa
// 查看系统中已经安装的软件包
rpm -q <软件包>
// 查看系统中某个软件包的版本号;
rpm -qlp <软件包>
//列出某个软件包中的所有文件
rpm -qf <软件包>
//找出一个文件属于哪个软件包
4 LINUX系统常用命令
4.1 与系统管理有关的命令
Wall (Write All)
对全部已 登录的用户发送信息,用户可以先反要发送的信息写好存入一个文件中,然后输入:# wall < FileName
例:Wall ‘Thank you !’
Write
向某一用户发送信息。
Write xxq
hello
posted @ 2006-06-01 15:30 edsonjava 阅读(123) | 评论 (0)编辑 收藏
 

Linux,在今天的广大电脑玩家耳中已经不再是那个曾经陌生又遥远的名字,大家提起Linux时,不再是把它当做与微软抗衡的一面大旗或自由软件爱好者的精神支柱。如果说几年前的Linux是星星之火的话,今天的它已经真正地形成了燎原之势。

  Linux,在今天的广大电脑玩家耳中已经不再是那个曾经陌生又遥远的名字,大家提起Linux时,不再是把它当做与微软抗衡的一面大旗或自由软件爱好者的精神支柱。如果说几年前的Linux是星星之火的话,今天的它已经真正地形成了燎原之势。随着越来越多成熟的Linux发行版的推出以及Linux推广的许多问题(安装不方便、中文化困难、软件匮乏、缺乏统一标准等)得到 圆满解决,现在Linux已经真正地向广大的电脑爱好者们敞开了大门。

  一、安装前的准备

  你完全可以打消Linux对计算机硬件是否支持的顾虑,放心大胆地安装。

  1.检查硬件支持

  在安装Linux之前,先确定你的计算机的硬件是否能被Linux所支持。

  首先,Linux目前支持几乎所有的处理器(CPU)。其次,早期的Linux只支持数量很少的显卡、声卡,而如今,如果要安装Linux,已经不需要再为硬件是否能被Linux支持担心了。经过十多年的发展,Linux内核不断完善,已经能够支持大部分的主流硬件,同时各大硬件厂商也意识到了Linux操作系统对其产品线的重要性,纷纷针对Linux推出了驱动程序和补丁,使得Linux在硬件驱动上获得了更广泛的支持。

  另外,如果你的声卡、显卡是非常新的型号,Linux内核暂时无法支持,那也不要紧,RedHat会自动把无法准确识别的硬件模拟成标准硬件来使用,让硬件一样可以在Linux发挥作用。

  由于设计Linux时的初衷之一就是用较低的系统配置提供高效率的系统服务,所以安装Linux并没有严格的系统配置要求,只要Pentium以上的CPU、64MB以上的内存、1GB左右的硬盘空间,就能安装基本的Linux系统并且能运行各种系统服务。但是如果要顺畅地运行X-Window,就需要有足够的内存,建议128MB以上。

  现在,你可以打消Linux对计算机硬件是否支持的顾虑,放心大胆地安装了吧。
 2.确认安装方式

  RedHat Linux9.0采用了稳定的内核Linux Kernel 2.4.20,配合GCC 3.2.1,以及GNU libc 2.3.2。这些最新的特性能够保证整个系统的优越表现。

  (1)从光盘安装

  最简单,最方便的安装方法当然是从CD安装,你可以享受最人性化的,类似于Windows的安装。你只要将计算机设置成光驱引导,把安装CD1放入光驱,重新引导系统,在安装界面中直接按回车,即进入图形化安装界面(图1)。



图 1

  由图可见,在提供“豪华”的图形化GUI安装界面的同时,REDHAT Linux 9.0仍然保留了以往版本中的字符模式安装界面,这对于追求安装速度与效率的用户一直是很有吸引力的。因为许多用户是将RedHat 9安装成服务器来使用的,不需要X-Window以及GUI安装界面。

  RedHat 9的安装步骤中比以往多了一个环节,那就是对安装光盘介质的检测。它允许在开始安装过程前对安装光盘介质进行内容校验,以防止在安装的中途由于光盘无法读取或是内容错误造成意外的安装中断,导致前功尽弃。

  REDHAT Linux 9.0如果完全安装将达到7张光盘,安装时间长达几十分钟。如果因为一张光盘的内容错误导致安装失败,这将浪费很多安装时间。所以,建议在安装之前对光盘进行介质检测与校验以保证安装顺利进行。
(2)从硬盘安装

  如果没有安装光盘,而是从网上直接下载Linux的ISO映像文件,能不能用下载的ISO文件进行安装而不用刻录成光盘呢?当然可以!

  从硬盘安装RedHat Linux 9.0通常需要三个文件①shrike-i386-disc1.iso;②shrike-i386-disc2.iso;③ shrike-i386-disc3.iso。这代表了安装时需要的三张光盘。由于是映像文件,系统无法直接读取,所以需要先将ISO里的文件还原。

  这里推荐大家使用Daemon Tool(http://www.linuxeden.com/download/winapps/daemon333.exe)这个Windows下的软件将ISO文件“解”到硬盘上。比方说shrike-i386-disc1.iso可以解压缩到C:盘的cd1目录,shrike-i386-disc2.iso解压缩到C:盘的cd2目录,shrike-i386-disc3.iso可以解压缩到C:盘的cd3目录待用。

  接下来重新启动系统进入MS-DOS方式,进入我们刚才解压出来的C\cd1目录,里面有个dosutils目录,执行里面一个名为autoboot.bat的DOS批处理文件,系统就会再次重新启动,进入Linux的安装界面。这时安装程序就会提示你选择是用光盘安装还是从硬盘安装,选择从硬盘安装后,系统会提示输入安装文件所在的目录。

  需要注意的是,我们刚解压缩的ISO文件是在Windows中操作的,如果直接输入c:\cd1,Linux安装程序是无法识别的,我们需要将c:\cd1对应到Linux安装程序能够识别的格式,因此这里应该输入的是/dev/hda1/cd1。

  二、正式开始安装

  你会觉得简直就和安装Windows一样了。

  通过上面的叙述,无论是从光盘安装,还是从硬盘安装,我们都可以方便地进入正式的安装过程。让我们来看看安装过程中几个重要的地方。

  1.选择系统默认语言

  RedHat支持世界上几乎所有国家的语言,这里只要在简体中文前面打上钩,并将系统默认语言选择为简体中文(图2),那么在安装过程结束,系统启动后,整个操作系统的界面都将是简体中文的了,用户不用做任何额外的中文化操作和设置。



图 2

  2.分区操作

  接下来,是磁盘分区的工作,这也许是整个安装过程中惟一需要用户较多干预的步骤,REDHAT Linux 9.0提供了两种分区方式——自动分区和使用DISK DRUID程序进行手动分区(图3)。



图 3

  (1)自动分区:如果是全新的计算机,上面没有任何操作系统,建议使用“自动分区”功能,它会自动根据磁盘以及内存的大小,分配磁盘空间和SWAP空间。

  这是一个“危险”的功能,因为它会自动删除原先硬盘上的数据并格式化成为Linux的分区文件系统(EXT3、REISERFS等),所以除非计算机上没有任何其他操作系统或是没有任何需要保留的数据,你才可以使用“自动分区”功能。

  (2)手动分区:如果硬盘上有其他操作系统或是需要保留其他分区上的数据,建议采用DISK DRUID程序进行手动分区。DISK DRUID是一个GUI的分区程序,它可以对磁盘的分区进行方便的删除、添加和修改属性等操作,它比以前版本中使用的字符界面Fdisk程序的界面更加友好,操作更加直观。下面我们来看看如何使用DISK DRUID程序对硬盘进行分区。

  因为Linux操作系统需要有自己的文件系统分区,而且Linux的分区和微软Windows的分区不同,不能共用,所以,需要为Linux单独开辟一个(或若干个)分区。Linux一般可以采用EXT3分区,这也是REDHAT Linux 9.0默认采用的文件系统。

  为Linux建立文件分区可以有两种办法,一种是利用空闲的磁盘空间新建一个Linux分区,另一种是编辑一个现有的分区,使它成为Linux分区。如果没有空闲的磁盘空间,就需要将现有的分区删除后,腾出空间,以建立Linux分区。
 DISK DRUID程序中有明显的新建、删除、编辑、重设等按钮。用户可以直观地对磁盘进行操作。在使用DISK DRUID对磁盘分区进行操作时,有四个重要的参数需要仔细设定:它们是挂载点、文件系统类型、驱动器、分区大小(图4)。



图 4

  挂载点:它指定了该分区对应Linux文件系统的哪个目录,Linux允许将不同的物理磁盘上的分区映射到不同的目录,这样可以实现将不同的服务程序放在不同的物理磁盘上,当其中一个物理磁盘损坏时不会影响到其他物理磁盘上的数据。

  文件系统类型:它指定了该分区的文件系统类型,可选项有EXT2、EXT3、REISERFS、JFS、SWAP等。Linux的数据分区创建完毕后,有必要创建一个SWAP分区,它实际上是用硬盘模拟的虚拟内存,当系统内存使用率比较高的时候,内核会自动使用SWAP分区来模拟内存。

  大小:指分区的大小(以MB为单位),Linux 数据分区的大小可以根据用户的实际情况进行填写,而SWAP大小根据经验可以设为物理内存的两倍,但是当物理内存大于1GB时,SWAP分区可以设置为2GB。

  允许的驱动器:如果计算机上有多个物理磁盘,就可以在这个菜单选项中选中需要进行分区操作的物理磁盘。

  经过磁盘分区的操作,安装过程中相对最复杂的一个步骤已经过去,接下来的安装将是一马平川。让我们来继续选择要安装的系统组件。
3.选择安装组件

  REDHAT Linux 9.0和先前的版本在安装组件的选择上非常相似,用户既可以选择桌面计算机、工作站、服务器、最简化安装这四个安装方法中的一个,也可以自己定义需要安装哪些软件包,并且安装程序会实时地估算出需要的磁盘空间,对用户非常方便。

  系统组件安装完毕后,安装程序会自动将用户选择的软件包从光盘介质拷贝到计算机的硬盘上,中途不需人工干预,并且在安装每个系统组件时都会对该组件做简短的说明。

  在选择软件包时,如果你想进一步配置系统,可以选定制软件包集合。建议定制,选上KDE桌面环境,这样你就有两个可以和WindowsXP媲美的真彩图标的桌面(图5)。



图 5

  怎么样,安装过程很简单吧?当然,这还不是最后一步,因为在安装完所有系统组件后,安装程序还会“体贴”地提醒你制作一张启动磁盘,以备不测。

  到此为止,Linux系统就已经顺利地安装完成了。
三、更改启动方式

  它比Windows更好玩一点。

  作为默认,REDHAT Linux 9.0在启动时会自动启动X-Window进入图形化操作界面。而许多Linux铁杆玩家已经习惯了在Console字符界面工作,或是有些玩家嫌X-Window启动太慢,喜欢直观快速的Console操作。

  1.进入字符界面

  为了在Linux启动时直接进入Console界面,我们可以编辑/etc/inittab文件。找到id:5:initdefault:这一行,将它改为id:3:initdefault:后重新启动系统即可。我们看到,简简单单地将5改为3,就能实现启动时进入X-Window图形操作界面或Console字符界面的转换,这是因为Linux操作系统有六种不同的运行级(run level),在不同的运行级下,系统有着不同的状态,这六种运行级分别为:

  0:停机(记住不要把initdefault 设置为0,因为这样会使Linux无法启动 )
  1:单用户模式,就像Win9X下的安全模式。
  2:多用户,但是没有 NFS 。
  3:完全多用户模式,标准的运行级。
  4:一般不用,在一些特殊情况下可以用它来做一些事情。
  5:X11,即进到 X-Window 系统。
  6:重新启动 (记住不要把initdefault 设置为6,因为这样会使Linux不断地重新启动)。

  其中运行级3就是我们要进入的标准Console字符界面模式。

  2.自由转换字符界面和X-Window图形界面

  在了解了启动自动进入X-Window图形操作界面和Console字符操作界面的转换后,也许你会想,这两种操作界面各有各的好处,我能不能“贪心”一点,同时拥有这两种操作界面呢?在无所不能的Linux操作系统中,这个要求当然是可以得到满足的。

  在X-Window图形操作界面中按“Alt+Ctrl+功能键Fnn=1~6”就可以进入Console字符操作界面。这就意味着你可以同时拥有X-Window加上6个Console字符操作界面,这是一件多么令人振奋的事情啊!

  在Console字符操作界面里如何回到刚才的X-Window中呢?很简单,按“Alt+Ctrl+F7”即可。这时Linux默认打开7个屏幕,编号为tty1~tty7。X-Window启动后,占用的是tty7号屏幕,tty1~tty6仍为字符界面屏幕。也就是说,用“Alt+Ctrl+Fn”组合键即可实现字符界面与X Window界面的快速切换。

  Linux的老用户们都知道,X-Window是一个非常方便地图形界面,它能使用户用鼠标最简单的进行操作,但是它也有不少缺点:比如启动和运行速度慢、稳定性不够、兼容性差、容易崩溃等。但是一旦X-Window系统出了问题,并不会使整个Linux系统的崩溃而导致数据丢失或系统损坏,因为当X-Window由于自身或应用程序而失去响应或崩溃时,我们可以非常方便地退出X-Window进入Console进行故障处理,要做的只是按“Alt+Ctrl+Backspace”键,这意味着只要系统没有失去对键盘的响应,X-Window出了任何问题,都可以方便地退出。

posted @ 2006-05-31 22:54 edsonjava 阅读(201) | 评论 (0)编辑 收藏
 
什么是ISO文件

ISO文件一般以iso为扩展名,是复制光盘上全部信息而形成的镜像文件,其文件格式为iso9660。
许多Linux操作系统的安装包都是以ISO文件的形式发布的。在得到相应的ISO文件后,可以将其内容烧录到光盘上。这样做出来的光盘与购买的安装光盘基本上是相同的。用这个光盘启动计算机,就可以安装Linux操作系统了。
本章所讨论的是硬盘安装法,是不需要烧录光盘的。但我们需要读取ISO文件中的信息。

Linux下操作ISO文件

在Linux下,以root身份执行以下格式的命令就可以从/path目录中读到xxx.iso这个文件中的内容。
实际使用时,应该根据实际情况修改/path和xxx.iso。
mount -t iso9660 -o loop xxx.iso /path
如果想制作ISO文件,把光盘放到光驱中,然后执行如下的拷贝命令就可以把光盘上的内容拷贝到一个ISO文件中去。实际使用时,应该根据实际情况修改xxx.iso。
cp /dev/cdrom xxx.iso


Windows下操作ISO文件

在windows下,一般需要专用工具软件才能操作ISO文件。比如WinISO、WinImage、Dameon Tools
等。
如果仅仅是想读取ISO文件中的内容,则可以用WinRAR。WinRAR3.0以上版本都支持iso9660文件
格式。用WinRAR打开ISO文件,选中需要的内容,将其解压出来就可以了。

Linux操作系统安装盘的ISO文件

红旗Linux4.1桌面版的iso安装文件在Linux环境下挂装到/mnt/iso目录后看到的内容。
如果把这个iso文件刻成光盘,那么看到的光盘内容也是这个样子。
值得注意的有两个目录。一个是images,在这个目录里包含了一些1.44MB软盘的镜像文件,其中bootdisk.img可以用来制作安装用启动软盘;另一个是dosutils,其中包含了一些DOS工具,比如rawrite.exe,用这个软件可以把软盘的镜像文件写到软盘里。
Linux启动安装法

这种方法需要制作一张Linux启动盘。
先把iso文件中images文件夹里的bootdisk.img读出来单独存储到硬盘上。
如果是在Linux下,就把软盘插入软驱,然后执行下面的命令把bootdisk.img写入软盘:
dd if=bootdisk.img of=/dev/fd0如果是在DOS/WINDOWS下,则还要把iso文件中dosutils文件夹里的rawrite.exe读出来并与 bootdisk.img保存在同一个目录下。然后在DOS提示符下执行rawrite,按提示插入软盘,将bootdisk.img写入软盘。
做好Linux启动盘后,用它引导启动计算机。当提示询问何种安装方式时,选择硬盘安装
方式(Hard Drive),并提供正确的iso文件位置信息,就能启动iso文件中的安装程序了。
按照安装程序的提示,就可以完成安装了。
在安装程序的引导下安装参见“启动虚拟主机安装Linux”。
posted @ 2006-05-31 22:51 edsonjava 阅读(331) | 评论 (0)编辑 收藏
 
轻松玩转VMware虚拟机(3)

应用以及技巧

在设置完和安装完虚拟机之后,我们需要开始考虑如何发挥其最大的作用。在笔者的使用过程中,发现对于一些初学者和玩家而言,极为有用和有趣的用途:

1.使用虚拟机虚拟老操作系统,在虚拟的系统中玩老游戏

这个作用也是朋友们使用最多的功能。由于最新的Windows XP操作系统已经完全取消了DOS,所以不少喜欢怀旧的朋友想要在新机器上运行老游戏的话,安装一个DOS系统是必不可少的。可是为了一个老游戏重新格式化安装系统的话,好像有些得不偿失,所以在此时,虚拟机就派上了用处。你只需要打开虚拟机,放入光盘或者软盘,简单设置和安装即可得到一个新的操作系统,这个虚拟的操作系统和你在物理机器上安装的操作系统是一摸一样的,你可以方便快捷地使用这个操作系统进行游戏。

2.使用虚拟机学习和体验新的操作系统和新技术

抛开发烧友和游戏来说,使用虚拟机的还有很大一部分用户都是需要采用虚拟机来模拟非当前操作系统或者比较复杂的网络环境,通过在虚拟的操作系统环境中进行操作,熟悉操作和新技术,来达到事半功倍的目的。VMware只是一个软件,可以帮助你在一个操作系统的环境下安装另一个操作系统,所以在安装操作系统之前,仍然需要对硬盘进行分区和格式化的操作。正因为此,我觉得虚拟机最好玩的地方在于,对于一个新手来说,这个东西可以让你学习如何对硬盘分区,并设置活动分区,然后格式化,然后装系统。像一些直接操作硬盘尤其是分区表和fat的软件,也可以用虚拟机来学习应用,这样不会对硬盘有什么大的伤害。同时由于VMware虚拟机软件允许同时装多个虚拟机,譬如说,主机的操作系统是Windows 2000,这样你可以再装一个Windows 98或Windows 2000,甚至Linux,这样就可以在Windows的环境下,一边用QQ,一边学习Linux了。

3.使用虚拟机来测试操作系统ISO文件

可能不少朋友都会觉得这个想法很奇怪,难道检测ISO文件不能使用别的办法而非要使用虚拟机吗?其实检测操作系统ISO的文件最简单的办法的确是使用VMware软件,因为如果想要检测该ISO文件的话,您需要把ISO文件刻盘,然后重新安装才能了解到刻出来的盘是否真的成功,程序文件是否完全,有无丢失,而当你使用了虚拟机软件之后,麻烦就少了许多,你现在完全可以使用虚拟机软件来检测ISO文件的好坏。首先使用VMware创建一个新的虚拟机,选择"Custom"然后选择"Windows XP Professional"作为操作系统。如果愿意,你可以编辑一下虚拟机的名称和保存位置。你也可以重新调整一下分配给虚拟机的内存大小,接着设置你的网络连接,这里我建议你选择"Do not use a network connection"因为它和我的网络连接有冲突,而且在这个测试中也没必要使用网络。选择"Create a new virtual disk",然后如果愿意你也可以重设一下虚拟机磁盘的大小,默认的大小就是可以使用的最大值。如果你仅仅是为了测试,用不了4GB,那么磁盘文件就不会占据4GB的空间,但如果虚拟机使用超过了4GB,软件会自动分配给虚拟机更多的磁盘空间。如果你想要测试OEM预安装模式,并且要使用6GB左右的空间,那么你最好在这里就调整得大一些。完成后,选择"Edit virtual machine settings",选中光驱,然后选择"Use ISO Image",选中你的ISO文件,点击“确定”,然后启动虚拟机,安装过程就开始了。等到安装结束后,你就得知这个刻录的ISO文件是否文件齐全,是否有错误了。

4.使用VMware虚拟机截图

可能很多朋友都不会想到这一点,但是这个好玩的做法的确存在。相信大家都看到过微软操作系统蓝屏的照片或者图片,大家在笑过之后是否会疑惑如果系统蓝屏了那我们该如何截图呢?如果有数码相机的话,很简单,只需要拍照就可以了。但是没有数码相机呢?那你就需要使用到VMware虚拟机了,所以大家见到的很多操作系统蓝屏和BIOS设置的图片都是使用VMware虚拟机截出来的图片。这下大家应该不再迷惑了,知道了那些图片是如何而来的了。

其实VMware虚拟机还有更多更有趣的玩法,这些都需要大家去慢慢摸索,笔者在这里介绍的也只是VMware虚拟机用途的冰山之一角,更多的好玩之处大家慢慢琢磨,相信大家会发现其拥有更多好玩和实用的功能。在这里笔者还要大家注意的就是VMware虚拟机软件的运行需要大量的资源,尤其是内存,不然会明显地感到机器很慢,机器一定要在256M内存以上才可安装使用,在这里也希望大家能快速上手。一台电脑装几十个系统?不是不可能嘛!

posted @ 2006-05-31 22:22 edsonjava 阅读(535) | 评论 (0)编辑 收藏
 
轻松玩转VMware虚拟机(2)

建立一个虚拟系统

双击桌面的“VMware”图标,即可进入VMware的主窗口,点击右侧的New Virtual Machine即可新建一个虚拟系统,之后选择Typical,再选择一种要安装的操作系统(如图2、图3)。

 

现在选择你建立的虚拟系统,点击上方工具栏中的POWER ON键便可开机了(如图4)。

其他按钮分别是POWER OFF:关机。Suspend:挂起,可以让虚拟机记录下当前虚拟系统的状态,下次可以用Resume重新恢复选择挂起时的运行状态,以便接着工作。Reset:重启,相当于物理计算机的RESET按钮。Full Screen:全屏,将虚拟机全屏显示。现在一台和真实计算机一样的机器已经建起来了,点击虚拟机的窗口,你的鼠标就融入虚拟系统了,你可以和使用一台真的计算机一样使用它了,而且任何设置都不会影响到你本机。如果想回到主机系统,则可以按Ctrl+Alt使鼠标回到主机系统。不要在虚拟机中使用Ctrl+Alt+Del组合键,因为主机系统同样也会对这个组合键做出反应,你应当使用Ctrl+Alt+Ins来代替。

虚拟机的启动过程和你的PC的启动过程也是没有什么不同的,一开始是自检,这时按F2可以进入BIOS设置。每一台虚拟机都有它自己的BIOS。

请到BIOS中去设置启动顺序(这里的BIOS当然是虚拟机中的BIOS)。之后便可使用光盘来启动安装(虚拟机可以用光盘镜像如:ISO、vcd文件作为光盘。比如从网上下载的Linux ISO文件,不需刻盘,可直接安装。软驱同理)。在光盘启动后要选择Text mode。实际上你即便是选择图形模式,安装程序检测后还是会跳到Text mode。而有时在检测的过程中会死机。在安装其他版本的Linux就没有这种情况。安装的时间可能比在一台单独的PC上安装的时间要略长。

你会发现在虚拟机中的设备和你实际的设备完全不一样,VMware为了保证系统的兼容性和稳定性,把现有的设备都虚拟成了最标准的、兼容性最好的设备。由于实际驱动设备的程序仍是在本机系统上运行的驱动程序,实际上的效率并没有多少降低。所以不要试图按照自己的机器配置系统。除此之外,在虚拟机中不用也不能安装任何驱动程序。(未完待续)

 

posted @ 2006-05-31 22:21 edsonjava 阅读(234) | 评论 (0)编辑 收藏
 
轻松玩转VMware虚拟机(1)

    笔者的一个朋友是个游戏狂人,他能够在Windows XP操作系统下装DOS玩DOS版的《红色警戒》、《三国志》和《大富翁》等游戏,甚至还为了一个日文的老游戏而装了日文版的DOS,他在电脑里安装了多个操作系统,甚至还使用了多重启动的管理软件来管理这些操作系统。其实不用这么麻烦,在一个操作系统平台上安装另外几个操作系统实在是太简单了。

    如果你厌倦了Windows,想尝试一下Linux,却害怕Linux的重新分区;也许你喜欢试一试各种软件,却讨厌重装系统;也许你已经安装了多个操作系统,可是当你需要切换操作系统的时候只能重新启动;也许你是个怀旧的游戏玩家,经常为操作系统不支持某游戏而恼火……那你一定要用VMware虚拟机,一切都会变得简单易行。

    VMware是VMware公司出品的一个多系统安装软件。利用它,你可以在一台电脑上将硬盘和内存的一部分拿出来虚拟出若干台机器,每台机器可以运行单独的操作系统而互不干扰,这些“新”机器各自拥有自己独立的CMOS、硬盘和操作系统,你可以像使用普通机器一样对它们进行分区、格式化、安装系统和应用软件等操作,还可以将这几个操作系统联成一个网络。在虚拟系统崩溃之后可直接删除不影响本机系统,同样本机系统崩溃后也不影响虚拟系统,可以下次重装后再加入以前做的虚拟系统。同时它也是唯一的能在Windows和Linux主机平台上运行的虚拟计算机软件。VMware虚拟机软件不需要重开机,就能在同一台电脑使用好几个OS,不但方便,而且安全。

    [点击下载VMware] http://software.pchome.net/system/sysenhance/4673.html

    安装虚拟平台

    在安装上没必要作过多的介绍,请大家注意的是VMware只能安装在WinNT/2000/XP或Linux,以及FreeBSD下。装好之后,你可以发现你多了两块名为VMware Virtual Ethernet Adapter (basic host-only support for VMnet1)和VMware Virtual Ethernet Adapter (Network Address Translation (NAT) for VMnet8)的虚拟网卡,这也是VMware的特色所在,因为在VMware下你可以使用虚拟网卡进行联网设置及其试验(如图1)。

posted @ 2006-05-31 22:19 edsonjava 阅读(174) | 评论 (0)编辑 收藏
 


网上有很多很多用js对表格进行排序的例子,比如说http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndude/html/dude07232001.asp
这个是微软的3个表格操作的例子,都做成了htc组件了
但是有个问题就是在mozilla等非IE浏览器下面都不能实现效果
于是去网上找了个例子,是可以进行在大部分浏览器下的排序的,可惜没有微软自己做的那么好,比如 显示目前是以何例何方式在排序的信息,对于此我特意改写了一下js代码,特意给出代码
注意是html和js混合的,没有分开,需要的可以自己分开来
经过我测试,效果良好:-)并且代码简洁易懂
以下是我改版国外的js后的代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--************************************************************************-->
<!--* Copyright 2002 by Mike Hall                                          *-->
<!--* Please see http://www.brainjar.com for terms of use.                 *-->
<!--************************************************************************-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>BrainJar.com: Table Sort</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link href="/common/article.css" rel="stylesheet" type="text/css" />

<script type="text/javascript">//<![CDATA[
var blankImg="blank.gif";
var downImg="down.gif";
var upImg="up.gif";
var imgElsrc=new Array();

function setImg(obj,col)
{
 for(var i=0;i<imgElsrc.length;i++)
 {
  var imgTemp = document.getElementById("img"+i);
  if(i!=col)
  {
   imgTemp.src=blankImg;
  }else
  {
   if (obj.reverseSort[col])
   imgElsrc[col] = downImg;
   else
   imgElsrc[col] = upImg;
   
   imgTemp.src=imgElsrc[col];
  }
 }/*alert(imgElsrc.length)*/
}
function sortTable2(col) {

  // Get the table section to sort.
  var tblEl = document.getElementById("planetData2");
  // Set up an array of reverse sort flags, if not done already.
  if (tblEl.reverseSort == null)
    tblEl.reverseSort = new Array();

  // If this column was the last one sorted, reverse its sort direction.
  if (col == tblEl.lastColumn)
    tblEl.reverseSort[col] = !tblEl.reverseSort[col];

  // Remember this column as the last one sorted.
  tblEl.lastColumn = col;

  //初始化图片的数组
  colLength=tblEl.rows[0].cells.length;
 if (imgElsrc.length<colLength)
 {
 imgElsrc=new Array(colLength);
 }
  // Set Image
  setImg(tblEl,col);

  // Set the table display style to "none" - necessary for Netscape 6
  // browsers.
  var oldDsply = tblEl.style.display;
  tblEl.style.display = "none";

  // Sort the rows based on the content of the specified column using a
  // selection sort.

  var tmpEl;
  var i, j;
  var minVal, minIdx;
  var testVal;
  var cmp;

  for (i = 0; i < tblEl.rows.length - 1; i++) {

    // Assume the current row has the minimum value.
    minIdx = i;
    minVal = getTextValue(tblEl.rows[i].cells[col]);

    // Search the rows that follow the current one for a smaller value.
    for (j = i + 1; j < tblEl.rows.length; j++) {
      testVal = getTextValue(tblEl.rows[j].cells[col]);
      cmp = compareValues(minVal, testVal);
      // Reverse order?
      if (tblEl.reverseSort[col])
        cmp = -cmp;
      // If this row has a smaller value than the current minimum, remember its
      // position and update the current minimum value.
      if (cmp > 0) {
        minIdx = j;
        minVal = testVal;
      }
    }

    // By now, we have the row with the smallest value. Remove it from the
    // table and insert it before the current row.
    if (minIdx > i) {
      tmpEl = tblEl.removeChild(tblEl.rows[minIdx]);
      tblEl.insertBefore(tmpEl, tblEl.rows[i]);
    }
  }

  // Restore the table's display style.
  tblEl.style.display = oldDsply;

  return false;
}

//-----------------------------------------------------------------------------
// Functions to get and compare values during a sort.
//-----------------------------------------------------------------------------

// This code is necessary for browsers that don't reflect the DOM constants
// (like IE).
if (document.ELEMENT_NODE == null) {
  document.ELEMENT_NODE = 1;
  document.TEXT_NODE = 3;
}

function getTextValue(el) {

  var i;
  var s;

  // Find and concatenate the values of all text nodes contained within the
  // element.
  s = "";
  for (i = 0; i < el.childNodes.length; i++)
    if (el.childNodes[i].nodeType == document.TEXT_NODE)
      s += el.childNodes[i].nodeValue;
    else if (el.childNodes[i].nodeType == document.ELEMENT_NODE &&
             el.childNodes[i].tagName == "BR")
      s += " ";
    else
      // Use recursion to get text within sub-elements.
      s += getTextValue(el.childNodes[i]);

  return normalizeString(s);
}

function compareValues(v1, v2) {

  var f1, f2;

  // If the values are numeric, convert them to floats.

  f1 = parseFloat(v1);
  f2 = parseFloat(v2);
  if (!isNaN(f1) && !isNaN(f2)) {
    v1 = f1;
    v2 = f2;
  }

  // Compare the two values.
  if (v1 == v2)
    return 0;
  if (v1 > v2)
    return 1
  return -1;
}

// Regular expressions for normalizing white space.
var whtSpEnds = new RegExp("^\\s*|\\s*$", "g");
var whtSpMult = new RegExp("\\s\\s+", "g");

function normalizeString(s) {

  s = s.replace(whtSpMult, " ");  // Collapse any multiple whites space.
  s = s.replace(whtSpEnds, "");   // Remove leading or trailing white space.

  return s;
}

//]]></script>
</head>
<body>

<div id="main">


<p>Let's try it out. Here's another table with the same planet data. But this
one uses the modified version of <code>sortTable().</code></p>

<table border="1">
  <caption style="caption-side:bottom;">The Planets</caption>
  <thead>
    <tr style="vertical-align:bottom">
      <th style="text-align:left"  ><a href="" onclick="return sortTable2(0)">Name</a><image id="img0" src="blank.gif"></th>
      <th style="text-align:center"><a href="" onclick="return sortTable2(1)">Diameter (mi.)</a><image id="img1" src="blank.gif"></th>
      <th style="text-align:center"><a href="" onclick="return sortTable2(2)">Distance<br />(mi. x 1000000)</a><image id="img2" src="blank.gif"></th>
      <th style="text-align:center"><a href="" onclick="return sortTable2(3)">Rotation (hrs.)</a><image id="img3" src="blank.gif"></th>
      <th style="text-align:center"><a href="" onclick="return sortTable2(4)">Period (days)</a><image id="img4" src="blank.gif"></th>
    </tr>
  </thead>
  <tbody id="planetData2">
    <tr>
      <td style="text-align:left">Mercury</td>
      <td style="text-align:right">3032</td>
      <td style="text-align:right">36.0</td>
      <td style="text-align:right">1407.6</td>
      <td style="text-align:right">88</td>
    </tr>
    <tr>
      <td style="text-align:left">Venus</td>
      <td style="text-align:right">7521</td>
      <td style="text-align:right">67.2</td>
      <td style="text-align:right">5832.5</td>
      <td style="text-align:right">225</td>
    </tr>
    <tr>
      <td style="text-align:left">Earth</td>
      <td style="text-align:right">7926</td>
      <td style="text-align:right">93.0</td>
      <td style="text-align:right">23.9</td>
      <td style="text-align:right">365</td>
    </tr>
    <tr>
      <td style="text-align:left">Mars</td>
      <td style="text-align:right">4222</td>
      <td style="text-align:right">141.6</td>
      <td style="text-align:right">24.6</td>
      <td style="text-align:right">687</td>
    </tr>
    <tr>
      <td style="text-align:left">Jupiter</td>
      <td style="text-align:right">88846</td>
      <td style="text-align:right">483.8</td>
      <td style="text-align:right">9.9</td>
      <td style="text-align:right">4331</td>
    </tr>
    <tr>
      <td style="text-align:left">Saturn</td>
      <td style="text-align:right">74897</td>
      <td style="text-align:right">890.8</td>
      <td style="text-align:right">10.7</td>
      <td style="text-align:right">10747</td>
    </tr>
    <tr>
      <td style="text-align:left">Uranus</td>
      <td style="text-align:right">31763</td>
      <td style="text-align:right">1784.8</td>
      <td style="text-align:right">17.2</td>
      <td style="text-align:right">30589</td>
    </tr>
    <tr>
      <td style="text-align:left">Neptune</td>
      <td style="text-align:right">30775</td>
      <td style="text-align:right">2793.1</td>
      <td style="text-align:right">16.1</td>
      <td style="text-align:right">59800</td>
    </tr>
    <tr>
      <td style="text-align:left">Pluto</td>
      <td style="text-align:right">1485</td>
      <td style="text-align:right">3647.2</td>
      <td style="text-align:right">153.3</td>
      <td style="text-align:right">90588</td>
    </tr>
  </tbody>
</table>

<p>You can now change the sort direction of each column independently.</p>

<p>The sort function can be modified in other ways to better accommodate both
the table data and its presentation. We'll look at some ideas next.</p>

<p class="footer"><a href="default5.asp">Prev</a>
|
<a href="default7.asp">Next</a></p>
</div>

</body>
</html>
主要增加了图片显示状态的功能,即增加函数setImg

另外还提供一个非常简单的js排序函数,但是好像只可以在IE下使用,代码如下
/*表格排序是对表格而言的,与表格的生成无关。就是说,无论用何种方式产生的表格排序时都一样。这是排序函数,很简单。
分升序和降序,要求传入参数:obj 表格名或id; n 排序列,第一列为0*/
function table_sort_asc(obj,n) {
  var i,j;
  var tab = eval("document.all."+obj);
  if(tab.tagName != "TABLE") return;
  for(i=0;i<tab.rows.length-1;i++)
 for(j=i+1;j<tab.rows.length;j++)
  if(tab.rows[i].cells[n].innerHTML > tab.rows[j].cells[n].innerHTML)
   tab.rows[i].swapNode(tab.rows[j]);
}

function table_sort_desc(obj,n) {
  var i,j;
  var tab = eval("document.all."+obj);
  if(tab.tagName != "TABLE") return;
  for(i=0;i<tab.rows.length-1;i++)
 for(j=i+1;j<tab.rows.length;j++)
  if(tab.rows[i].cells[n].innerHTML < tab.rows[j].cells[n].innerHTML)
   tab.rows[i].swapNode(tab.rows[j]);
}

除了用js来进行排序外,还可以用xml+xslt进行排序,但是还是有浏览器不兼容的问题,比较麻烦,我搜集了一些网上常用的表格排序的例子,现在放在网站上,提供下载

posted @ 2006-05-30 17:55 edsonjava 阅读(964) | 评论 (0)编辑 收藏
 

Fun with Tables

Dave Massy
Microsoft Corporation

July 24, 2001

Ever find yourself staring at a table of statistical data, straining to fathom its full implications? If so, you're not alone. A static table of data displayed on the screen doesn't always lend itself to ready comprehension. There are, however, some cool, dynamic behaviors you can apply to standard HTML tables that will allow your users to see the data in a variety of ways. These behaviors not only add interest, they can help considerably in reading and comparing data in a table. This month we'll take a look at these behaviors, and how you can use them to make your tables communicate—dynamically.

Before we look at these behaviors in detail, let's recall what a behavior is and the benefits it can bring to a DHTML page. The best way to think of a behavior is as a reusable DHTML component. There are two types of behavior: attached behaviors and element behaviors. Attached behaviors were introduced in Microsoft® Internet Explorer 5.0 and can be attached to any element in the HTML document using cascading style sheets (CSS) to add to the functionality of that element. Element behaviors were introduced in Internet Explorer 5.5, and take the concept further with the ability to implement your own custom elements for use in an HTML Document. For more information, see DHTML Behaviors.

The behaviors we'll look at in this article are all attached behaviors, so they will work in Internet Explorer 5.0 or later, and are implemented as HTML components, often referred to as HTC files. Because they are implemented as HTC files, they can be deployed on a server alongside the HTML files that use them, without any installation on the client machine. We will look at three behaviors: a highlighter, a sort function, and a column drag behavior. All three are written as HTML Component (HTC) files and therefore require no additional installation. They simply need to be placed on the server along with the HTML files. The files are published here, so you can take a close look at how they work (in any text editor), and modify them if you wish to change or add new functionality.

Note   These behaviors will work in Microsoft Internet Explorer for Windows version 5.0 or later. Because they are attached behaviors, when viewed in other browsers, users will see the original table displayed as it was authored, without the added dynamic functionality.

Highlighter

The first behavior we'll look at is the highlighter.

View the code.

View the sample.

First, run the qualhl.htm file and you'll see a standard table of data. Now mouse over some of the rows of data; the rows highlight, visually aligning the data on one row. Now click on the row; the clicked row remains highlighted in a different color.

Utilizing this behavior is simple. Here's the relevant code from the HTML file:

<TABLE style="behavior:url(tablehl.htc);" slcolor='#FFFFCC' hlcolor='#BEC5DE' >

The table tag includes a style that attaches the behavior by pointing to the tablehl.htc file, which adds the highlighting functionality. The behavior takes note of two attributes: the hlcolor, which defines the color of the mouseover highlighting, and the slcolor, which defines the color of the row selected by the mouse click. There is also an event that fires on the table called onrowselect when the mouse is clicked on a particular row. You can handle the event in your own script on the page if you wish.

I won't go into the details of how the behavior itself works, but if you take a look at the .htc file in any text editor, you can see that it changes the background color of the rows of the table appropriately through the use of runtimeStyle, and exposes the properties at the top of the file.

Sorter

The sorter behavior does something a little more interesting, as it allows sorting to take place on the head of the table, based on the column contents.

View the code.

View the sample.

By running the qualsort.htm, you can see that with this behavior attached to the table, clicking any column head in the table results in the content sorting accordingly. Attaching this behavior to the table is equally simple:

<TABLE style="behavior:url(sort.htc);" >

It does require that the table be well formed using THEAD to define the column head, and TBODY to contain the rest of the table data.

The behavior itself enacts a simple sort algorithm, but uses the DHTML Object Model to adjust the content of the HTML table to the appropriate order when a head is clicked. We also make use of some small image files to indicate at the top of the column the sort key and the direction of the sort.

This ability to sort data in tables is helpful in making sense of complex sets of statistics.

Drag

The third behavior I want to show here for tables is a little more fun.

View the code.

View the sample.

If you open qualdrag.htm, you will find that you can drag any of the heads to another column, thus rearranging the table.

Once again, attaching this behavior to a standard table that uses a THEAD and a TBODY is easy:

<TABLE style="behavior:url(sort.htc);" >

The behavior exposes a dragcolor attribute, so the color of the target can be specified.

Since an HTML table is laid out in row order, and not in column order, the manipulations of the content of the table are a little more complex in this behavior than with the sort functionality.

Together

Because these behaviors have been written as separate components, they can be combined on the same table by using the following syntax:

<TABLE style="behavior:url(dragdrop.htc) url(tablehl.htc) url(sort.htc);" >

View the sample.

This CSS syntax allows complimentary behaviors to be used on the same tag without separating out different elements. Further, if you are always going to use these behaviors together, we can combine their functionality into one single .htc file.

View the code.

View the sample.

If you open Qual.htm, you will find all three behaviors attached to the table.

Make Your Own Modifications

These behaviors, which came from an IE team demonstration, show the power of DHTML and attached behaviors, and are a great example of how you can add functionality to an ordinary HTML page and still have it display fine in older browsers. You are free to make use of these behaviors in your own pages, and I encourage you to make your own modifications as well. If you do make any interesting modifications, let me know at DHTMLDud@Microsoft.com. (Please understand, though, that I can't guarantee a response to every submission).

 


DHTML Dude

David Massy occasionally wears sun glasses and pretends to be a dude, but when the dark glasses are removed, he works as a technical evangelist on Windows and Internet Explorer technologies, which means he talks to customers of Microsoft about how they might best use the technologies. Before becoming a technical evangelist, Dave worked on the Internet Explorer team as a program manager. Since Dave is originally from England, he is valiantly struggling to educate his American colleagues on how to pronounce tomato correctly.

posted @ 2006-05-30 17:54 edsonjava 阅读(245) | 评论 (0)编辑 收藏
 
1.1描写properties文件。
这个描述文件一般放在可以放在两个地方:(1)WEB-INF/classes目录下,或者放在在/project_root/,也就是你的web_app的根目录下。利用这个控制日志纪录的配置,当然也可以通过xml文件配置,这里我们暂且说说properties的配置。
建立文件名log4j.properties文件。放在%tomca_home%/web_app/fcxtest/目录下。
文件内容如下:(fcxtest为自己建立的web app目录)

#--------------------------------
# 设定logger的root level为DEBUG,指定的输出目的地(appender)为A1
log4j.rootLogger=DEBUG, A1

# 设定调试信息的输出位置,此处设定输出为控制台
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# 设定调试信息的输出位置,此处设定输出为fcxtest.log文件
# log4j.appender.A1=org.apache.log4j.RollingFileAppender
# log4j.appender.A1.File=fcxtest.log
# log4j.appender.A1.MaxFileSize=1000KB

# 设定制定的A1使用的PatternLayout.
# 有关ConversionPattern中的转意字符的含义参考说明
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p [%t] %C{2} (%F:%L) - %m%n
#--------------------------------

1.2建立测试用的Servlet类
这个测试的com.fcxlog.LogShow类主要是显示如何使用log4j。

package com.fcxlog;

import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.PropertyConfigurator;

public class LogShow extends javax.servlet.http.HttpServlet{

protected String configfile = "log4j.properties";

public void init() throws ServletException{
ServletContext sct = getServletContext();
System.out.println("[Log4j]: The Root Path: " + sct.getRealPath("/"));
//指定自己的properties文件
//以前使用的是BasicConfigurator.configure(),现在使用PropertyConfigurator替代
org.apache.log4j.PropertyConfigurator.configure(sct.getRealPath("/") + configfile);
}

public void service(javax.servlet.http.HttpServletRequest req,javax.servlet.http.HttpServletResponse res){

//初始化Logger,以前使用Category,现在改用Logger替代
//org.apache.log4j.Category log = org.apache.log4j.Category.getInstance(LogShow.class);
org.apache.log4j.Logger log = ora.apache.log4j.Logger.getLogger(LogShow.class);
log.info("调试信息");

}
}

1.3 测试了
至于如何测试,发布,就不说了。

在此说明:
(1)本篇可能错误不少,一方面是参考《Short introduction to log4j》着翻译了一点,有诸多言辞不妥之处,还望指正。一方面,凭借自己的理解,所以难免有不全的地方,还望各位补充。
(2)因为时间有限,每天只能写一点,本文主要是介绍有关Logger及Logger level相关概念的
(3)有关Log4j介绍(一),请参阅:http://www.javaren.com/bbs/cgi-bin/topic.cgi?forum=1&topic=12766&show=0#lastviewpost


概述:
本文主要是简要的介绍了Log4j的api,以及其一些特征和设计原理。它本身是一个开源的软件,允许开发者任意的操纵应用系统日志信息。Log4j的使用通过外部配置文件进行配置。

任何大型应用系统都有其自己的系统日志管理或跟踪的API,所以在1996年的时候,E.U. SEMPER项目组(http://www.semper.org/)也开发其自己的日志管理API,后来经过无数次的修改和补充,发展成了现在的log4j,一个给予java的日志管理工具包。有关最新的版本信息和源码,请访问http://jakarta.apache.org/log4j/docs/index.html
把纪录语句放在代码之中,是一种低端的调试方法,不过有时却不得不说是很有效,也是必需的。毕竟很多时候,调试器并不见得很适用。特别是在多线程应用程序(multithread application)或分布式应用程序(distributed application)

经验告诉我们,在软件开发生命周期中,日志系统是一个非常重要的组件。当然,日志系统也有其自身的问题,过多的日志纪录会降低系统运行的速度。

(一)Log4j的三个重要组件?? Loggers, Appenders, Layouts

这三个组件协同的工作,使得开发者能够依据信息类别和级别去纪录信息,并能够运行期间,控制信息记录的方式已经日志存放地点。

(二)记录器层次(Logger hierarchy)

几乎任何纪录日志的API得功能都超越了简单的System.out.print语句。允许有选择控制的输出日志信息,也就是说,某的时候,一些日志信息允许输出,而另一些则不允许输出。这就假设日志纪录信息之间是有分别的,根据开发者自己定义的选择标准,可以对日志信息加以分类。

纪录器的命名是依据实体的。下面有一段有点绕口的解释,我就直抄了,各位可以看看:(Name Hierarchy)A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger.
(形象的解释,比如存在记录器 a.b.c,记录器a.b,记录器a。那么a就是a.b的ancestor,而a.b就是a.b.c的parent,而a.b.c就是a.b的child)

根纪录器(root logger)是记录器层次的顶端。它有两个独特点:(1)总是存在的(2)能够被重新找回。可以通过访问类的静态方法 Logger.getRootLogger 重新得到。其他的纪录器通过访问静态方法 Logger.getLogger 被实例话或被得到,这个方法将希望获得的记录器的名称作为参数。一些Logger类的方法描述如下:
public class Logger {
// Creation & retrieval methods:
public static Logger getRootLogger();
public static Logger getLogger(String name);
// printing methods:
public void debug(Object message);
public void info(Object message);
public void warn(Object message);
public void error(Object message);
public void fatal(Object message);

// generic printing method:
public void log(Level l, Object message);
}

记录器被赋予级别,这里有一套预定的级别标准:DEBUG, INFO, WARN, ERROR and FATAL ,这些是在 org.apache.log4j.Level 定义的。你可以通过继承Level类定义自己的级别标准,虽然并不鼓励这么做。
如果给定的记录器没有被赋予级别,则其会从离其最近的拥有级别的ancestor处继承得到。如果ancestor也没有被赋予级别,那么就从根记录器继承。所以通常情况下,为了让所有的记录器最终都能够被赋予级别,跟记录器都会被预先设定级别的。比如我们在操作properties文件中,会写这么一句:log4j.rootLogger=DEBUG, A1 。实际上就这就指定了root Logger和root Logger level。

Appenders
Log4j允许记录信息被打印到多个输出目的地,一个输出目的地叫做Appender。目前的Log4j存在的输出目的地包括:控制台(Console),文件(File),GUI Componennt,Remote Socket Server,JMS,NT Event Logger,and Remote Unix Syslog daemons。
多个Appender可以绑定到一个记录器上(Logger)。

通过方法 addAppender(Logger.addAppender) 可以将一个Appender附加到一个记录器上。每一个有效的发送到特定的记录器的记录请求都被转送到那个与当前记录器所绑定的Appender上。(Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy),换句话说,Appender的继承层次是附加在记录器继承层次上的。举个例子:如果一个Console Appender被绑定到根记录器(root Logger),那么所有的记录请求都可以至少被打印到Console。另外,把一个file Appender绑定到记录器C,那么针对记录器C(或C的子孙)的记录请求都可以至少发送到Console Appender和file Appender。当然这种默认的行为方式可以跟改,通过设定记录器的additivity flag(Logger.setAdditivity)为false,从而可以使得Appender的不再具有可加性(Additivity)。
下面简要介绍一下Appender Additivity。

Appender Additivity:记录器C所记录的日志信息将被发送到与记录器C以及其祖先(ancestor)所绑定的所有Appender。
但是,如果记录器C的祖先,叫做P,它的additivity flag被设定为false。那么,记录信息仍然被发送到与记录器C及其祖先,但只到达P这一层次,包括P在内的记录器的所有Appender。但不包括P祖先的。
通常,记录器的additivity flag的被设置为true。


Layouts
这一块主要是介绍输出格式的。PatternLayout,Log4j标准的分配器,可以让开发者依照conversion patterns去定义输出格式。Conversion patterns有点像c语言的打印函数。
参看配置文件的java properties,如下面的两行:
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p [%t] %C{2} (%F:%L) - %m%n
第一行就指定了分配器,第二行则指定了输出的格式。
有关输出格式的定义可以参考/org/apache/log4j/PatternLayout.html
更基础的知识,可以到田贯升门诊室,另有一篇偏重基础的介绍~  
posted @ 2006-05-30 16:53 edsonjava 阅读(228) | 评论 (0)编辑 收藏
 
1.1准备工作
一。Tomcat已正确配置与使用。
二。软件下载:log4j------http://www.apache.org/dist/jakarta/log4j/jakarta-log4j-1.2.8.zip

1.2. Log4j简介

在强调可重用组件开发的今天,除了自己从头到尾开发一个可重用的日志操作类外,Apache为我们提供了一个强有力的日志操作包-Log4j。
Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
此外,通过Log4j其他语言接口,您可以在C、C++、.Net、PL/SQL程序中使用Log4j,其语法和用法与在Java程序中一样,使得多语言分布式系统得到一个统一一致的日志组件模块。而且,通过使用各种第三方扩展,您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。本文介绍的Log4j版本是1.2.8,怎样通过一个配置文件来灵活地进行配置,主要的应用平台是Tomcat4.

1.3。Log4j的配置。

首先到jakarta下载一个log4j的组件。把jakarta-log4j-1.2.8\dist\lib下的log4j-1.2.8.jar文件copy到classpath指定的目录下!可以是Tomcat的common\lib目录下,也可以是你需要用到log4j的application下的lib目录。
1.4在Application目录下的web.xml文件加入以后代码

log4j
com.apache.jakarta.log4j.Log4jInit

log4j
/WEB-INF/log4j.properties

1


这段代码的意思是说,在Tomcat启动时加载com.apache.jakarta.log4j.Log4jInit这个名叫Log4jInit.class这个类文件。其中Log4jInit.class的源代码如下

package com.apache.jakarta.log4j;
import org.apache.log4j.PropertyConfigurator;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Log4jInit extends HttpServlet {

public void init() {
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("log4j");
// if the log4j-init-file is not set, then no point in trying
System.out.println("................log4j start");
if(file != null) {
PropertyConfigurator.configure(prefix+file);
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res) {
}
}
这段代码很简单,可以看出,在加载的过程中,程序会读取/WEB-INF/log4j.properties这个文件
这个文件就是本文的重点,也就是log4j的配置文件。

# Set root logger level to DEBUG and its only appender to A1
#log4j中有五级logger
#FATAL 0
#ERROR 3
#WARN 4
#INFO 6
#DEBUG 7
#配置根Logger,其语法为:
#log4j.rootLogger = [ level ] , appenderName, appenderName, …
log4j.rootLogger=INFO, A1 ,R
#这一句设置以为着所有的log都输出
#如果为log4j.rootLogger=WARN, 则意味着只有WARN,ERROR,FATAL
#被输出,DEBUG,INFO将被屏蔽掉.
# A1 is set to be a ConsoleAppender.
#log4j中Appender有几层如控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
#ConsoleAppender输出到控制台
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 使用的输出布局,其中log4j提供4种布局. org.apache.log4j.HTMLLayout(以HTML表格形式布局)
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

log4j.appender.A1.layout=org.apache.log4j.PatternLayout
#灵活定义输出格式 具体查看log4j javadoc org.apache.log4j.PatternLayout
#d 时间 ....
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
#R 输出到文件 RollingFileAppender的扩展,可以提供一种日志的备份功能。
log4j.appender.R=org.apache.log4j.RollingFileAppender
#日志文件的名称
log4j.appender.R.File=log4j.log
#日志文件的大小
log4j.appender.R.MaxFileSize=100KB
# 保存一个备份文件
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.TTCCLayout
#log4j.appender.R.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

配置以这里就差不多了,如果你想更深入了解配置文件的各个细节,可以去查看docs。还有,在文章的最后面我们提供配置文件中一些主要的语法。下面我们来看看怎样在程序中使用log4j.

1.4 Log4j的使用。
使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:
public static Logger getLogger( String name),
必须在使用前要把这个类导入
import org.apache.log4j.Logger;

name一般是类文件的名字,如下:
static Logger logger = Logger.getLogger ("".class.getName () ) ;

您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:
logger.debug ( Object message ) ;
logger.info ( Object message ) ;
logger.warn ( Object message ) ;
logger.error ( Object message ) ;

为什么这里要分级别的呢?试想一下,我们在写程序的时候,为了调试程序,会在很多会出错的地方加入大量的logger.info();信息。当然程序调试完毕,我们不需要这些输出信息了,那怎么办呢?以前的做法是把每个程序中的logger.info删除,但这是不现实的,如果程序不大还可以,但如果程序很多,做这些事情就很烦人了。但因为log4j分级别了,当我们不需要输出这样调试时用到的log.info()时,我们可以把输出的级别调高,如调到warn,或error级别,这样info级别及以下的级别就不会出输出了,是不是很方便的呢?

其实除了这种使用方式,log4j还有其它的使用方面,不需要配置文件,直接在程序中定义输入出级别,层次等信息,如果要了解这方法的使用,可以参考文档。

1.5。附注:
以下是配置文件的一些重要的语法
定义配置文件

其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。

Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:

配置根Logger,其语法为:

log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。
appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。

配置日志信息输出目的地Appender,其语法为

log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1

log4j.appender.appenderName.option = valueN
其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

配置日志信息的格式(布局),其语法为:

log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1

log4j.appender.appenderName.layout.option = valueN
其中,Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

posted @ 2006-05-30 16:30 edsonjava 阅读(163) | 评论 (0)编辑 收藏
 
log4j在web端的使用技巧!

log4j的好处在于:

1.

通过修改配置文件,就可以决定log信息输出到何处(console,文件,...),是否输出。
这样,在系统开发阶段可以打印详细的log信息以跟踪系统运行情况,而在系统稳定后可以关闭log输出,从而在能跟踪系统运行情况的同时,又减少了垃圾代码(System.out.println(...)等)。

2.

使用log4j,需要整个系统有一个统一的log机制,有利于系统的规划。

log4j的使用本身很简单。但合理地规划一个系统的统一log机制需要周全的考虑。

其他关于log4j的信息参看log4j自带的文档。

PART II 配置文件详细解释
先看一个配置文件的例子:

1.

配置文件的例子

log4j.rootLogger=DEBUG
#将DAO层log记录到DAOLog,allLog中
log4j.logger.DAO=DEBUG,A2,A4
#将逻辑层log记录到BusinessLog,allLog中
log4j.logger.Businesslog=DEBUG,A3,A4

#A1--打印到屏幕上
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-5p [%t] %37c %3x - %m%n

#A2--打印到文件DAOLog中--专门为DAO层服务log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A2.file=DAOLog
log4j.appender.A2.DatePattern='.'yyyy-MM-dd
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

#A3--打印到文件BusinessLog中--专门记录逻辑处理层服务log信息log4j.appender.A3=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A3.file=BusinessLog
log4j.appender.A3.DatePattern='.'yyyy-MM-dd
log4j.appender.A3.layout=org.apache.log4j.PatternLayout
log4j.appender.A3.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

#A4--打印到文件alllog中--记录所有log信息log4j.appender.A4=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A4.file=alllog
log4j.appender.A4.DatePattern='.'yyyy-MM-dd
log4j.appender.A4.layout=org.apache.log4j.PatternLayout
log4j.appender.A4.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

2.

Appender的使用
一个Appender代表log信息要写向的一个地方。log4j可使用的Appender有很多类型,这里只考虑3种:ConsoleAppender,FileAppender,DailyRollFileAppender
2.1
 ConsoleAppender
如果使用ConsoleAppender,那么log信息将写到Console。就是直接把信息打印到System.out上了。
2.2 
FileAppender
使用FileAppender,那么log信息将写到指定的文件中。这应该是比较经常使用到的情况。
相应地,在配置文件中应该指定log输出的文件名。如下配置指定了log文件名为demo.txt
log4j.appender.A2.File=demo.txt
注意将A2替换为具体配置中Appender的别名。
2.3
 DailyRollingAppender
使用FileAppender可以将log信息输出到文件中,但是如果文件太大了读起来就不方便了。这时就可以使用DailyRollingAppender。DailyRollingAppender可以把Log信息输出到按照日期来区分的文件中。如下配置文件就会每天产生一个log文件,每个log文件只记录当天的log信息:

  1. log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender
  2. log4j.appender.A2.file=demo
  3. log4j.appender.A2.DatePattern='.'yyyy-MM-dd
  4. log4j.appender.A2.layout=org.apache.log4j.PatternLayout
  5. log4j.appender.A2.layout.ConversionPattern=%m%n


3.

Layout的配置
Layout指定了log信息输出的样式。
详细信息请查看PatternLayout的javadoc。
例子1:显示日期和log信息
  1. log4j.appender.A2.layout=org.apache.log4j.PatternLayout
  2. log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %m%n

打印的信息是:
2002-11-12 11:49:42,866 SELECT * FROM Role WHERE 1=1 order by createDate desc

例子2:显示日期,log发生地方和log信息
  1. log4j.appender.A2.layout=org.apache.log4j.PatternLayout
  2. log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %l "#" %m%n
  3. 2002-11-12 11:51:46,313 cn.net.unet.weboa.system.dao.RoleDAO.select(RoleDAO.java:409) "#" SELECT * FROM Role WHERE 1=1 order by createDate desc 

例子3:显示log级别,时间,调用方法,log信息
  1. log4j.appender.A2.layout=org.apache.log4j.PatternLayout
  2. log4j.appender.A2.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%nlog信息:[DEBUG] 2002-11-12 12:00:57,376 method:cn.net.unet.weboa.system.dao.RoleDAO.select(RoleDAO.java:409)
  3. SELECT * FROM Role WHERE 1=1 order by createDate desc 


PART 3 log4j的使用
log4j使用步骤有3个:
3.1
.根据配置文件初始化log4j
配置文件如PART 2所叙述。现在讲的是如何在程序中配置log4j。
log4j可以使用3中配置器来初始化:BasicConfigurator,DOMConfigurator,PropertyConfigurator
这里用的是PropertyConfigurator。使用PropertyConfigurator适用于所有的系统。
如下的语句
PropertyConfigurator.configure("log4j.properties");
就以log4j.properties为配置文件初始化好了log4j环境。
注意一点:这个语句只需要在系统启动的时候执行一次。例如:在unet webOA项目中可以这么用:
在ActionServlet的init()方法中调用一次。
public class ActionServlet extends HttpServlet{
/*** Initialize global variables*/
public void init() throws ServletException {
// 初始化Action资源
try{initLog4j();...}
catch(IOException e)
{throw new ServletException("Load ActionRes is Error");}}
protected void initLog4j(){PropertyConfigurator.configure("log4j.properties");}
}
//end class ActionServlet

3.2 
在需要使用log4j的地方获取Logger实例
如下是RoleDAO类中的使用例子:
static Logger log = Logger.getLogger("DAO");
注意这里使用"DAO"标识符,那么对应的在配置文件中对应的配置信息如下:

#定义DAO Logger
log4j.logger.DAO=DEBUG,A2
#设置Appender A2的属性
log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A2.file=demolog4j.appender.A2.DatePattern='.'yyyy-MM-ddlog4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss} %l%n%m%n
public class RoleDAO extends BaseDBObject{...static Logger log = Logger.getLogger("DAO");
public BeanCollection selectAll() throws SQLException{
StringBuffer sql = new StringBuffer(SQLBUF_LEN);sql.append("SELECT * FROM " + tableName + " order by roldId");
//System.out.println(sql.toString());log.debug(sql);
}
}

3.3 
使用Logger对象的debug,info,fatal...方法
log.debug("it is the debug info");

附件1:log4j的一个bug
当这样使用时,DailyRollingFileAppender不能正确使用:
public Class RoleDAO(){
static Logger log = Logger.getLogger("DAO");
//在每一次new RoleDAO对象的时候都执行一次configure()操作
public RoleDAO(TransactionManager transMgr) throws SQLException{...PropertyConfigurator.configure("log4j.properties");}
public void select(){...//使用log4j进行log记录log.debug("...");
}
}

 1. 不需要显式编码去初始化 log4j,只要你的配置文件放在 CLASSPATH的目录下就可以了.
 2.用common log而不要直接用log4j的 Logger logger = XXX, 例子如下:
   在类 ThisIsMyClass 的代码中:
   private static Log log = LogFactory.getLog(ThisIsMyClass.class);

   log4j 就会自动用步骤1所述方法初始化.

   用ThisIsMyClass.class作为参数去取得一个Log对象,
   好处就是可以更灵活地在 log4j 配置文件中配置相应的 appender 和 logger 
posted @ 2006-05-30 16:23 edsonjava 阅读(311) | 评论 (0)编辑 收藏
 
基于 Java 开发的论坛,最有名的当属 Jive ,国产的 Jute 也很不错。好是好,但需要花费银子才能用。对于企业,应该考虑使用Jute,因为其提高良好的技术支持。对于个人,或许就不值得了。这里推荐开源项目 mvnForum, 基于 J2EE technology (Jsp/Servlet).

mvnFourm is free, opensource and released under the terms of the GNU General Public License. It means that you could use it free of charge to build your own discussion communities.

一、简介

mvnForum 基于Jsp/Servlet开发,支持Jsp 1.2 和 Servlet 2.3,安装和使用都非常简单。

    主要特征:

  • 基于 MVC 架构
  • 内建数据库连接池
  • 多种数据库 (DB2, MySQL, Oracle 8i/9i, Sql Server, postgreSQL, hsqldb, Interbase/Firebird, SAPDB)
  • 国际化 (支持14种语言: English, 简体, 繁体等等)
  • Jakarta Common Logging
这里可以查看论坛功能和全部特征。

二、安装环境

  参考
服务器:tomcat 4.1.27 http://jakarta.apache.org/tomcat/index.html
数据库:PostgreSQL 7.3.3 http://www.postgresql.org

三、安装

1、下载mvnForum 1.0.0 RC 1,如果需要源码,可以下载mvnForum 1.0.0 RC 1 Source
2、将mvnforum-1.0.0-rc1.zip截压缩到mvnforum-1.0.0-rc1目录。把mvnforum-1.0.0-rc1/webapp目录复制到tomcat/webapps/目录下,然后将webapp改名为mvnforum(可根据需要改成任意的名称)。接着把mvnforum-1.0.0-rc1/driver/postgresql.jar复制到tomcat/webapps/mvnforum/WEB-INF/lib/目录下。
3、建立论坛数据库表。首先在PostgreSQL创建一数据库mvnForum,执行命令如下:
CREATEDB -E unicode mvnForum
接着执行mvnforum-1.0.0-rc1/sql/mvnForum_postgresql.sql文件,创建表,执行命令如下:
#psql mvnForum
mvnForum=#\i mvnForum_postgresql.sql
4、修改tomcat/webapps/mvnforum/WEB-INF/class/mvncore_db_DBOptions.properties文件,这个文件配置与数据库相关的资料。设置内容如下:
DRIVER_CLASS_NAME = org.postgresql.Driver
DATABASE_URL = jdbc:postgresql://192.168.0.10:5432/mvnForum
DATABASE_USER = postgres
DATABASE_PASSWORD =
5、基本安装完成,启动Tomcat,通过http://localhost:8080/mvnforum/index.jsp就可以访问论坛了。
6、管理员管理,http://localhost:8080/mvnforum/mvnforumadmin/index。用户名和密码都是admin,管理员登陆后可以增加版面和管理用户了。

posted @ 2006-05-30 15:28 edsonjava 阅读(253) | 评论 (0)编辑 收藏
 
<object id="player" height="64" width="260" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">
    <param NAME="AutoStart" VALUE="-1">
    <!--是否自动播放-->
    <param NAME="Balance" VALUE="0">
    <!--调整左右声道平衡,同上面旧播放器代码-->
    <param name="enabled" value="-1">
    <!--播放器是否可人为控制-->
    <param NAME="EnableContextMenu" VALUE="-1">
    <!--是否启用上下文菜单-->
    <param NAME="url" VALUE="http://download.xaonline.com/music/huangpy/lover/HPY01.wma">
    <!--播放的文件地址-->
    <param NAME="PlayCount" VALUE="1">
    <!--播放次数控制,为整数-->
    <param name="rate" value="1">
    <!--播放速率控制,1为正常,允许小数,1.0-2.0-->
    <param name="currentPosition" value="0">
    <!--控件设置:当前位置-->
    <param name="currentMarker" value="0">
    <!--控件设置:当前标记-->
    <param name="defaultFrame" value="">
    <!--显示默认框架-->
    <param name="invokeURLs" value="0">
    <!--脚本命令设置:是否调用URL-->
    <param name="baseURL" value="">
    <!--脚本命令设置:被调用的URL-->
    <param name="stretchToFit" value="0">
    <!--是否按比例伸展-->
    <param name="volume" value="50">
    <!--默认声音大小0%-100%,50则为50%-->
    <param name="mute" value="0">
    <!--是否静音-->
    <param name="uiMode" value="mini">
    <!--播放器显示模式:Full显示全部;mini最简化;None不显示播放控制,只显示视频窗口;invisible全部不显示-->
    <param name="windowlessVideo" value="0">
    <!--如果是0可以允许全屏,否则只能在窗口中查看-->
    <param name="fullScreen" value="0">
    <!--开始播放是否自动全屏-->
    <param name="enableErrorDialogs" value="-1">
    <!--是否启用错误提示报告-->
    <param name="SAMIStyle" value>
    <!--SAMI样式-->
    <param name="SAMILang" value>
    <!--SAMI语言-->
    <param name="SAMIFilename" value>
    <!--字幕ID-->
</object>

1.avi格式

<object id="video" width="400" height="200" border="0" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA">
<param name="ShowDisplay" value="0">
<param name="ShowControls" value="1">
<param name="AutoStart" value="1">
<param name="AutoRewind" value="0">
<param name="PlayCount" value="0">
<param name="Appearance value="0 value=""">
<param name="BorderStyle value="0 value=""">
<param name="MovieWindowHeight" value="240">
<param name="MovieWindowWidth" value="320">
<param name="FileName" value="file:///D|/work/vod/Mbar.avi">
<embed width="400" height="200" border="0" showdisplay="0" showcontrols="1" autostart="1" autorewind="0" playcount="0" moviewindowheight="240" moviewindowwidth="320" filename="file:///D|/work/vod/Mbar.avi" src="Mbar.avi">
</embed>
</object>

2.mpg格式

<object classid="clsid:05589FA1-C356-11CE-BF01-00AA0055595A" id="ActiveMovie1" width="239" height="250">
<param name="Appearance" value="0">
<param name="AutoStart" value="-1">
<param name="AllowChangeDisplayMode" value="-1">
<param name="AllowHideDisplay" value="0">
<param name="AllowHideControls" value="-1">
<param name="AutoRewind" value="-1">
<param name="Balance" value="0">
<param name="CurrentPosition" value="0">
<param name="DisplayBackColor" value="0">
<param name="DisplayForeColor" value="16777215">
<param name="DisplayMode" value="0">
<param name="Enabled" value="-1">
<param name="EnableContextMenu" value="-1">
<param name="EnablePositionControls" value="-1">
<param name="EnableSelectionControls" value="0">
<param name="EnableTracker" value="-1">
<param name="Filename" value="../../../mpeg/halali.mpg" valuetype="ref">
<param name="FullScreenMode" value="0">
<param name="MovieWindowSize" value="0">
<param name="PlayCount" value="1">
<param name="Rate" value="1">
<param name="SelectionStart" value="-1">
<param name="SelectionEnd" value="-1">
<param name="ShowControls" value="-1">
<param name="ShowDisplay" value="-1">
<param name="ShowPositionControls" value="0">
<param name="ShowTracker" value="-1">
<param name="Volume" value="-480">
</object>


3.smi格式

<OBJECT id=RVOCX classid=clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA width=240
height=180>
<param name="_ExtentX" value="6350">
<param name="_ExtentY" value="4763">
<param name="AUTOSTART" value="-1">
<param name="SHUFFLE" value="0">
<param name="PREFETCH" value="0">
<param name="NOLABELS" value="-1">
<param name="SRC" value="rm.rm">
<param name="CONTROLS" value="ImageWindow">
<param name="CONSOLE" value="console1">
<param name="LOOP" value="0">
<param name="NUMLOOP" value="0">
<param name="CENTER" value="0">
<param name="MAINTAINASPECT" value="0">
<param name="BACKGROUNDCOLOR" value="#000000"><embed src="real.smi" type="audio/x-pn-realaudio-plugin" console="Console1" controls="ImageWindow" height="180" width="240" autostart="true"></OBJECT>

4.rm格式

<OBJECT ID=video1 CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" HEIGHT=288 WIDTH=352>

<param name="_ExtentX" value="9313">
<param name="_ExtentY" value="7620">
<param name="AUTOSTART" value="0">
<param name="SHUFFLE" value="0">
<param name="PREFETCH" value="0">
<param name="NOLABELS" value="0">
<param name="SRC" value="rtsp://203.207.131.35/vod/dawan-a.rm";>
<param name="CONTROLS" value="ImageWindow">
<param name="CONSOLE" value="Clip1">
<param name="LOOP" value="0">
<param name="NUMLOOP" value="0">
<param name="CENTER" value="0">
<param name="MAINTAINASPECT" value="0">
<param name="BACKGROUNDCOLOR" value="#000000"><embed SRC type="audio/x-pn-realaudio-plugin" CONSOLE="Clip1" CONTROLS="ImageWindow" HEIGHT="288" WIDTH="352" AUTOSTART="false">

</OBJECT>

5.wmv格式

<object id="NSPlay" width=200 height=180 classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,5,715" standby="Loading Microsoft Windows Media Player components..." type="application/x-oleobject" align="right" hspace="5">
<param name="AutoRewind" value=1>
<param name="FileName" value="xxxxxx.wmv">
<param name="ShowControls" value="1">
<param name="ShowPositionControls" value="0">
<param name="ShowAudioControls" value="1">
<param name="ShowTracker" value="0">
<param name="ShowDisplay" value="0">
<param name="ShowStatusBar" value="0">
<param name="ShowGotoBar" value="0">
<param name="ShowCaptioning" value="0">
<param name="AutoStart" value=1>
<param name="Volume" value="-2500">
<param name="AnimationAtStart" value="0">
<param name="TransparentAtStart" value="0">
<param name="AllowChangeDisplaySize" value="0">
<param name="AllowScan" value="0">
<param name="EnableContextMenu" value="0">
<param name="ClickToPlay" value="0">
</object>

posted @ 2006-05-30 12:51 edsonjava 阅读(196) | 评论 (0)编辑 收藏
 

java中提供了io类库,可以轻松的用java实现对文件的各种操作。下面就来说一下如何用java来实现这些操作。
 
新建目录

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%
  3. //String URL = request.getRequestURI();
  4. String filePath="C:\\测试\\";
  5. filePath=filePath.toString();//中文转换
  6. java.io.File myFilePath=new java.io.File(filePath);
  7. if(!myFilePath.exists())
  8. myFilePath.mkdir();
  9. %>

新建文件

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%@ page import="java.io.*" %>
  3. <%
  4. String filePath="c:/测试/newFile.txt";
  5. filePath=filePath.toString();
  6. File myFilePath=new File(filePath);
  7. if(!myFilePath.exists())
  8. myFilePath.createNewFile();
  9. FileWriter resultFile=new FileWriter(myFilePath);
  10. PrintWriter myFile=new PrintWriter(resultFile);
  11. String content ="这是测试数据";
  12. String strContent = content.toString();
  13. myFile.println(strContent);
  14. resultFile.close();
  15. %>


删除文件

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%
  3. String filePath="c://测试//newFile.txt";
  4. filePath=filePath.toString();
  5. java.io.File myDelFile=new java.io.File(filePath);
  6. if(myDelFile.exists())
  7. {  
  8.     myDelFile.delete();
  9.     out.println(filePath+"删除成功!!!");
  10. }
  11. else
  12. {
  13.     out.println(filePath+"该文件不存在");
  14. }
  15. %>

文件拷贝

  1. <%@ page contentType="text/html; charset=gb2312" %>
  2. <%@ page import="java.io.*" %>
  3. <%
  4. int bytesum=0;
  5. int byteread=0;
  6. //file:读到流中
  7. InputStream inStream=new FileInputStream("c://测试//newFile.txt");
  8. FileOutputStream fs=new FileOutputStream( "c://测试//copyFile.txt");
  9. byte[]  buffer =new  byte[1444];
  10. int length;
  11. while ((byteread=inStream.read(buffer))!=-1)
  12.  {
  13.    out.println("<DT><B>"+byteread+"</B></DT>");
  14.    bytesum+=byteread;
  15.    out.println(bytesum);
  16.    fs.write(buffer,0,byteread);
  17.  }
  18. inStream.close();
  19. %>

整个文件夹拷贝

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%@ page import="java.io.*" %>
  3. <%String url1="C:/aaa";
  4.   String url2="d:/java/";
  5.   (new File(url2)).mkdirs();
  6.  File[] file=(new File(url1)).listFiles();
  7.  for(int i=0;i<file.length;i++){
  8.   if(file[i].isFile()){
  9.    file[i].toString();
  10.    FileInputStream input=new FileInputStream(file[i]);
  11.    FileOutputStream output=new FileOutputStream(url2+"/"+(file[i].getName()).toString());
  12.    byte[] b=new byte[1024*5];
  13.     int len;
  14.     while((len=input.read(b))!=-1){
  15.     output.write(b,0,len);
  16.     }
  17.     output.flush();
  18.     output.close();
  19.     input.close();
  20.   }
  21.  }
  22. %>

文件下载

  1. <%@ page contentType="text/html; charset=gb2312"%>
  2. <%@ page import="java.io.*" %>
  3. <%
  4.   String fileName = "newFile.txt".toString();
  5.   //读到流中
  6.   InputStream inStream=new FileInputStream("c://测试//newFile.txt");
  7.   //设置输出的格式
  8.   response.reset();
  9.   response.setContentType("text/plain");
  10.   response.addHeader("Content-Disposition","attachment; filename=\"" + fileName + "\"");
  11.   //循环取出流中的数据
  12.   byte[] b = new byte[100];
  13.   int len;
  14.   ServletOutputStream outStream = response.getOutputStream();
  15.   while((len=inStream.read(b)) >0)
  16.   outStream.write(b,0,len);
  17.   outStream.flush();
  18.   outStream.close();
  19.   inStream.close();
  20. %>

数据库字段中的文件下载

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%@ page import="java.util.*,java.sql.*,java.io.*"%>
  3. <%
  4.     String id = request.getParameter("id");
  5.     if(id==null)
  6.     {   throw new Exception ("没有找到图片");
  7.     }
  8.     else
  9.     {
  10.        try
  11.        {
  12. com.gzrealmap.lib.jdbc.JDBCUtil  SqlBean= com.gzrealmap.lib.jdbc.JDBCUtil.getInstance();
  13.                SqlBean.connect();
  14.                String sql = "select * from innernews where id = '"+79+"'";
  15.                ResultSet rs = SqlBean.queryforUpdate(sql);
  16.                rs.next();
  17.                //String fileNamedb = rs.getString("imageName");
  18.                String file= rs.getString("acc");
  19.                //String fileName = new String(fileNamedb.getBytes(),"iso-8859-1");
  20.                String fileName = "a.jpg";
  21.                 response.setHeader("Content-Disposition",  "inline; filename=\"" + fileName + "\"");    
  22.                String filter = fileName.substring(fileName.lastIndexOf("."));
  23.               
  24.                if(filter.equals(".txt"))
  25.                {
  26.                    response.setContentType("text/plain");
  27.                }
  28.                else if(filter.equals(".doc")||filter.equals(".dot"))
  29.                {
  30.                    response.setContentType("application/msword");
  31.                }
  32.                else
  33.                {
  34.                  response.setContentType("image/jpeg;charset=GB2312");
  35.                }
  36.                ServletOutputStream o = response.getOutputStream();
  37.                //o.write(file);
  38.                out.println(file);
  39.                //o.flush();
  40.                //o.close();
  41.                SqlBean.disconnect();
  42.        }
  43.         catch(Exception ex)
  44.        {
  45.            out.println(ex.getMessage());
  46.        }
  47.     }  
  48. %>

把网页保存成文件

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%@ page import="java.text.*,java.util.*,java.net.*,java.io.*"%>
  3. <%
  4.  URL stdURL = null;
  5.  BufferedReader stdIn = null;
  6.  PrintWriter stdOut = null;
  7.  try {
  8.   stdURL = new URL("<a href="http://www.163.com/">http://www.163.com</a>");
  9.  }
  10.  catch (MalformedURLException e) {
  11.    throw e;
  12.  }
  13. try {
  14.     //将字节流转变成为字符流
  15.     stdIn = new BufferedReader(new InputStreamReader(stdURL.openStream()));
  16.     String theFileName = "c://测试//163.html";
  17.     stdOut = new PrintWriter(new BufferedWriter(new FileWriter(theFileName.toString())));
  18.  }
  19.  catch (IOException e) {
  20.  }
  21.  /***把URL指定的页面以流的形式读出,写成指定的文件***/
  22.  try {
  23.     String strHtml = "";
  24.    while((strHtml = stdIn.readLine())!=null) {
  25.    stdOut.println(strHtml);
  26.    }
  27.  }
  28.  catch (IOException e) {
  29.    throw e;
  30.  }
  31.  finally {  
  32.    try {
  33.      if(stdIn != null)
  34.        stdIn.close();
  35.      if(stdOut != null)
  36.        stdOut.close();
  37.        }
  38.    catch (Exception e) {
  39.      System.out.println(e);
  40.    }
  41.  }
  42. %>

直接下载网上的文件

  1. <%@ page contentType="text/html;charset=gb2312"%>
  2. <%@ page import="java.io.*"%>
  3. <%@ page import="java.net.*"%>
  4. <%
  5.   int bytesum=0;
  6.   int byteread=0;
  7.   URL url = new URL("<a href="http://pimg.163.com/sms/micheal/logo.gif">http://pimg.163.com/sms/micheal/logo.gif</a>");
  8.   URLConnection conn = url.openConnection();
  9.   InputStream inStream = conn.getInputStream();
  10.   /**
  11.   String theFileName = "c:/测试/logo.gif";
  12.   theFileName = theFileName.toString();
  13.   File myFilePath=new File(theFileName);
  14.   if(!myFilePath.exists())
  15.   myFilePath.createNewFile();
  16.   **/
  17.   FileOutputStream fs=new FileOutputStream("c:/测试/logo2.gif");
  18.   byte[]  buffer =new  byte[1444];
  19.     while ((byteread=inStream.read(buffer))!=-1)
  20.     {
  21.        out.println("<DT><B>"+byteread+"</B></DT>");
  22.        bytesum+=byteread;
  23.        //System.out.println(bytesum);
  24.        fs.write(buffer,0,byteread);
  25.      }
  26. %>

按行读文件

  1. <%@ page contentType="text/html; charset=gb2312" %>
  2. <%@ page import="java.io.*" %>
  3. <%
  4. FileReader myFileReader=new FileReader("c:/哈哈.txt");
  5. BufferedReader myBufferedReader=new BufferedReader(myFileReader);
  6. String myString=null;
  7. String resultString=new String();
  8. while((myString=myBufferedReader.readLine())!=null) {
  9. resultString=resultString+myString+"
  10. ";
  11. }
  12. out.println(resultString);
  13. myFileReader.close();
  14. %>

对word文档的处理(上传与下载)

  1. <%@ page contentType="application/msword" %>
  2. <!-- 以上这行设定本网页为excel格式的网页 -->
  3. <%
  4.    response.setHeader("Content-disposition","inline; filename=test1.doc"); //线上浏览方式
  5.   // response.setHeader("Content-disposition","attachment; filename=test1.doc");//下载方式
  6.    //以上这行设定传送到前端浏览器时的档名为test1.doc
  7.    //就是靠这一行,让前端浏览器以为接收到一个word档
  8. %>
  9. //然后输出动态内容就可以得到一个word文档了

1,打开:
1)文件头上加:

  1. <%@ page  contentType="application/msword"%> 
  2. xml文件里:
  3. <mime-mapping>
  4.         <extension>doc</extension>
  5.         <mime-type>application/msword</mime-type>
  6. </mime-mapping>

2)可以用js,以下代码来自引用:

]]>




 
2,下载:

  1. <%@ page contentType="text/html;charset=gb2312" import= "java.io.*"%>
  2. <%// 得到文件名字和路径
  3.   String filename = "jsp.doc";
  4.   String filepath = "C:\\";
  5.   // 设置响应头和下载保存的文件名
  6.   response.setContentType("APPLICATION/OCTET-STREAM");
  7.   response.setHeader("Content-Disposition","attachment; filename=\"" + filename + "\"");
  8.   // 打开指定文件的流信息
  9.   java.io.FileInputStream fileInputStream = new java.io.FileInputStream(filepath + filename);
  10.   //FileOutputStream out  = new FileOutputStream(filepath+"测试\\" + filename);
  11.   // 写出流信息
  12.   int i;
  13.   while ((i=fileInputStream.read()) != -1) {
  14.    out.write(i);
  15.   }
  16.   fileInputStream.close();
  17.   out.close()
  18.  %>
posted @ 2006-05-30 12:46 edsonjava 阅读(361) | 评论 (0)编辑 收藏
 
     摘要: Write an image of a given format ...  阅读全文
posted @ 2006-05-30 12:39 edsonjava 阅读(362) | 评论 (1)编辑 收藏
 
     摘要: Read an image ...  阅读全文
posted @ 2006-05-30 10:30 edsonjava 阅读(343) | 评论 (0)编辑 收藏
 
 
回复此消息
前言
Jive是一个广受欢迎的开放的源码的论坛项目,它有很多值得我们学习的地方。这篇文章谈的就是Jive缓存机制的实现,希望对大家有所帮助。
简介
我们知道,在两个存取速度差距很大的对象(比如数据库和内存)之间,通常要加一个缓存,来匹配二者的速度。因此,缓存机制在我们实际项目中还是经常遇到的。同样Jive也使用缓存来加快贴子的显示。如果你正试图编写一个类似的东西,不妨研究一下Jive源码,可能对你大有帮助。
在Jive2.1.2中,涉及jive的缓存机制的java类大致可以分为以下四个部分(为了简化起见,本文只讨论帖子的缓存机制的实现,用户名和权限的存取虽然也用到了的缓存,但其实现机制与前者类似,因此不再赘述):
第一部分:提供HashMap,LinkedListedlist等数据结构以便实现缓存机制,其中HashMap是JDK提供的,其Key类型为Object。你可以在com.jivesoftware.util包中找到他们,包括的类有Cache类, LinkedList类,LinkedListNode类和Casheable接口,CacheObject类,CacheableBoolean类,CacheableInt类,CacheableLong类,CacheableLongArray类,CacheableString类,CacheSizes类,CacheTimer类;
第二部分:提供LongHashMap,LongLinkedListedlist等数据结构以便实现缓存机制,与第一部分不同的是,它的HashMap是自己编写的,其Key类型为Long,因此被冠以LongHashMap的名称。你同样可以在com.jivesoftware.util包中找到他们,包括的类有LongHashMap类,LongCache类, LongCacheObject类,
LongLinkedList类和LongLinkedListNode类;还有第一部分中的Casheable接口,它的各种数据类型的实现以及CacheSizes类和CacheTimer类,也可归于这部分,它们可看作是第一部分和第二部分的交集;
第三部分:调用底层数据结构以提供论坛对象的缓存,你可以在com.jivesoftware.forum.database包中找到他们,包括的类主要有DatabaseCacheManager类,DbForumFactory类,DbForum类,DbForumThread类,DbForumMessage 类,DatabaseCache类,ForumCache类, ForumThreadCache类,ForumMessageCache类; 
第四部分:向Jsp页面提供访问接口,同样在com.jivesoftware.forum.database包中,包括类有ForumThreadBlockIterator和ForumMessageBlockIterator类,第三部分的DbForum类,DbForumThread类和DbForumMessage 类也可以包括进来,实际上,这三个类是第三和第四部分联系的纽带,在com.jivesoftware.util包中还有一个LongList类,它用来将ForumThreadBlockIterator和ForumMessageBlockIterator转化成Long型数组,因此也应算在这部分;
因此缓存机制也可以划分为三层,即第一、二部分的底层数据结构、第三部分的中间层和第四部分的上层访问接口,下面分别讨论:
底层数据结构
  jive缓存机制的原理其实很简单,就是把所要缓存的对象加到HashMap哈希映射表中,用两个LinkedListedlist双向链表分别维持着缓存对象和每个缓存对象的生命周期,如果一个缓存对象被访问到,那么就把它放到链表的最前面,然后不定时的把要缓存对象的对象加入链表中,把过期对象删除,如此反复。我们比较一下第一和第二部分就可以发现,他们的代码几乎完全相同。差别就在第二部分的哈希映射表没有采用JDK提供的类,而是采用了作者自己编写的一个类,他将原来哈希映射表的Key的类型由Object改为Long,这样做虽然在一定程度上加快了缓存的速度并减小了缓存的大小,但无形之中也减低了程序的稳定性和可读性,因此不推荐大家仿效。值得一提的是,在Jive1.0.2版中,所有Forum,Thread,Message的ID和他们的内容的缓存都是用第一部分的Java类实现的,在升级到后面的版本时,其内容采用了第二部分的Java类实现,但其ID仍用第一部分的Java类实现,这也是Jive容易混淆的一个地方。好了,我们先来看第一部分的Java类实现。
我们先来看LinkedListNode类的源码:
public class LinkedListNode {
public LinkedListNode previous;
    public LinkedListNode next;
    public Object object;
    public long timestamp;

public LinkedListNode(Object object, LinkedListNode next,
            LinkedListNode previous)
    {
        this.object = object;
        this.next = next;
        this.previous = previous;
    }
public void remove() {
        previous.next = next;
        next.previous = previous;
    }
public String toString() {
        return object.toString();
    }
}
很明显,这是一个双向链表的节点类,previous,next分别记录前后节点的指针,object用于记录所需缓存的对象,timestamp用于记录当前节点被创建时的时间戳。当该时间戳超过该节点的生存周期时,它就会被remove()方法删除掉。就是由LinkedListNode构成了LinkedList链表,而LinkedList类中只是实现了getFirst(),getLast(),addFirst(),addLast(),clear()等链表的基本方法,没有其他内容。
再来看Cacheable接口和它的一个实现类CacheableInt:

public interface Cacheable {
    public int getSize();
}
public class CacheableInt implements Cacheable {
    private int intValue;
    public CacheableInt(int intValue) {
        this.intValue = intValue;
    }
    public int getInt() {
        return intValue;
    }
    public int getSize() {
        return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt();
    }
}
从上面的代码可以看到Cacheable接口只有一个方法getSize(),它要求继承类实现该方法汇报占用缓存的大小,以便实施管理。Integer,Boolean,Long,LongArray,String类型都有其对应的类,CacheSizes类则把各种类型的长度封装起来,便于修改和移植。那么为什么CacheableInt. getSize()得到的是sizeOfObject()+sizeOfInt()呢,聪明的读者一定想到了,因为任何都继承自Object,计算空间时当然也要把它给算上。
还有一个CacheObject类,它是缓存的基本元素,我们来看代码:
public final class CacheObject {
public Cacheable object;
      public int size;
      public LinkedListNode lastAccessedListNode;
public LinkedListNode ageListNode;

      public CacheObject(Cacheable object, int size) {
        this.object = object;
        this.size = size;
}
}
lastAccessedListNode记录着一个缓存节点的Key,是构成lastAccessedList链表的基本元素,在lastAccessedList链表中,经常被访问到的节点总是在最前面。而ageListNode记录着缓存节点的加入时间,是构成ageList链表的基本元素,而ageList链表则是按时间先后排序,先加入的节点总是在最后面。lastAccessedListNode和ageListNode本来可以写成两个类,毕竟lastAccessedListNode并不需要ageListNode的成员变量timestamp,但是为了简化程序,Jive把他们写成了一个类,这也是容易混淆的一个地方。
现在来看缓存机制中最关键的一个类Cache的部分代码,主要是add()和get()方法:
public class Cache implements Cacheable {
protected static long currentTime = CacheTimer.currentTime;
protected HashMap cachedObjectsHash;
protected LinkedList lastAccessedList;
protected LinkedList ageList;
//缓存元素的最大尺寸128kbit,可修改

protected int maxSize =  128 * 1024; 
//整个缓存的大小
protected int size = 0;
//缓存元素的最大保存时间,用Cache(long maxLifetime)初始化
protected long maxLifetime = -1;
//记录cache的命中次数和未命中次数
protected long cacheHits, cacheMisses = 0L; 
……
//向哈希表中添加一个关键字为Key的缓存对象object
public synchronized void add(Object key, Cacheable object) {
        //先把原来的对象remove掉
        remove(key);
int objectSize = object.getSize();
        //如果对象太大,则不加入缓冲存
        if (objectSize > maxSize * .90) {
            return;
        }
        size += objectSize;
        //新建一个缓存对象,并放入哈希表中
        CacheObject cacheObject = new CacheObject(object, objectSize);
        cachedObjectsHash.put(key, cacheObject);
        // 把缓存元素的Key放到lastAccessed List链表的最前面
        LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key);
        cacheObject.lastAccessedListNode = lastAccessedNode;
        //把缓存元素的Key放到ageList链表的最前面,并记下当前时间
        LinkedListNode ageNode = ageList.addFirst(key);
        ageNode.timestamp = System.currentTimeMillis();
        cacheObject.ageListNode = ageNode;
// 在cullCache()中,先调用deleteExpiredEntries()把过期对象删掉,如果缓存还是太满,则掉用remove(lastAccessedList.getLast().object)把lastAccessedList中不常访问的对象删掉
        cullCache();
    }
//在哈希表中得到一个关键字为Key的缓存对象object
public synchronized Cacheable get(Object key) {
        // 清理过期对象
        deleteExpiredEntries();

        CacheObject cacheObject = (CacheObject)cachedObjectsHash.get(key);
        if (cacheObject == null) {
            //没找到则未命中次数加一
            cacheMisses++;
            return null;
        }

        //找到则命中次数加一
        cacheHits++;

        //将该缓存对象从lastAccessedList链表中取下并插入到
        //链表头部
        cacheObject.lastAccessedListNode.remove();
        lastAccessedList.addFirst(cacheObject.lastAccessedListNode);

        return cacheObject.object;
    }
到这里第一部分的Java类实现就说完了,正如上文提到的那样,第二部分的Java类实现与第一部分基本上没有什么差别,因此就不再赘述。下面给出第二部分的类图,以供读者参考。
[img]http://www.javaresearch.org/members/{tomjava}
中间层
     中间层是联系上层访问接口和低层数据结构的纽带。它的主要功能就是根据ID(对应于数据库中的编号)到缓存中去找相应的对象,如果缓存中有该对象就直接得到,没有则去读数据库生成一个新的对象,再把该对象放入缓存中,以便下次访问时能直接得到。下面是相关类的类图:
[img]http://www.javaresearch.org/members/{tomjava}
(注:Forum表示论坛,Thread表示论坛贴子的线索,Message表示论坛贴子,它们的关系是这样的 :Forum包括数条Thread,Thread包括数条Message。)
由上图可见,DbForum类,DbForumThread类和DbForumMessage 类的实例对象都包含一个 DbForumFactory类的实例对象factory。DbForum,DbForumThread和DbForumMessage被DbForumFactory生产出来,同时他们也通过DbForumFactory来访问缓存。而在DbForumFactory中则包含一个DatabaseCacheManager类的实例对象cacheManager,它负责管理所有的缓存对象,如图,这些缓存对象就是ForumCache类, ForumThreadCache类和ForumMessageCache类的实例。ForumCache类, ForumThreadCache类和ForumMessageCache类继承自同一个抽象类DatabaseCache,而在DatabaseCache类中,有一个LongCache型的成员变量cache,就这样,中间层就和低层的数据结构结合起来了。
我们以thread线索对象的获得为例来说明中间层是如何运作的,请看代码摘要:
DbForum.java
public class DbForum implements Forum, Cacheable {
    。。。 。。。
public ForumThread getThread(long threadID)
            throws ForumThreadNotFoundException
    {
        return factory.getThread(threadID, this);
    }
。。。 。。。
}

DbForumFactory.java
public class DbForumFactory extends ForumFactory {
    。。。 。。。
protected DbForumThread getThread(long threadID, DbForum forum) throws
            ForumThreadNotFoundException
    {
        DbForumThread thread = cacheManager.threadCache.get(threadID);
        return thread;
}
。。。 。。。
}

ForumThreadCache.java
public class ForumThreadCache extends DatabaseCache {
    。。。 。。。
public DbForumThread get(long threadID)
            throws ForumThreadNotFoundException
{ //缓存中寻找以threadID为编号的DbForumThread对象
DbForumThread thread = (DbForumThread)cache.get(threadID);
        if (thread == null) {
      //如果在缓存中找不到该对象
            //新建一个以threadID为编号的DbForumThread对象
thread = new DbForumThread(threadID, factory);
//将新建对象加入缓存
            cache.add(threadID, thread);
        }
        return thread;
}
   。。。 。。。
}

DbForumThread.java
public class DbForumThread implements ForumThread, Cacheable {
   。。。 。。。
protected DbForumThread(long id, DbForumFactory factory)
            throws ForumThreadNotFoundException
    {
        this.id = id;
        this.factory = factory;
//读取数据库,其中id对应数据库中的jiveThreadProp表中的threadID字段
        loadFromDb();
        isReadyToSave = true;
}
   。。。 。。。
}
   从上面的代码我们可以看到,当我们调用DbForum类 的getThread(long threadID)方法去获得一个编号为threadID的线索对象时,我们实际上调用的是DbForumFactory类中的getThread(long threadID, DbForum forum)方法,而它则是调用ForumThreadCache类的方法来完成任务的。那么ForumThreadCache类里get(long threadID)方法有做了什么呢?代码已经很清楚了,就是根据threadID到缓存中去找相应的线索对象,如果缓存中有该对象就直接得到,没有则新建一个DbForumThread对象,再把该对象放入缓存中。看到这里,有点让人奇怪,好象程序中根本没有连接数据库的语句。再看DbForumThread类的代码,终于恍然大悟,原来Jive在新建一个DbForumThread对象就已经用loadFromDb()方法把数据读出来了;另一方面,如果在缓存中找了DbForumThread对象,程序根本就不会新建DbForumThread对象,因而就好象没有数据库的操作,这实际上就是我们通过缓存机制所要达到的目的。
    Message帖子对象的获得与Thread对象的获得类似,因此就不再重复了。从上面我们可以看到,只要我们得到了论坛线索的编号threadID,我们就可以得到对应的线索对象,不管它是从缓存中来,还是从数据库中来。那么threadID是如何从Jsp页面传到中间层的呢?让我们来看上层访问接口的运行机制吧。
上层访问接口
    上层访问接口的主要功能连接Jsp页面和中间层,换句话说,就是把Jsp页面中要调用的Thread、Message对象的ID传递到中间层。下面给出访问Thread的相关类的类图(访问Message机制类似,故省略)。其中的forum.jsp是显示论坛内容的页面,在这里,我们把forum.jsp看成是一个特殊的类,它里面有一个ForumThreadIterator类的实例变量threads和DbForum类的实例变量forum,故它和ForumThreadIterator类及DbForum类的关系应是关联关系。
[img]http://www.javaresearch.org/members/{tomjava}
先来看forum.jsp和DbForum 类的部分代码:
forum.jsp
<%
// ResultFilter结果过滤类
ResultFilter filter = new ResultFilter();
    filter.setStartIndex(start);
    filter.setNumResults(range);
//调用Dbforum的threads()方法,获得ForumThreadIterator对象实例
ForumThreadIterator threads = forum.threads(filter);
。。。 。。。
while (threads.hasNext()) {
        //对thead进行遍历
        ForumThread thread = (ForumThread)threads.next();
        //得到thread的ID
        long threadID = thread.getID();
        //得到线索的根帖子rootMessage
        ForumMessage rootMessage = thread.getRootMessage();
        //得到帖子主题和作者等信息
        String subject = rootMessage.getSubject();
        User author = rootMessage.getUser();
。。。 。。。
}
%>
DbForum.java
public class DbForum implements Forum, Cacheable {
。。。 。。。
public ForumThreadIterator threads(ResultFilter resultFilter) {
    //生成SQL语句
        String query = getThreadListSQL(resultFilter, false);
        //得到threadID块
long [] threadBlock = getThreadBlock(query.toString(),
 resultFilter.getStartIndex());
        。。。 。。。
        //返回ForumThreadBlockIterator对象
        return new ForumThreadBlockIterator(threadBlock, query.toString(),
                startIndex, endIndex, this.id, factory);
}
protected long[] getThreadBlock(String query, int startIndex) {
        int blockID = startIndex / THREAD_BLOCK_SIZE;
        int blockStart = blockID * THREAD_BLOCK_SIZE;  
        String key = query + blockID;     
 //根据Key的值到缓存中取得ThreadID的数组
        CacheableLongArray longArray = 
(CacheableLongArray)threadListCache.get(key);
        //在缓存中则返回
        if (longArray != null) {
            long [] threads = longArray.getLongArray();
            return threads;
        }
        // 否则到数据库中取ThreadID的块,以数组形式返回
        else {
            LongList threadsList = new LongList(THREAD_BLOCK_SIZE);
            Connection con = null;
            Statement stmt = null;
            。。。数据库操作 。。。
            }
            long [] threads = threadsList.toArray();
            //将 ThreadID的块加入缓存
threadListCache.add(key, new CacheableLongArray(threads));
            return threads;
        }
     }
。。。 。。。
}
在forum.jsp中有一个ResultFilter类的实例resultFilter,它给出页面显示Thread的起始位置和数量,它作为参数传入forum.threads()中,用于构造相关的SQL语句。当调用forum.threads(filter)时,程序将生成的SQL语句传入到getThreadBlock()方法中去得到一个threadID的块,也就是一组threadID。之所以要读threadID块,是因为显示论坛时并不是显示一条线索就行了,而是一下显示十几条,这样做可以避免反复读数据库,再说threadID不是thread对象,并不怎么占空间。
应该说,使用了块以后,减轻了数据库的访问量,因而论坛的效率有了很大的提高。然而好戏还在后头,Jive又把块放入了缓存中。在getThreadBlock()方法里,Jive用Cache类的实例对象threadListCache来缓存threadID块,而关键字就是SQL语句加上blockID,也就是说,主要SQL语句和blockID相同,就可以在缓存中取出相同的threadID块。当然,缓存中找不到,还是要到数据库中读出来加入缓存的。这样论坛的效率又得到了进一步的提升。
ForumThreadBlockIterator类继承自ForumThreadIterator抽象类,而ForumThreadIterator类又实现了Iterator接口,因此得到ForumThreadBlockIterator的实例对象threads后,就可以在用threads.next()方法对它进行编历了。ForumThreadBlockIterator类的代码想都可以想得到,就是逐个读取ThreadID,然后根据ThreadID返回Thread对象,由此上层访问接口就和中间层衔接起来了。
小结
Jive的缓存机制值得我们学习的地方有很多,比如读取线索时不是读一条而是读一个block;显示线索的起始位置和数量用专门的一个类来管理,动态生成SQL语句;用一个专门的类来负责管理缓存;把论坛缓存对象的功能抽象出来形成一个缓存的抽象类DatabaseCache,让它去跟低层数据结构联系起来。这些都体现了面向对象的设计原则即提高软件的可维护性和可复用性。
同时,Jive也告诉我们,要想编好程序,只懂条件语句和循环语句可不行;必须选择好数据结构,掌握好面向对象的设计原则,熟悉设计模式思想方法,这样才能编写出强壮高效的代码。
posted @ 2006-05-23 00:23 edsonjava 阅读(411) | 评论 (0)编辑 收藏