前言
BT软件我用过不少,从最开始的Azureus(现在改名为Vuze),BitTorrent,到BitComet,BitSpirit,再到Transmission,μTorrent,Halite(64位),rTorrent,中间间或还用过一些其他乱七八糟的记不得名字的软件,总之体验了很多,也有不少使用心得。
要说最稳定,功能最全,那自然是Azureus(Vuze),它几乎支持所有BT软件的功能,同时最不容易对硬盘造成损坏,运行起来最稳定。在改名为Vuze之后,界面上有了大的改进,一直是弱项的启动速度貌似也有了提高,又加入了许多新的功能,比如说转换视频格式之类的。当然,它的速度也不慢,我感觉和μTorrent不相上下,位列第二。
Azureus是我用的最久的BT软件,这可能和我一直使用Mac有关系。当我使用Windows系统的时候,我就用μTorrent,这个软件没有什么多说的,最小,非常快,简洁,都是它的优点,而且高校的BT站点都基本指定必须使用μTorrent才能获得最大速度。
Halite是我买了新电脑之后开始使用的。我的MacBook Pro和MacBook在前不久终于撑不下去了(用了也有三四年了),所以我就去中关村配了台新电脑,8G的内存自然要用Windows 7 64Bit的系统了,所以,我选择了Halite,貌似这个是最成熟的64位BT软件,当然,Windows下再用Vuze就没意思了(由于Vuze是Java写的,所以肯定也支持64位系统),我想找的就是原生软件。Halite使用的感觉很不错,速度很快,不过一方面它界面上有些小Bug,另外,它也不支持BT的一些功能,不过,对于普通用户来说,使用完全没有问题。
最后,则要说本文的重点了,rTorrent,这是我使用过的最好的BT软件,注意,是最好的,没有之一。原因有两点,一是速度最快,不知道是怎么回事,同样的网络环境,rTorrent下载的速度一定是最快的;二是没有图形界面,对于我来说,BT软件没有图形界面能省去不少资源,因为大部分时候是不用看BT软件的界面的,而rTorrent由于这点原因,可以用在很多小系统上,例如一些NAS设备,当然,rTorrent默认的控制台界面也不错,和screen配合起来就已经很好用了,它也支持网络监控(通过SCGI实现),可以使用rutorrent或者wTorrent通过网页控制。
安装
我说的安装是在UNIX/Mac/Linux系统上的安装,至于说Windows用户也想使用,我建议你还是老老实实使用μTorrent吧,至于说为什么这么说,那是因为μTorrent的速度已经足够快了,仅仅比rTorrent小那么一点而已,但是配置rTorrent的需要花费的时间和精力,远远超过了配置μTorrent(需要配置吗,不需要!)所要花的时间和精力(基本为0)!
安装可以编译安装,也可以使用yum,apt等直接安装,不过我建议编译安装。
依赖的软件包有:
curl curl-ca-bundle perl5 perl5.8 libidn gettext libiconv gperf ncurses ncursesw openssl pkgconfig libsigcxx2 libtorrent
如果要使用网络监控,那么还需要安装:
xmlrpc-c
如果要使用网络监控,编译安装rTorrent的时候,注意configure时要加参数:
configure --with-xmlrpc-c
编译安装其他的部分就不再赘述了。
配置
rTorrent的配置文件是$HOME(用户文件夹下)的.rtorrent.rc文件。
在这里我首先简单解释一下这个配置文件的格式,接着我们再看看一些常用的功能如何配置。
(未完待续……)
众所周知,libpcap是一个可移植的网络数据包捕获库,使用C/C++编写的程序都可以用它,当然,它还有很多其他语言的Wrapper。libpcap可以从tcpdump的网站下载到。
这里我主要介绍一下它的C语言API。在文章的最后,我将介绍一下winpcap(libpcap的Windows实现版)。
Prefix
首先,我们需要了解C语言的基本知识。其次,最好对计算机网络有一定的了解。
Knowledge
我们可以分5个步骤来描述使用libpcap编写程序的步骤:
- 我们首先要决定监听的设备接口,这个可以用一个string来表示,也可以由pcap提供给我们。
- 初始化pcap。我们要告诉pcap需要监听的是哪一个设备。pcap对于不同的设备,使用session来区分它们,一个设备就是一个“session”。
- 我们需要创建一个监听的原则(rule),然后编译它,提供给pcap作为一个session的filter。
- 接下来,我们进入真正的监听循环中。我们告诉pcap监听多少packets;每当一个packet到来的时候,都会调用我们已经定义好的一个函数,在这个函数中,我们处理监听到的数据。
- 最后,我们关闭监听的session。
Functions
在源代码中,#include <pcap.h>是必须的。
char* pcap_lookupdev(char* errbuf)
返回一个设备名,Linux可能是eth1,Mac OS X可能是en0,错误时返回NULL。参数errbuf是遇到错误时对错误的描述字符串,我们一般这么定义:
char errbuf[PCAP_ERRBUF_SIZE]。
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
这个函数会打开device所表示的设备,返回一个“session”,错误时返回NULL。snaplen是pcap能捕获的最大字节数,promisc为true(非0值,一般是1)的时候,这个设备在监听的时候使用的是promiscuous mode(即使有时候设置成了false,在某些特定情况下也会强制变为true)。to_ms是超时毫秒数,0表示没有超时。如果没有超时,一般来说,读取snaplen字节之后才会返回,除非发生错误。errbuf和前面提到的是一个意思。
这里解释一下promisc模式,中文一般翻译为混杂模式。非混杂模式只监听所选择设备的数据包,包括接收到的,发送到的,以及路由的。而混杂模式则会监听线路上所有的数据包,工作在non-switched environment下(比如说hub),但是,混杂模式是可以被检测到的,而且在高速的网络环境下,会给系统造成很大的负担。
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
这个函数编译str,其中str是一种常规字符串,在tcpdump的man page中有详细的说明,这里就不再赘述了。所得结果存在fp中,可以作为
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
的参数,给session设置filter。
p就是在前面pcap_open_live打开的session。optimize是决定常规字符串是否优化的的选项,1表示true,0表示false。netmask是子网掩码。
通过
int pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf)
可以得到device的网络地址和子网掩码。
上面三个函数错误时返回-1,成功时返回其他值。
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
这个函数获得监听到的数据包,h是一个存储抓取的数据包信息的结构体。
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
而
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
和
int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
则分别是循环监听数据包cnt次,如果cnt为负数,那么表示一直监听,直到发生错误。callback是一个回调函数,它的格式是这样的:
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
其中,user作为用户自定义的数据,传入给callback的args参数,callback将监听到的数据和信息分别存入packet和header。
pcap_loop和pcap_dispatch的区别在于,前者会一直读取数据,直到读取了cnt个数据包,后者会在pcap_open_live设置的timeout时间到了之后返回(尽管是不能保证的)。细节的区别请参看pcap的man page。
pcap_dispatch返回读取的packets数目,0表示没有读到packets,-1表示发生了错误,可以使用pcap_perror()或者pcap_geterr()得到错误信息。-2表示调用了pcap_breakloop(pcap_t *)。
void pcap_perror(pcap_t *p, char *prefix)
打印出p的错误信息,prefix作为错误信息的前缀。
char *pcap_geterr(pcap_t *p)
得到错误信息,在p关闭后,其返回值将不会指向正确的错误信息。
pcap_loop返回0表示读取了cnt个packets,返回-1表示发生了错误,返回-2表示调用了pcap_breakloop(pcap_t *)。
void pcap_freecode(struct bpf_program *)
释放掉编译后的bpf_program资源。
void pcap_close(pcap_t *p)
一般会在最后关闭p,释放所有资源。
Extras
int pcap_datalink(pcap_t *p)
int pcap_list_datalinks(pcap_t *p, int **dlt_buf);
int pcap_set_datalink(pcap_t *p, int dlt);
这三个函数操作设备的datalink,pcap_datalink会返回设备的链路层类型,例如:
DLT_EN10MB是Ethernet (10Mb, 100Mb, 1000Mb, and up)类型。
DLT_IEEE802_11是IEEE 802.11 wireless LAN类型。
pcap_list_datalinks将会列出设备支持的链路层类型,返回类型个数,错误则返回-1。
而pcap_set_datalink设置设备的链路层类型,错误返回-1。
Network
注意,我们抓到的数据包有包头,例如对于TCP/IP的数据包,包括Ethernet Header,IP Header,TCP Header。
所以,在这一个小节里,我们将简单讲一下以太网(Ethernet)的数据包结构。
这幅图显示了以太网+IP+TCP的结构。
Ethernet Header长度总是14字节,分别包括6字节的目标MAC地址,6字节的源MAC地址,2字节的以太网类型。这里是Ethernet Header的type字段含义。
而IP和TCP的头定义都在上面两个链接中说的很清楚了,这里就不再赘述了。
Winpcap
Winpcap是libpcap在Windows下的实现版本,编程的流程和libpcap没有什么区别,值得注意的是,微软的C编译器支持的C标准很老,如果你编译的是C文件,那么需要注意所有的变量定义都要放在函数的开始。
使用MySQL也已经有好多年了,Paul Dubois的那本MySQL (Developer’s Library)当年也从头到尾的翻了好多好多遍。但是由于各种原因,没有总结留下任何东西。以前使用MySQL都用的C API,最近打算开始学习PHP,自然离不开MySQL,所以打算把这些年学习的MySQL的相关知识总结一下,写在这篇文章中。既然命名为Tips,那么就要有Tips的样子,我的目标是短小精悍,那么就不可能长篇的议论,我打算通过一种舒服的方式,把MySQL常用功能和API总结出来,在过很久之后看的时候,能够立即回想起来MySQL的一些知识要点,快速的进入开发状态之中。
1. 安装
在使用MySQL之前,最先要做的就是安装。在Windows上的安装我就不说了,非常简单直接。这里说一下在Linux/UNIX系统上的安装。
当然,我指的是编译安装。使用apt或者yum安装MySQL的同学们可以不看这部分内容了。首先,在一个空白的系统上,需要安装gcc,gcc-c++,ncurses-devel,这是在CentOS 5.4上的包名称,使用其他发行版的同学,自己查找一下这三个包对应的名称,安装就可以了。去MySQL的官方网站下载MySQL源码包吧。
configure的时候需要一些参数,这些参数可以./configure –help查看说明,也可以去官方网站查询文档,这里我记录一下自己选用的一些参数含义。
|
静态编译
|
–enable-static –with-client-ldflags=-all-static –with-mysqld-ldflags=-all-static
|
静态编译选项,据说可以提高性能13%,没有测试过。 |
|
以线程方式编译客户端
|
–enable-thread-safe-client
|
|
|
去掉innodb表支持
|
–without-innodb |
innodb是一种支持事务处理的表,适合企业级应用。 |
|
去掉isam表类型支持
|
–without-isam |
isam表是一种依赖平台的表,现在很少用了。 |
| 取消导航调试 |
–without-ndb-debug |
|
| 日志文件目录 |
–localstatedir=/usr/local/mysql/var/ |
|
| MySQL后缀名 |
–with-server-suffix=-enterprise-gpl |
在用mysql登录的时候在版本号的后面可以看到。 |
| 去除诊断模式 |
–without-debug |
如果用–with-debug=full编译,大多数查询慢20%。 |
| 大表支持 |
–with-big-tables |
|
| 字符集 |
–with-extra-charsets=gbk,gb2312,utf8 |
设置支持gbk,gb2312,utf8字符集。 |
| 强制使用pthread库 |
–with-pthread |
POSIX线程库。 |
| 汇编 |
–enable-assembler |
|
| 默认字符集 |
–with-charset=utf8 |
|
| 使用unix套接字 |
–with-unix-socket-path=/tmp/mysql.sock |
据说提高7.5%性能连接性能,所以在windows下mysql性能肯定不如unix。 |
| 端口 |
–with-tcp-port=3306 |
指定MySQL服务将监听TCP 3306端口。 |
对于大部分应用来说,上面的参数基本不用设置。对于中国用户来说,则最多需要设置一下字符集。
我的configure命令:
./configure –with-charset=utf8 -with-extra-charsets=all –prefix=/home/mysql
当然,这个命令的前提是系统中要有一个/home/mysql的文件夹,我是用创建一个名称为mysql的用户来做到这点的,而且,configure命令也是使用mysql用户运行的,添加一个mysql用户的命令很简单,下面这句可以直接复制粘贴。
sudo useradd -m -c "MySQL Server" -d /home/mysql -s /bin/bash -u 502 mysql
然后make && make install,再更改下权限:sudo chown mysql:mysql -R mysql 。
2. 配置
安装好了之后,需要运行mysql_install_db来初始化数据库。这个命令可以有不少参数,下面列一下。
| –basedir=/usr/local/mysql |
默认mysql路径 |
| –datadir=/usr/local/mysql/data |
默认mysql数据路径 |
| –user=mysql |
默认使用mysql服务用户 |
| –defaults-file=/usr/local/mysql/my.cnf |
默认配置文件 |
| –pid-file=/usr/local/mysql/mysql.pid |
默认PID文件路径 |
另外,MySQL配置文件命名为my.cnf,存储的位置和读取顺序在这里做一个说明。
| 文件名 |
说明 |
| /etc/my.cnf |
全局选项 |
| $MYSQL_HOME/my.cnf |
服务器相关选项 |
| defaults-extra-file |
服务器启动时用–defaults-extra-file=path指定的文件 |
| ~/.my.cnf |
用户相关选项 |
MYSQL_HOME是一个环境变量,包含服务器相关的my.cnf文件驻留的目录路径。
如果未设置MYSQL_HOME,并且DATADIR中有一个my.cnf文件,BASEDIR中没有my.cnf文件,mysqld_safe将MYSQL_HOME设置为DATADIR。否则,如果未设置MYSQL_HOME并且在DATADIR中没有my.cnf,则mysqld_safe将MYSQL_HOME设置为BASEDIR。
典型情况为/usr/local/mysql/data,请注意这是配置时指定的数据目录的位置,而不是mysqld_safe启动时用–datadir指定的。运行时使用–datadir对寻找选项文件的服务器没有效果,因为服务器在处理命令行参量之前寻找这些选项。
MySQL按照上述顺序寻找选项文件,并读存在的选项文件。如果你想要使用的某个选项文件不存在,则用明文文本编辑器创建。如果存在多个选项文件,文件中指定的后读取的选项要优先文件中指定的先读取的选项。
可以从MySQL安装目录/share/mysql/下拷贝一个配置文件到mysql用户目录下,重命名为.my.cnf,我的拷贝命令:
cp share/mysql/my-medium.cnf .my.cnf
注释掉其中的skip-federated选项,如果有的话。
3. 启动和关闭
有两种启动方式:
一种是运行mysqld_safe 参数 &,例如:
mysqld_safe –defaults-file=/usr/local/mysql/my.cnf –user=mysql &
另一种是运行MySQL安装目录/share/mysql/mysql.server 参数,例如:
/home/mysql/share/mysql/mysql.server start
关闭MySQL可以kill掉进程,也可以使用/home/mysql/share/mysql/mysql.server stop 命令。
4. 命令
首先要给root设置一下密码,这个我们采用
/home/mysql//bin/mysqladmin -u root password ‘new-password’
或者
/home/mysql//bin/mysqladmin -u root -h ehost.edomain password ‘new-password’
的方式。
4.1. 创建用户
虽然创建用户有好多种方式,但是我在这里只说一种,这种记住了就可以了。
GRANT命令:
GRANT [PRIVILEGES, …] ON [database].[table] TO ‘user’@'somehost’ IDENTIFIED BY ‘password’ [WITH GRANT OPTION];
下面是几个例子:
GRANT ALL PRIVILEGES ON *.* TO ‘monty’@'localhost’ IDENTIFIED BY ‘some_pass’ WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO ‘monty’@'%’ IDENTIFIED BY ‘some_pass’ WITH GRANT OPTION;
GRANT RELOAD,PROCESS ON *.* TO ‘admin’@'localhost’;
GRANT USAGE ON *.* TO ‘dummy’@'localhost’;
其实,登陆进去之后,help grant就可以理解得到很详细的说明了。说到这里,郑重的跟大家说,多用help,多看文档,不要老用Google,哈哈。
5. API
C语言的API忘记了,很久没用了。
PHP的正在学习,慢慢的会加上。
最近有很多工作要做,在这里记录一下,以免自己忘记掉。
学习:
- 阅读APUE。
- 阅读readline源代码。
- 阅读Objective-C 2.0。
- 阅读黄帝内经素问篇。
- 学习php,css,html,以便于更好的搭建自己的网站。
- 学习JavaScript,实现html页面的语法高亮(参考SyntaxHighlighter),希望能达到弹出一个窗口那样的效果。
维护:
- 网站维护。
- 主页。
- Google Analytics(应该已经没有问题了)。
- Google AdSense(目前正在评估阶段)。
- MX设置。
- 总结之前写的很多技术文档,将其放在Blog上。
- 版权声明。
十月 19th, 2010 in
Todo | tags:
todo,
任务,
计划 |
No Comments
很久很久之前,我就希望自己能有一个Blog了,之前试验了 blog.edu.cn 、blogger.com 、blog.sina.com.cn 、hi.baidu.com 、blog.163.com等等等等,都不能让我满意。最后,我期待能有自己的一个虚拟主机,一个域名,来搭建自己的一个Blog。
但是由于学业,生活,心态等等原因,这个愿望一直没有实现。不得不说,这些原因其实都不是根本的。根本原因是因为自己的心一直处于一种等待的状态,等待着毕业,等待着工作,等待着下一个月,下一周,明天,等待着日落,日出。我没有为将来,为自己的明天做出什么特别的努力,反而是一直在等待。
在《基督山伯爵》的末尾,大仲马通过主人公告诉我们,永远都要保持等待与希望,美好的明天总会到来。在等待和希望中,我终于等来了这篇Blog的诞生。其实,真的是等来的吗?不是,或者说不仅仅是,在日复一日的等待中,我们还需要主动的,积极的去做些事情,做些能实现希望的事情,希望才会实现,难道不是这样的吗?
在这篇日志之前的日志,都是我从我的其他Blog上Copy过来的,日期设置成了当时发布时的日期而已。
多余的话就不说了,总之,这是我第一篇真正意义上的Blog,那么,祝贺吧,开心吧,哈哈哈哈^-^!!!!
十月 18th, 2010 in
Mood | tags:
mood,
心情,
杂谈 |
No Comments
声明:我曾经写过一个制作Jar电子书的程序,所有支持JavaME MIDI2.0的手机都可以安装使用这种格式的电子书。之前在我的blogger上Post了相应的文档,包括一开始的制作文档和之后的更新文档。由于我的博客搬家,所以这里把和evader相关的文章都放在这里,按照时间从后往前的顺序排列。
另:evader是我给这个制作Jar电子书的软件起的名字。
2009年4月27日 星期一
evader 0.1.6版本发布
嗯,正式的版本也该发布了。
evader即eBookGenerator改名之后的软件,eBookGenerator从0.1.6版本起,正式改名为evader。如果需要了解这款软件除了在这里提到的性能以外的详细性能,请参看下面《一个制作Jar格式电子书的小程序》。
在这个版本里,我做了以下的工作:
- 改名。将eBookGenerator这个不太贴切的名字改成了evader,这个名字的中文含义是“逃避”,为什么起这个名字呢?是因为我觉得读书的时候,就如同逃离了整个现实世界,从而沉浸在书中的世界一样,这是采用这个名字的一个原因。其次,这个名字是ender,E-Book,maker等单词的排列组合,那个v,是我一时的灵感,具体代表什么,我也不太清楚,只是觉得这个字母很漂亮,这是采用这个名字的第二个原因。
- 加入页面滚动选项。按手机的 * 键可以使页面开始自动向下滚动,按 # 键停止滚动,在设置页面里可以设置滚动速度。
- 加入长时间背景灯高亮选项。在设置页面里可以打开长时间背景灯高亮功能。这个功能需要手机本身支持NokiaUI,目前市场上所有的Nokia手机和大部分的Song Ericsson手机都支持。其他的手机需要根据具体的使用情况来判断。当然,不支持的手机不会有任何的Bug,完全正常使用。这里需要再做一点说明,在设置了长时间背景灯高亮选项之后,会立即生效。在取消掉长时间背景灯高亮选项之后,需要先关闭手机软件,然后再打开,这样才会生效。
- 增加设置密码选项。在设置页面里可以设置看书的密码。
- 支持多国语言。根据手机的语言来判断软件使用的语言。目前仅支持简体中文和英文。
- 关于(About)里面增加了一些显示信息。
- 增加了帮助和版权信息。
- 改进了搜索功能。支持搜索时搜索下一处的连续搜索功能。
- 支持不同的书生成不同的图标。每个图标上有书名的前两个字。
- 完善了Readme,虽然同样是只有中文的Readme。
- 加入了License。我本人保留对这款软件所有的权利。如果你需要重新分发这款软件,必须附带上我的license文件。
- 提升了MIDP支持的版本。目前最低支持MIDP 2.0和CLDC 1.0。之后有空我会发一个支持MIDP 1.0 和 CLDC 1.0的版本。等之后再说吧。
这些功能使整个软件更加完善,也更加便于使用。
不足之处:
- 没有给软件想出一个好听的中文名字。我也不打算再想了,就叫evader好了。
- 搜索速度没有改进。这是因为搜索速度的改进和看书上下翻页的速度(特别是向上翻页的速度)是矛盾的,是非常矛盾的,而且我也不太想改进了,目前能够准确的搜索到需要的字符串,而且有很方便的搜索下一处功能,搜索这个东西本身在手机上就没有优势,只要能搜索到需要的字符串就可以了,我经过测试,在我的Nokia 6500s上,一百二十万字的小说,搜索最后一个字需要的时间大约是20秒左右。这对我来说,是可以忍受的。
新的软件可以在这里下载。版本号是0.1.6。zip文件里有一个jar文件,一个Readme文件,一个license文件,jar文件的名字叫做evaderGenerator.rj.ip.jar。运行时在命令行下输入:
java -jar evaderGenerator.rj.ip.jar [config file name]
使用说明可以参看Readme文件或者参看下面《一个制作Jar格式电子书的小程序》。
2009年4月13日 星期一
eBookGenerator改进计划
【2009 4 13】计划将eBookGenerator改进至0.1.6版本。
改进功能:
- 能够根据不同的电子书生成不同的图标。(目前打算在这个图标的基础上进行修改,修改之后的图标采用LGPL协议发布)。
- 在“关于”里加入版本号,文本字符数信息(总字符数,已读字符数)。
- 给自己的软件规定使用协议。
- 完善Readme。
- 给软件起一个好听容易记住的中文名字。
- 加入长时间高亮的设置选项,用户可以选择是否长时间高亮。
- 增加设置密码选项,用户可以设置阅读电子书时是否需要密码。
- 支持多国语言,目前先实现支持中文和英文,如果需要支持其他语言,编写相应的local文件加入程序jar包里即可。定义local文件格式并发布。
- 加入自动翻页选项,用户可以设置是否自动翻页,以及自动翻页的时间间隔。
- 希望能改进搜索,缩短搜索时间(因为这个功能与支持大文件和灵活翻页冲突,所以不一定能够实现)。
完成的优先级根据编号从小到大而逐渐降低,后两项不一定能够实现。这并不是因为技术原因,而是因为我的设计宗旨是,可以不需要的功能就不需要。
预计2周后发布eBookGenerator 0.1.6版本。
2009年3月25日 星期三
一个制作Jar格式电子书的小程序
[版本更新至0.1.6,增加了很多新的实用功能,2009 04 27]
具体更新说明参看上面《evader 0.1.6版本发布》,本文已经加上了新的版本下载地址。
[修改了一个愚蠢的错误,所有人都必须重新下载 2009 03 29]
因为自己比较喜欢看书,所以我经常会下载一些电子书放在手机上来看。从网上下载的电子书,大多是txt、html、chm等格式的,而我比较偏好于txt格式,因为它能够方便的制作成手机上的电子书。
比较让我遗憾的是,目前Jar格式的手机电子书制作软件我觉得都不合自己胃口,首先,这类制作软件的大部分对txt文件大小有限制,如果txt文件过大,放在手机上就有可能有OutOfMemory的错误,也就是手机的堆栈不够用;其次,在阅读的时候,大部分软件有向下翻页还比较迅速,但向上翻页的时候就非常慢,甚至出错死机的情况;再次,这类软件大部分都是windows下运行的程序,而我需要一个简单的,夸平台的程序就可以了,命令行的最好。因为上面的原因,我就花了一点时间,自己写了一个制作电子书的小软件。
这个软件为了达到夸平台的目的,是用java写成的,而且是命令行格式的,软件是用txt文件制作能够在手机上读取的Jar格式的电子书软件,这个软件主要为了解决上面提出的三个问题:
- 可以支持足够大的txt文件,只要手机能够存放下的文件,那么就能够读取这么大的文件,而且性能上不会有任何损失。
- 能够快速的定位到txt文件的任何位置,无论是向上翻页,向下翻页,还是跳转,都是非常灵活自如的。
- 跨平台支持,简单易用的命令行格式。
针对这三个要求,我用了3天左右完成了这个软件,修改Bug又花了2天时间,我把它和说明文件上传到了我的大米盘里,但是大米盘貌似被盾了,所以我选用了91box,新的0.1.6版在2009年4月27日发布了,这里是下载文件,新版本的改进参看上面《evader 0.1.6版本发布》。
我给这个软件起了个很土的名字,叫做eBookGenerator,不知道有没有人已经用了这个名字,如果没有人用,那么就叫这个了,别人再写的程序也不要再叫这个了啊,虽然我不准备为它付版权费。。。
在这里,我说明一下这个软件的使用方法:
首先要求你的系统里安装有java,可以从这里下载:http://www.java.com ,安装。
程序的使用:
windows用户在 命令提示符(cmd) 下输入,其他系统用户在各自的终端下输入:
java -jar eBookGenerator.rj.p.jar [ConfigFile]
一个命令行的小程序,有且只有一个参数,那就是配置文件。
所以,讲解如何使用这个程序,其实是讲解配置文件的格式。
配置文件的格式:
配置文件使用换行回车来作为分隔符,采用GBK编码方式,大家主意了,这个是很重要的,绝对不能有任何问题。
每一行代表一个参数,下面是参数的列表,括号里面是对它们的解释:
- 书籍名称(也就是你想要看的txt的名称)
- 作者(txt的作者)
- 创建者(应该就是你了)
- 联系方式(如果你想让看书的人联系谁,那么就写在这里,一般是Email啦)
- 说明(如果你有任何想说的话,都可以写在这,注意,不能有换行哦)
- 书籍章节数目(我把一个txt文件定义为一个章节,因为大部分书有很多章节,这些章节在不同的txt文件里,所以,你需要几个txt文件,就写几个,接下来是章节列表)
- 章节 1 的名称
- 章节 1 在硬盘上的位置(比如说C:\\我的小书第一章.txt)
- 章节 2 的名称
- 章节 2 在硬盘上的位置
- ……
- 依次类推……
- ……
- txt文件的编码方式(注意,所有txt文件都要统一编码哦,比如说GBK编码,如果是windows系统,用记事本打开没有问题的话,一般都是GBK啦)
- 希望在电子书上的编码方式(不同的编码方式,占用的存储空间不同,中文书用GBK最省空间,英文书当然是ISO-8858-1啦,也就是我们常说的Latin-1编码。中文书的话,这里一般都用GBK吧,繁体用Big5啦。)
- 一个比较重要的数(这个数是一个和看书的性能相关的数,必须大于0,一般来说,差一点的手机用128,一般的用256,好一点的用512,我不推荐大于512以上的数,越小的数,在手机上读起来越快,但是生成电子书的时候会越慢,所以,这其实是一个综合考虑的数。大家看着用啦,我的Nokia 6500s 上用256就飕飕的快,呵呵。)
好了,参数就这么多,大家应该很明白了,把你所要看书的参数写在一个文件里,用记事本就可以编辑,然后保存成GBK编码,windows用记事本的话,在编码那里选择ANSI就对啦。
下面是一个配置文件的例子,每一个配置项占一行,文件使用GBK编码,文件名叫config.txt。
生成电子书使用:
java -jar eBookGenerator.rj.p.jar config.txt
就会在当前文件夹下生成 一个.jar和一个.jad文件,.jad文件是描述程序用的。具体如何安装,就要看你的手机型号了,像我的就是把.jar文件发送到手机上就可以了。
config.txt文件:
极品家丁
禹岩
Ender
thelastender@gmail.com
架空历史小说。
3
极品家丁 第一部分
/Users/ender/JavaME/极品家丁.0.txt
极品家丁 第二部分
/Users/ender/JavaME/极品家丁.1.txt
极品家丁 第三部分
/Users/ender/JavaME/极品家丁.2.txt
GBK
GBK
256
那么我再来介绍一下这个软件吧,它有如下特性:
- 支持绝大多数手机(应该是手机支持Java,它就支持这部手机)。
- 快速阅读,无论如何翻页,跳转,都能够以很快的速度读取所要的文字。
- 支持跳转功能。
- 退出保存上次阅读章节和位置,并且每一章的阅读位置都会被保存,方便再次读取。
- 支持书签功能,可以加入书签,修改书签,跳转到书签所在位置。
- 支持文字搜索功能,可以搜索指定区间内的文字,支持跳转到搜索结果处。
- 很好的文本格式控制,能够正确的显示空格,回车,换行,提升阅读体验。
- 可以设置字体大小,颜色,背景色等等。
- 进度条实时指示文章阅读位置。
- 当然,最重要的,它完全的解决了上面我提出的3个问题,特别是支持大文件哦。
当然,很多人可能会说它怎么如此简单,但是我需要的恰恰是这样一个简单的软件,太复杂的功能,我不需要而且我觉得没有必要。
下面的非常重要哦:
因为是刚刚写成的软件,测试还很不充分,所以我希望每一个喜欢这个软件的人,能够提出自己的意见,特别是那些发现了bug的人,我很需要很需要你们的支持,请务必告诉我你所发现的bug,能帮我把它改进的更好。大家可以发邮件给我thelastender@gmail.com,标题里注明和eBookGenerator相关就可以了: ) 。
那么,下面上几张图吧:)
打开软件:

点击阅读,进入章节列表:

点击阅读,开始读书:

进度条和选项菜单:

关于:

具体的其他功能就需要各位使用着自己去体会了:)
哦,对了,这里需要说明一下,这个程序的图标是我从网上找的,不知道是谁做的,如果做的人看到了,我在这里想要说一声,不好意思,你的图标做很好看(虽然极有可能他看不懂这行文字。。。。),或者,有哪位能给这个软件做个图标呢?谢谢啦:)
我会在博客上更新和eBookGenerator相关的信息,希望关注的人有空看看就可以了。
注:大家如果不懂txt是什么,我在这里说明一下:txt就是纯文本文件格式,其他的我也不知道了。。。。。
1.连接方式:
众所周知,目前利用JDBC连接数据库的方式有4种:①. JDBC-ODBC桥;②. 部分本地API和部分Java驱动程序;③. JDBC网络纯Java驱动程序;④. 本地协议纯Java驱动程序。如下面所示:
① Java Apps <-> JDBC API <-> JDBC-ODBC Bridge <-> ODBC API <-> ODBC Level <-> Database
这种方案效率比较低,但是在只有ODBC驱动的情况下,只能使用这个方法。例如Microsoft Access。
sun.jdbc.odbc.JdbcOdbcDriver实现了JDBC-ODBC桥。
② Java Apps <-> JDBC API <-> JDBC Driver <-> Local Driver from Manufacturer <-> Database
③ Java Apps <-> JDBC API <-> JDBC Driver <-> Application Server <-> Local Driver <-> Database
Bea的WebLogic和IBM的Websphere使用了这种方法。
④ Java Apps <-> JDBC API <-> JDBC Driver <-> Database
这是所有里面效率最高的,各个大的数据库厂商都为其提供了驱动。我们现在也主要使用这种方式来连接数据库。
2.连接数据库的基本方式:
访问数据库时,有统一的步骤:
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433;databasename=pubs","user","password");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from jobs");
下面我们来详细了解一下:
java.sql.Driver接口是所有驱动程序需要实现的接口。这个接口是数据库厂商使用的。
下面是主要数据库的JDBC驱动的类名:
com.microsoft.jdbc.sqlserver.SQLServerDriver
oracle.jdbc.driver.OracleDriver
com.mysql.jdbc.Driver
接口种有一个Connection connect(String url,Properties info) throws SQLException 方法,来建立到数据库的连接。 在程序中,我们不需要直接访问这些类,而是由驱动程序管理器来调用这些驱动。我们通过JDBC驱动管理器注册每个驱动程序,使用驱动管理器类的方法来建立数据库的连接。
加载JDBC驱动是调用Class类的静态方法forName(),传递要加载的JDBC驱动的类名,运行时,类加载器从CLASSPATH环境变量中定位和加载JDBC驱动类。
我们使用DriverManager的public static void registerDriver(Driver driver) throws SQLException 来注册驱动程序类的实例。
但是一般在实现了Driver接口的驱动程序类里面都包含了静态代码块,在这个静态代码块中,会注册驱动程序自身的一个实例。
我们使用DriverManager的getConnection方法来建立到数据库的连接。
public static Connection getConnection(String url) throws SQLException
public static Connection getConnection(String url,String user,String password) throws SQLException
public static Connection getConnection(String url,Properties info) throws SQLException
Properties使用键值对来指定连接参数,通常需要指定user和password属性。
JDBC url的语法如下:
jdbc:subprotocal:subname
例如SQL Server 2000的:
jdbc:microsoft:sqlserver://localhost:1433;databasename=pubs
Oracle:
jdbc:oracle:thin:@localhost:1521:ORCL
MySQL:
jdbc:mysql://localhost:3306/databasename
另外也可以通过JDBC-ODBC桥的方式访问数据库,这时候驱动类型是:
sun.jdbc.odbc.JdbcOdbcDriver
JDBC url是:jdbc:odbc:datasource_name
3.数据库连接池:
在JDBC中,可使用DataSource接口,它由驱动程序供应商来实现。可以通过JNDI服务查询来获得DataSource对象,例如:
javax.naming.Context ctx = new javax.naming.InitialContext();
javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup("java:comp/env/jdbc/bookstore");
java.sql.Connection conn = ds.getConnection();
其中
javax.naming.Context表示一个命名上下文的接口,在这个接口中,定义了将对象和名字绑定,以及通过名字查询对象的的方法。查询一个命名的对象,使通过调用Context接口的lookup方法。
javax.naming.InitialContext是Context接口的实现。
javax.sql.DataSource接口有三种类型的实现:
① 基本的实现-产生一个标准的连接对象。
② 连接池实现 - 产生一个自动参与到连接池中的连接对象。
③ 分布式事务实现-产生一个用于分布事务的连接对象,这种连接对象几乎总是参与到连接池中。
3.1 连接池简介:
这个其实是很浅显的一个道理,将一群对象放在一个Pool中,需要的时候拿出来,不需要了放回去,这样能显著减少建立对象和销毁对象的时间。特别是数据库中,建立连接是很费时间的,而且能够同时建立的连接数也是有限的,所以采用连接池技术能极大的提高性能。
Pool技术在Apache Commons的Pool项目中。这个是需要用的一个项目。
3.2 Apache Commons DBCP
提供了一种数据库连接池的实现,这个实现用在了Tomcat中。
简单的说,DBCP提供了一个连接池,具体它的运作机制,我还正在研究中,现在仅仅是能够使用其进行基本的工作。
它有两种得到连接Connection的方式,一种是通过实现的各种的DataSource,另一种是通过DriverManager。而得到DataSource的方式也有两种,一种是通过JNDI,另一种是直接建立。
①那么我们就先来看看通过DriverManager得到Connection的方式:
GenericObjectPool connectionPool = new GenericObjectPool(null);
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "username", "password");
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true);
PoolingDriver driver = new PoolingDriver();
driver.registerPool("example",connectionPool);
Connection conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:example");
其中PoolableConnectionFactory()的构造方法使用的是PoolableConnectionFactory(ConnectionFactory connFactory, ObjectPool pool, KeyedObjectPoolFactory stmtPoolFactory, String validationQuery, boolean defaultReadOnly, boolean defaultAutoCommit)
其实就是有一个通用对象池,然后有一个基于DriverManager的产生连接的Factory,还有一个产生PoolableConnection的Factory,产生PoolableConnection的Factory使用前两者做参数,从DriverManagerConnectionFactory处获得连接,然后产生PoolableConnection,放入通用对象池中。然后通过一个Pooling的Driver,这个Driver覆盖了本身的Driver,例如mysql的Driver,DriverManager最后实际上是从这个PoolingDriver这里得到连接的。
那么接下来是通过使用DataSource得到连接的方式,只要得到DataSource,那么就可以使用其getConnection()方法得到连接了。
②下面是一种得到PoolingDataSource的方法:
GenericObjectPool connectionPool = new GenericObjectPool(null);
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "username", "password");
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true);
PoolingDataSource dataSource = new PoolingDataSource(connectionPool);
注意,使用①和②的方法的时候,要先注册驱动类,可以简单的采用Class.forName()方法。
下面则是得到BasicDataSource的方法:
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.apache.commons.dbcp.TesterDriver");
ds.setUrl("jdbc:apache:commons:testdriver");
ds.setMaxActive(getMaxActive());
ds.setMaxWait(getMaxWait());
ds.setDefaultAutoCommit(true);
ds.setDefaultReadOnly(false);
ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
ds.setDefaultCatalog("test catalog");
ds.setUsername("username");
ds.setPassword("password");
ds.setValidationQuery("SELECT DUMMY FROM DUAL");
下面则是得到BasicDataSource的另外一种方法
Properties properties = new Properties();
properties.setProperty("driverClassName", "org.apache.commons.dbcp.TesterDriver");
properties.setProperty("url", "jdbc:apache:commons:testdriver");
properties.setProperty("maxActive", "10");
properties.setProperty("maxWait", "500");
properties.setProperty("defaultAutoCommit", "true");
properties.setProperty("defaultReadOnly", "false");
properties.setProperty("defaultTransactionIsolation", "READ_COMMITTED");
properties.setProperty("defaultCatalog", "test");
properties.setProperty("username", "username");
properties.setProperty("password", "password");
properties.setProperty("validationQuery", "SELECT DUMMY FROM DUAL");
BasicDataSource ds = (BasicDataSource) BasicDataSourceFactory.createDataSource(properties);
下面是获得PerUserPoolDataSource的一种方法:
DriverAdapterCPDS pcds = new DriverAdapterCPDS();
pcds.setDriver("org.apache.commons.dbcp.TesterDriver");
pcds.setUrl("jdbc:apache:commons:testdriver");
pcds.setUser("foo");
pcds.setPassword("bar");
PerUserPoolDataSource tds = new PerUserPoolDataSource();
tds.setConnectionPoolDataSource(pcds);
tds.setDefaultMaxActive(getMaxActive());
tds.setDefaultMaxWait((int)(getMaxWait()));
tds.setPerUserMaxActive("foo",new Integer(getMaxActive()));
tds.setPerUserMaxWait("foo",new Integer((int)(getMaxWait())));
tds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
DataSource ds = tds;
下面是获得SharedPoolDataSource的一种方法:
DriverAdapterCPDS pcds = new DriverAdapterCPDS();
pcds.setDriver("org.apache.commons.dbcp.TesterDriver");
pcds.setUrl("jdbc:apache:commons:testdriver");
pcds.setUser("foo");
pcds.setPassword("bar");
pcds.setPoolPreparedStatements(false);
SharedPoolDataSource tds = new SharedPoolDataSource();
tds.setConnectionPoolDataSource(pcds);
tds.setMaxActive(getMaxActive());
tds.setMaxWait((int)(getMaxWait()));
tds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
DataSource ds = tds;
下面是得到PreparedStatement的Pool的例子:
ConnectionFactory connFactory = new DriverManagerConnectionFactory("jdbc:apache:commons:testdriver","u1","p1");
ObjectPool connPool = new GenericObjectPool();
KeyedObjectPoolFactory stmtPoolFactory = new GenericKeyedObjectPoolFactory(null);
PoolableConnectionFactory x = new PoolableConnectionFactory(connFactory, connPool, stmtPoolFactory, null, false, true);
DataSource ds = new PoolingDataSource(connPool);
Connection conn = ds.getConnection();
Statement stmt1 = conn.prepareStatement("select 1 from dual");
下面是另外一个使用PreparedStatement的Pool的例子:
ds是一个BasicDataSource
ds.setPoolPreparedStatements(true);
ds.setMaxOpenPreparedStatements(2);
然后用ds得到的连接conn创建PreparedStatement就可以看出来了。
那么我们现在再来一下利用JNDI得到DataSource的方法。
对于BasicDataSource
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
System.setProperty(Context.PROVIDER_URL, " file:///tmp");
InitialContext ic = new InitialContext();
// Construct BasicDataSource reference
Reference ref = new Reference("javax.sql.DataSource", "org.apache.commons.dbcp.BasicDataSourceFactory", null);
ref.add(new StringRefAddr("driverClassName", "org.apache.commons.dbcp.TesterDriver"));
ref.add(new StringRefAddr("url", "jdbc:apache:commons:testdriver"));
ref.add(new StringRefAddr("username", "username"));
ref.add(new StringRefAddr("password", "password"));
ic.rebind("jdbc/basic", ref);
// Use
InitialContext ic2 = new InitialContext();
DataSource ds = (DataSource) ic2.lookup("jdbc/basic");
assertNotNull(ds);
Connection conn = ds.getConnection();
assertNotNull(conn);
conn.close();
对于PerUserPoolDataSource
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
System.setProperty(Context.PROVIDER_URL, " file:///tmp");
InitialContext ic = new InitialContext();
// Construct DriverAdapterCPDS reference
Reference cpdsRef = new Reference("org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS", "org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS", null);
cpdsRef.add(new StringRefAddr("driver", "org.apache.commons.dbcp.TesterDriver"));
cpdsRef.add(new StringRefAddr("url", "jdbc:apache:commons:testdriver"));
cpdsRef.add(new StringRefAddr("user", "foo"));
cpdsRef.add(new StringRefAddr("password", "bar"));
ic.rebind("jdbc/cpds", cpdsRef);
// Construct PerUserPoolDataSource reference
Reference ref = new Reference("org.apache.commons.dbcp.datasources.PerUserPoolDataSource", "org.apache.commons.dbcp.datasources.PerUserPoolDataSourceFactory", null);
ref.add(new StringRefAddr("dataSourceName", "jdbc/cpds"));
ref.add(new StringRefAddr("defaultMaxActive", "100"));
ref.add(new StringRefAddr("defaultMaxIdle", "30"));
ref.add(new StringRefAddr("defaultMaxWait", "10000"));
ic.rebind("jdbc/peruser", ref);
// Use
InitialContext ic2 = new InitialContext();
DataSource ds = (DataSource) ic2.lookup("jdbc/peruser");
assertNotNull(ds);
Connection conn = ds.getConnection("foo","bar");
assertNotNull(conn);
conn.close();
还有一种方式是这样的:
首先新建一个DataSource,无论是什么DataSource都行,这里,例如PerUserPoolDataSource。
PerUserPoolDataSource dataSource = new PerUserPoolDataSource();
Hashtable environment = new Hashtable();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
Context context = new InitialContext(environment);
context.createSubcontext("jdbc");
context.bind("jdbc/test",dataSource);
Context ctx = new InitialContext(environment);
DataSource ds = (DataSource)context.lookup("jdbc/test");
可以看出,这样的方式并没有对DataSource进行必要的配置。
其实结合JNDI和普通创建的方式来看,一个是利用DriverAdapterCPDS和相应类直接创建,另一个是利用DriverAdapterCPDS和相应类的工厂创建。
相比起利用DriverManager的方式,利用JNDI的方式只不过是使用DriverAdapterCPDS这个类从驱动中实现了一个ConectionPoolDataSource,然后再用相应的其他需要的DataSource进行包装即可。看过BasicDataSource和PoolingDataSource的源代码之后,发现其实这个很简单,BasicDataSource是利用DriverConnectionFactory产生的连接,然后得到一个对象池,将其传递给PoolingDataSource,在PoolingDataSource里,对象池还是对象池,只是生成连接的时候使用了一个扩展自DelegatingConnection类的类:PoolGuardConnectionWrapper将Connection进行包装才传递给外面的。这两个相当于利用的方式方法都比较简单。
而InstanceKeyDataSource则更加复杂,里面用到了Java里面的javax.sql.ConnectionPoolDataSource接口,如果有些厂家实现了ConnectionPoolDataSource,那么就可以直接拿来用,如果没有,那么使用DriverAdapterCPDS从驱动中得到一个。
其实有三个数据源接口:
javax.sql.DataSource(普通情况下面用);
javax.sql.XADataSource(多个数据库联合做 transaction);
javax.sql.ConnectionPoolDataSource(连接池 datasource)。
从这里看出,数据源和DriverManager是很不同的,具体使用哪中方式大家自己决定。这里我们引出一个比较重要的话题,那么就是如果选择连接数据库的方式。
从上面可以看出,DBCP是使用的自己的Pool来实现的连接池(BasicDataSource和PoolingDataSource)。而使用Java EE标准的ConnectionPoolDataSource接口的则是使用DriverAdapterCPDS的,有人说Tomcat使用的是BasicDataSource,我还没来得及研究。一般来说,做Java EE开发,Server一般都有自己的连接池,到时候使用就可以了,普通的就用DBCP的BasicDataSource就可以了,这个可以用在非Java EE的应用上,是一个不错的好处。其实连接池这种东西,原理很简单,关键是要高效稳定,ConnectionPoolDataSource是Sun定义的接口,并不是因为它好或者怎么样。像Hibernate和Spring都只用了DataSource。
Oracle和MySQL在其驱动里都实现了ConnectionPoolDataSource,例如com.mysql.jdbc.jdbc2.optional.MySQLConnectionPoolDataSource,其实只要用这个就行了,而比如说你有一些特殊应用,例如就想PerUserPoolDataSource的设计目的一样,不同用户配置不同,这些的时候,你可以用一下DBCP里的相关的,但是其实都有了ConnectionPoolDataSource,其他的就很简单了。而DriverAdapterCPDS是使用驱动(没有ConnectionPoolDataSource)实现一个ConnectionPoolDataSource,所以一般还是不要用的好。
另外,在研究DBCP时,发现JNDI自己学的不好,所以找机会要好好的补一下了。
貌似让人有点犯迷糊了,那么我们接下来结合DBCP的包和类的组成,来看看吧。
DBCP有4个包:
org.apache.commons.dbcp 数据库连接池API
org.apache.commons.dbcp.cpdsadapter 这个包包含了一个公共类,实现了ConnectionPoolDataSource (CPDS),这个类能够使用早期的基于驱动的实现。其实是所有驱动相关的都和这个有关。
org.apache.commons.dbcp.datasources 这个包包含两个DataSources:PerUserPoolDataSource 和 SharedPoolDataSource,这两个类提供数据库连接池。
org.apache.commons.jocl Java Object Configuration Language,一个XML程序,能够描述需要实例化的Java对象。
接下来一个包一个包介绍:
org.apache.commons.dbcp 这个包是最重要的包了,里面就是数据库连接池的实现。
它包含很多类,一个接口ConnectionFactory,这个是用来创建连接的接口。
和ConnectionFactory相关的类有:
DataSourceConnectionFactory,基于DataSource的实现。
DriverConnectionFactory,基于Driver的实现。
DriverManagerConnectionFactory,基于DriverManager的实现。
PoolableConectionFactory,能够实现PoolableConnection的ConnectionFactroy。
PoolableConnection,是一种代理的Connection,能够放进一个ObjectPool。
PoolablePreparedStatement,这个同样也是基于代理,和PoolingConnection结合起来实现一个存放PreparedStatement的Pool。
和DataSource相关的类:
PoolingDataSource,一个简单的DataSource实现,能够从ObjectPool中得到Connection。
BasicDataSource,一个基本的javax.sql.DataSource的实现,其实是一个JavaBean,为基本的应用提供了一站式解决方案。
同样,我们有BasicDataSource的能够利用JNDI生成的BasicDataSourceFactory类,这里需要注意,BasicDataSourceFactory类有一个createDataSource(Properties)的方法,可以生成BasicDataSource。
Pooling相关:
PoolingDriver,一个Driver的实现类,能够从注册了的ObjectPool获得Connection。
PoolingConnection,一个能够实现存放PreparedStatement的Pool的基于代理的Connection。
代理类,都是相应类的利用代理的设计模式的实现。
DelegatingCallableStatement
DelegatingConnection
DelegatingPreparedStatement
DelegatingResultSet
DelegatingStatement
org.apache.commons.dbcp.cpdsadapter 这个包前面说过了,就包括一个DriverAdapterCPDS类,实现了ConnectionPoolDataSource (CPDS),实现和驱动相关的东西。
org.apache.commons.dbcp.datasources 这个包主要包括PerUserPoolDataSource 和 SharedPoolDataSource,以及它们使用JNDI时的工厂类,还有一个它们的基类。PerUserPoolDataSource对于不同用户使用不同的连接池,而且可以调整设置每个用户的参数,SharedPoolDataSource则所有使用相同的设置,分享最多的连接。
org.apache.commons.jocl 这个包里有两个类,ConstructorUtil 和 JOCLContentHandler。前面说过了,这是使用配置文件生成连接池的方式,但是这个有点复杂,对于我来说没什么大的用处,我就没有研究,但是这不影响对DBCP的大部分使用。
那么我们需要再说明一下DBCP的设置参数:
username 用户名
password 密码
url 连接的url
driverClassName 驱动器类名
connectionProperties 生成新的连接时,JDBC驱动使用的参数,格式是 [propertyName=property;]*
其他的参数如下,来自于DBCP的文档,这里就不详细的说了,在这里可以看一下。
我在这里写了一个XML配置文件,用来配置DBCP,文件名为database-config.xml,可以在这里找到。
可以同时设置多个数据库,利用default来设置默认的数据库,也可以在相应的数据库类初始化的时候设置采用的数据库。
4.访问数据库:
数据库中有三个接口,分别是Statement,PreparedStatement和CallableStatement,它们定义了访问数据库的不同方式。
下面是一些语句和注意点:
Statement
Connection接口创建Statement:
Statement createStatement() throws SQLException
ResultSet executeQuery(String sql) throws SQLException
查询,返回ResultSet对象。
int executeUpdate(String sql) throws SQLException
执行INSERT,UPDATE或者DELETE,也可执行SQL DDL语句,如CREATE TABLE。
boolean execute(String sql) throws SQLException
有多个结果集返回的语句。返回true表示返回的第一个结果是ResultSet,返回false表示返回的是一个更新的行数或者没有结果。可以使用getResultSet()或者getUpdateCount()来获取结果,并且调用getMoreResults()方法返回下一个结果集。
int[] executeBatch() throws SQLException
提交一批命令,使用addBatch()方法来将SQL命令加入到列表中。
一个Statement对象在同一时刻只能有一个打开的ResultSet对象,在Statement接口中定义的所有executeXXX()方法都隐含的关闭Statement当前的ResultSet对象。
PreparedStatement
从Connection的preparedStatement方法得到PreparedStatement对象。
PreparedStatement pstmt = conn.preparedStatement("insert into employee values(?,?,?)");
然后调用set方法:
pstmt.setDate(3,java.sql.Date.valueOf("2004-5-8");
pstmt.executeUpdate();
CallableStatement
用于执行SQL存储过程。
CallableSatement cstmt = conn.preparedCall("call p_changesal(?,?)");
在执行存储过程前,凡是存储过程中类型为OUT的参数必须被注册,对于类型为IN的参数,可以通过set方法来设置。
cstmt.registerOutParameter(2,java.sql.Types.INTEGER);
cstmt.setInt(1,7358);
cstmt.execute();
int sal = cstmt.getInt(2);
ResultSet
有很多get方法,例如getString()等,一般都提供两种参数,一种以列的索引为参数(从1开始),一种以列的名字为参数。
ResultSetMetaData
使用ResultSet的getMetaData()得到。
有如下方法:
int getColumnCount() throws SQLException
int getColumnDisplaySize(int column) throws SQLException
String getColumnName(int column) throws SQLException
int getColumnType(int column) throws SQLException
得到的是JDBC类型,在java.sql.Types类中定义。
String getColumnTypeName(int column) throws SQLException
返回数据库特定的类名。
String getTableName(int column) throws SQLException
DatabaseMetaData
获取数据库的信息,使用Connection的getMetaData()得到。
ParameterMetaData
获取PreparedStatement对象中的参数和属性信息。
使用PaeparedStatement对象的getParameterMetaData()方法得到。
事务处理。
使用Connection的setAutoCommit()方法,传递false,取消自动提交。
使用Connection的rollback()进行回滚,使用commit()提交事务。
可滚动的结果集。
创建Statement的时候,可以使用重载的方法:
Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
创建PreparedStatement的时候,也有重载的方法:
PreparedStatement preparedStatment(int resultSetType, int resultSetConcurrency) throws SQLException
resultSetType:
ResultSet.TYPE_FORWARD_ONLY 只能向前滚动。
ResultSet.TYPE_SCROLL_INSENSITIVE 可以滚动,但是对数据变化不敏感。
ResultSet.TYPE_SCROLL_SENSITIVE 可以滚动,对数据变化敏感。
resultSetConcurrency:
ResultSet.CONCUR_READ_ONLU 不能更新数据库
ResultSet.CONCUR_UPDATABLE 可以更新数据库
通过DatabaseMetaData的两个方法来判断数据库是否支持可以滚动和可更新的结果集。
boolean supportsResultSetType(int type) throws SQLException
boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException
ResultSet提供了下面的方法来支持结果集的滚动。
boolean isBeforeFist() throws SQLException
boolean isAfterLast() throws SQLException
boolean isFirst() throws SQLException
boolean isLast() throws SQLException
void beforeFirst() throws SQLException
void afterLast() throws SQLException
boolean first() throws SQLException
boolean last() throws SQLException
boolean absolute(int row) throws SQLException
boolean previous() throws SQLException
boolean relative(int row) throws SQLException
ResultSet提供了下面的方法用于修改结果集。
更新一行
void updateXXX(int columnIndex, XXX x) throws SQLException
void updateXXX(String columnName, XXX x) throws SQLException
调用上面的方法之后,还要调用updateRow()方法才能有效。
如果不想更改,那么在updateRow()方法之前调用cancelRowUpdates()方法。
可以调用rowUpdated()方法来判断当前行是否被更新。
插入一行
先调用moveToInsertRow(),插入行是一个与可更新结果集相联系的特殊的缓存行。
然后调用updateXXX()方法,然后调用insertRow()方法。
当游标在插入行的时候,只能调用updateXXX(),getXXX(),和insertRow()方法。
在调用getXXX()之前必须先调用updateXXX()。
可以用rowInserted()来判断当前行是否是插入行。
删除一行
使用deleteRow()来删除一行,当游标指向插入行的时候,不能调用这个方法。
一个被删除的行可能在结果集中留下一个空的位置。可以用rowDeleted()方法来判断一行是否被删除。
可以更新结果集必须满足:
只能针对数据库中单张表操作。
查询语句不能包含任何的join操作。
查询操作的表中必须有主键,而且在查询的结果集中必须包含作为主键的字段。
如果还想执行插入,还必须满足:
查询操作必须选择数据库表中所有不能为空的列。
查询操作必须选择所有没有默认值的列。
SQL数据类型与Java数据类型的关系:
INTEGER,INT int
TINYINT,SMALLINT short
BIGINT long
DECIMAL,NUMERIC java.math.BigDecimal
FLOAT float
DOUBLE double
CHAR,VARCHAR String
BOOLEAN,BIT boolean
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
BLOB java.sql.Blob
CLOB java.sql.CLOB
ARRAY java.sql.Array
5.附加内容-对象池的使用
在DBCP里,使用了apache commons的另一个项目Pool的结果。
即org.apache.commons.pool
这里简单介绍一下它的使用方法。
相比起DBCP来,Pool简单多了。
它有两个包:
org.apache.commons.pool 对象池API。
org.apache.commons.pool.impl 对象池API的实现。
在org.apache.commons.pool包里:
ObjectPool是一个接口:
public interface ObjectPool {
Object borrowObject();
void returnObject(Object borrowed);
}
BaseObjectPool是其抽象实现,建议都继承它来实际使用对象池。
KeyedObjectPool是另外一个接口,能够使用key得到对象:
public interface KeyedObjectPool {
Object borrowObject(Object key);
void returnObject(Object key, Object borrowed);
}
BaseKeyedObjectPool是其抽象实现。
PoolableObjectFactory是一个接口,对于想放进池里的对象,需要实现这个接口:
public interface PoolableObjectFactory {
Object makeObject();
void activateObject(Object obj);
void passivateObject(Object obj);
boolean validateObject(Object obj);
void destroyObject(Object obj);
}
BasePoolableObjectFactory是其抽象实现。
KeyedPoolableObjectFactory是另一个接口,定义了要放进KeyedObjectPools的类需要实现的接口:
public interface KeyedPoolableObjectFactory {
Object makeObject(Object key);
void activateObject(Object key, Object obj);
void passivateObject(Object key, Object obj);
boolean validateObject(Object key, Object obj);
void destroyObject(Object key, Object obj);
}
BaseKeyedPoolableObjectFactory是其抽象实现。
在这个包里,除了上面说的外,还包括KeyedObjectPool和ObjectPool的工厂接口。
同时还包括PoolUtils类。
在org.apache.commons.pool.impl里,有
StackObjectPool 提供一个有限对象的对象池,是LIFO的。
StackKeyedObjectPool 提供Keyed对象池,是LIFO的。
GenericObjectPool 通用的对象池,提供了广泛的设置参数,可以设置成LIFO或者FIFO的。
GenericKeyedObjectPool 通用Keyed对象池,是FIFO的。
SoftReferenceObjectPool 提供一个无限的对象池,允许垃圾回收器回收,是LIFO的。
包里其他还有相应Pool的工厂类,还提供了两个个Generic Pool的Config类。
举一下例子:
比如说我们想要得到一个StringBuilder的对象池。
那么,我们建立一个StringBuilderFactory类,然后让其实现PoolableObjectFactory接口。
我们可以直接继承BasePoolableObjectFactory,这也是推荐的做法。
import org.apache.commons.pool.BasePoolableObjectFactory;
public class StringBuilderFactory extends BasePoolableObjectFactory {
// for makeObject we'll simply return a new buffer
public Object makeObject() {
return new StringBuilder();
}
// when an object is returned to the pool,
// we'll clear it out
public void passivateObject(Object obj) {
StringBuilder buf = (StringBuilder)obj;
buf.setLength(0);
}
// for all other methods, the no-op
// implementation in BasePoolableObjectFactory
// will suffice
}
然后把这个类传入想要的Pool的构造函数里就可以了:
ObjectPool pool = new StackObjectPool(new StringBufferFactory());
OK,就到这里吧,讲了不少,只要能有讲了之后的效果就行。
十二月 27th, 2008 in
Java,
Program | tags:
dbcp,
java,
pool,
对象池,
数据库 |
No Comments
虽然ESRI的官方文档(英文)已将如何安装ArcSDE解释的很详细了,而且网上也有很多参考资料,但是这里还是针对自己的习惯以及针对ubuntu,用中文做一下说明,只是为了之后自己方便查阅。
1. 安装环境
我们安装的ArcSDE版本是9.1 for Oracle 10gR2,根据ESRI的官方文档,针对Oracle 9i说明文档的可以直接用来说明如何在10g上安装,除了下面一些改动。
1. All references to Oracle9i can be changed to Oracle10gR2.
2. All references to the sdesetupora9i command should be substituted with sdesetupora10g.
3. All references to the sdeservice –o create –d oracle9i,SID command should be substituted with sdeservice –o create –d oracle10g,SID
4. UNIX installations/System Requirements:
The ArcSDE 9.1 for Oracle10gR2 release is available on the following UNIX and Linux platforms: Solaris 64bit, AIX 64bit, Red Hat Linux.
2. 安装前的准备
a. 安装好Oracle 10g
在ubuntu上安装Oracle 10g可以参考我的另一篇文档。
b. 创建并设置好sde用户
ArcSDE需要一个名为sde的用户,这个用户其他的属性都不重要,所以我将其所属的组设置为admin,以便于使用sudo。
官方文档上说shell应当设置为/bin/sh或者/bin/csh,我这里设置成bash,也是可以的。
同时,将sde加入oinstall组,是为了使之能运行一些oracle命令,同时使用oracle的一些动态链接库。
创建用户
sudo useradd -g admin -G oinstall -d /home/sde -s /bin/bash -m -k /etc/skel sde
设置密码
sudo passwd sde
输入你为sde用户设置的密码。
然后su sde,登录sde用户,或者直接退出当前用户,用sde用户登录。
c. 设置环境变量
在.bashrc文件里修改(ubuntu默认.profile文件读取.bashrc文件,而且.bashrc文件在每次登录的时候都会读取),需要设置的环境变量如下:
SDEHOME #ArcSDE安装的地方
ORACLE_HOME #Oracle安装的地方
ORACLE_SID #Oracle SID的值
TNS_ADMIN #tnsnames.ora文件所在的地方
PATH=$PATH:$SDEHOME/bin:$ORACLE_HOME/bin
LD_LIBRARY_PATH=$SDEHOME/lib:/usr/lib:/lib:$ORACLE_HOME/lib #(All platforms except HP and IBM)
SHLIB_PATH=$SDEHOME/lib:/usr/lib:/lib:$ORACLE_HOME/lib #(for HP only)
LIBPATH=$SDEHOME/lib:/usr/lib:/lib:$ORACLE_HOME/lib #(for IBM only)
TWO_TASK #value for TWO_TASK #(if ArcSDE and Oracle on different machines)
如果Oracle数据库安装在本地,那么ORACLE_HOME和ORACLE_SID是需要设置并且起作用的,而,如果设置了TWO_TASK,那么它们就不起作用。
TWO_TASK指出在远程安装的Oracle的地址。如果在本地安装的Oracle,那么就不需要指出TNS_ADMIN和TWO_TASK。
注意:ArcSDE启动的时候,giomgr进程会读取$SDEHOME/etc/dbinit.sde文件中的设置,这会覆盖掉在.profile文件中的设置。
我的环境变量的设置如下:
umask 022
export ORACLE_BASE=/opt/oracle
export SDEHOME=/home/sde/sdeexe91
export ORACLE_HOME=/opt/oracle
export ORACLE_SID=GIS
export PATH=$SDEHOME/bin:$ORACLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$SDEHOME/lib:/usr/lib:/lib:$LD_LIBRARY_PATH:$ORACLE_HOME/lib/stubs
d. 创建Oracle数据库用户和tablespace
d.i. 创建tablespace
tablespace至少需要200M的空间,我们这里创建成2G,最大是unlimited。
由于Oracle推荐使用本地管理表空间,那么这里就设置成本地管理。关于本地管理和数据字典管理的定义和比较,请参阅下面的文档:
Locally Managed Tablespaces:
A tablespace that manages its own extents maintains a bitmap in each datafile to keep track of the free or used status of blocks in that data file. Each bit in the bitmap corresponds to a block or a group of blocks. When an extent is allocated or freed for reuse, the Oracle server changes the bitmap values to show the new status of the blocks.
Dictionary-Managed Tablespaces:
For a tablespace that uses the data dictionary to manage its extents, the Oracle server updates the appropriate tables in the data dictionary whenever an extent is allocated or deallocated.
Advantages of Locally Managed Tablespaces:
Locally managed tablespaces have the following advantages over dictionary-managed tablespaces:
Local management avoids recursive space management operations, which can occur in dictionary-managed tablespaces if consuming or releasing space in an extent results in another operation that consumes or releases space in a undo segment or data dictionary table.
Because locally managed tablespaces do not record free space in data dictionary tables, it reduces contention on these tables.
Local management of extents automatically tracks adjacent free space, eliminating the need to coalesce free extents.
The sizes of extents that are managed locally can be determined automatically by the system. Alternatively, all extents can have the same size in a locally managed tablespace.
Changes to the extent bitmaps do not generate undo information because they do not update tables in the data dictionary (except for special cases such as tablespace quota information).
创建tablespace的语句是:
CREATE TABLESPACE sde DATAFILE
'/opt/oradata/GIS/SDE/SDE.dbf' SIZE 2000M
AUTOEXTEND ONNEXT 32M
MAXSIZE UNLIMITED
LOGGING
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M
SEGMENT SPACE MANAGEMENT AUTO;
在做这部之前,需要用oracle用户登录,然后在/opt/oradata/GIS/下创建SDE文夹。
其中,EXTENT MANAGEMENT LOCAL是指本地管理方式,UNIFORM 是指对于一个表来说,每次增加分配的空间大小(extent的大小)。
而NEXT是指对于tablespace来说,每次空间不够的时候再次分配的大小。
这里有一段话,可以表明这两者的一些区别:
“每次分配extent时是一个消耗资源的操作,当然是分配次数越少越好。在有些情况下,这也会直接影响性能。例如,在执行批量插入时,如果你的 extent太小,很快就要用完,就得再分配下一个,这时insert进程就只能等待系统去分配下一个extent.在这种情况下分配大一点的 extent会有助于提高性能。另外一点是extent是由连续的block组成的,extent大的话,数据存放会比较集中。这对于顺序读可能有利,但对于随机读则不见得有什么好处。并且现在数据文件基本上都是放在存储上,存储已经把空间分散到不同的磁盘上了。即使在oracle看来是连续的空间,在存储上实际也可能是分散的。
如果你的表确实很大,那就分配大一些的extent,如果表比较小就分配小一些的extent.”
我的理解是,如果tablespace的空间不足了,那么分配NEXT标明的大小。在tablespace空间足够的情况下,在往tablespace中的table里插入数据的时候,如果一次插入的数据大于UNIFORM的值,那么就需要再次分配UNIFORM规定的大小。
所以,如果每次插入的数据量比较大,那么可以考虑UNIFORM的值大一些。
SEGMENT SPACE MANAGEMENT应当设置为AUTO,这个网页http://www.databasejournal.com/features/oracle/article.php/1576991 阐述了为什么设置成AUTO的好处。
其余参数可以参看Oracle的说明,这里推荐一个网页:http://www.psoug.org/reference/tablespaces.html
同时,这里有两幅Oracle提供的图,能够很清楚的看出如何创建和修改tablespace。
创建tablespace:

修改tablespace:

d.ii. 创建sde用户。
使用sqlplus,创建的sql语句是:
CREATE USER sde
IDENTIFIED BY 你的密码
DEFAULT TABLESPACE sde
TEMPORARY TABLESPACE TEMP;
创建用户的sql语句很简单,关于其参数可以参看这个网页http://www.psoug.org/reference/user.html
3. 安装
a. 下载和解压
从http://support.esri.com/index.cfm?fa=downloads.patchesServicePacks.viewPatch&PID=19&MetaID=1155 下载ArcSDE 9.1 for Oracle10gR2,如果你还想打补丁,那么也在附近的网页找找,下载相应的补丁文件。
打补丁的方法本文不涉及,非常简单,稍微看一下就会了。
解压下载的sde91-ora10gR2-lx.tar.Z文件,由于ubuntu没有compress命令,所以不能用tar Zxvf xxx.tar.Z解压。
应当先uncompress sde91-ora10gR2-lx.tar.Z得到sde91-ora10gR2-lx.tar文件
然后再tar xvf sde91-ora10gR2-lx.tar 即可。
貌似也可以用gunzip解压,请参看其manual查阅解压参数。
解压之后会得到安装文件以及一些必要的文档,其实参看这些附带的文档就可以很好的完成安装,不必看我这篇文章。
b. 安装
进入解压之后的文件夹
cd linux/oracle10g/
运行
./install -load
如果你的LANG环境变量不是英语,那么就会出现一个提示,然你看目前显示的语言是否可以读(不是乱码),我的local设置的是zh_CN.UTF-8
所以出来乱码,我也不知道怎么能让显示中文正常,所以就输入no,用英文显示。
首先需要同意license,必然是yes。
接下来需要注意的是选择CD-ROM 挂载点,因为ArcSDE默认为是从光驱安装的,所以,在这里只要输入ArcSDE安装文件所在的位置就可以了。
我的是/home/sde/linux/oracle10g。
接下来输入要安装的位置,我的是/home/sde
然后接下来自己看自己需要的选择,因人而异,我一路回车下来的。
如果在安装的过程中,想回到上一个选项,那么输入’^'。
之后在/home/sde下面有sdeexe91文件夹,里面是安装的文件。
4. Post Installation
也即安装完成之后到启动服务之前的配置。
a. 设置SDE的配置文件
修改/etc/services文件,加入:
esri_sde 5151/tcp # ArcSDE service on pinetree
但是对于ubuntu来说,5151这个端口被占用了,如下:
pcrd 5151/tcp # PCR-1000 Daemon
那么可以修改pcrd的端口,也可以修改sde的端口。在这里,我们修改pcrd的端口号为5152。
sde的服务名称为esri_sde,也可以改为别的,我们这里就用它。
同时修改$SDEHOME/etc/services.sde,也加入:
esri_sde 5151/tcp # ArcSDE service on pinetree
同时还可以根据需要修改$SDEHOME/etc下的其他文件。
dbinit.sde #正如前面提到的,这个文件下的参数会覆盖掉.profile或者.cshrc文件下的参数。设置的方法是set (variablename)=(value)
giomgr.defs #这个文件定义了sde服务器如何运行的一些参数,默认的参数在大部分情况下已经够用了。
#如何设置,请参阅ArcSDE Configuration and Tuning Guide for Oracle(安装包里附带的一个pdf文件)。
#在执行了sdesetupora10g命令之后,这个文件里的参数会被读入数据库,生成一个名为server_config的表。
#如果想要在运行了sdesetupora10g命令之后修改参数,请参阅sdeconfig命令。
dbtune.sde #这个文件定义了数据库中表的物理存储参数。同样参阅ArcSDE Configuration and Tuning Guide for Oracle文档。
#运行了sdesetupora10g命令之后,这个文件里的参数会被读入数据库,生成一个名为dbtune的表。
#如果想要在运行了sdesetupora10g之后修改参数,请参阅sdedbtune命令。
#sdeconfig命令和sdedbtune命令都可以在ArcSDE Administration Command Reference(安装包里附带的html和chm文件)中找到详细说明。
b. 修改数据库执行权限。
用sqlplus登录数据库,运行下面的命令:
grant execute on dbms_pipe to public;
grant execute on dbms_lock to public;
同时还需要修改数据库中sde用户的权限,需要给它加入这些权限。
具体的命令在这里。
在安装完成之后,为了安全起见,还需要撤销sde用户的一些权限,在这里。
上面这步做不做都可,对于不同的应用,个人有不同的选择。
c. 执行sdesetupora10g命令
这个命令的作用是在数据库中生成相应的地理信息数据表,以及一些元数据表,同时设置ArcSDE存储的方式等。
要启动sde服务,这个命令必须执行成功。
sdesetupora10g的执行参数是:
-?
-h
-o upgrade [-H (sde_directory)] [-u (DB_Admin_user)] [-p (DB_Admin_password)] [-D (database)] [-s datasource] [-l (key)] [-N] [-q]
-o list [-H (sde_directory)] [-u (DB_Admin_user)] [-p (DB_Admin_password)] [-D (database)] [-s datasource] [-q]
-o install [-H (sde_directory)] [-u (DB_Admin_user)] [-p (DB_Admin_password)] [-D (database)] [-s datasource] [-l (key)] [-N] [-q]
-o update_key -l (key) [-u (DB_Admin_user)] [-p (DB_Admin_password)] [-D (database)] [-H (sde_directory)][-s datasource] [-N] [-q]
我们这里是第一次执行,而且是安装,所以用-o install参数。-o list参数列出所安装的ArcSDE版本。
这个命令的详细信息可参看ArcSDE Administration Command Reference。
运行命令:
sdesetupora10g -o install -u sde -p 你的密码
如果最后显示Successfully installed ArcSde.
那么恭喜你,最重要和最困难的一步完成了。
5. 认证
ArcSDE是收费软件,那么必然需要一个认证文件来确保你是正确合法的购买的ArcSDE,而且正确的使用。
对于我们中国人来说,对外国的版权费早在清朝的时候已经交过了,所以放心的使用“盗版”吧,不过不要让他们发现就好。
认证文件是一个.ecp文件,要用sdesetupora10g命令的-o update_key参数来注册。
其实也可以在上面安装的一步,-o install的时候,附加上-l参数来注册。
命令是:
sdesetupora10g -o update_key -l 你的.ecp文件 -u sde -p 你的密码
6. 启动ArcSDE服务
命令
sdemon -o start
会让你输入
Please enter ArcSDE DBA password:
输入相应密码。
如果显示:
ArcSDE Instance esri_sde started Mon Jul 14 21:53:54 2008
而且运行
sdemon -o status
显示的是Accepting Connections.
那么恭喜你,服务已经启动了。
如果要设置随着机器自动启动,那么创建sde.sh文件,加入这里的内容。
然后:
chmod 755 sde.sh
sudo cp sde.sh /etc/init.d/
sudo update-rc.d sde.sh defaults 99
就可以了。
7. 卸载
卸载ArcSDE很简单。
首先停止服务:
sdemon -o shutdown
然后删除$SDEHOME文件夹:
rm -R $SDEHOME
最后删除掉/etc/services里相应的服务项,就可以了。
8. 其他
这里稍微提一下升级的事情,也就是打补丁的事情。
ESRI官方网站上到目前提供的是ArcSDE9.2的补丁,那么去下载了,然后参照网站上的说明做,就可以了。
这里需要注意的是,如果升级,那么还需要给sde添加前面提到的权限才可以。
本文描述在Ubuntu 8.04下安装Oracle 10gR2的方法。对于Ubuntu6.06以上的版本,安装Oracle 10gR2应该都是没有什么问题的,但是仍然推荐8.04版,因为它各方面性能比之前的版本有了很大幅度的提高。
1. 系统
Oracle需要有X系统,如果你没有的话,那么就安装一个你需要的X系统,我安装的Ubuntu 8.04 Desktop版本,所以用的是GNOME。如果你安装的是server版,那么可以安装fluxbox之类的轻量X系统,说句题外话,我个人还是倾向于FVWM,曾经在FreeBSD下配置了一个非常不错的FVWM,但是由于年代久远,当时的东西没有留下来,现在也没有那个时间和精力去配置,所以一直是一个遗憾。
2. 需要安装的软件以及一些配置
sudo apt-get install gcc make binutils libmotif3 lesstif2 libaio1 gawk libc6 libc6-dev alien rpm libstdc++5 sun-java6-jre gcc-3.3-base build-essential autoconf automake1.9
如果空间足够,那么可以考虑安装java-jdk。
或许不需要这么多,我是这么装的,各位可以试验一下究竟都需要什么。
安装完成之后,需要做一些修改。
sudo ln -s /usr/bin/rpm /bin/rpm
sudo ln -s /usr/bin/basename /bin/basename
sudo ln -s /usr/bin/awk /bin/awk
然后创建/etc/redhat-release,因为Oracle需要安装在获得其认证的Linux发行版上,Redhat是其中之一,那么就需要让它认为Ubunut是Redhat。
内容是Red Hat Linux release 3.1 (drupal)
你写成Red Hat Linux release 4.1 也可。
3. 创建Oracle用户和安装路径
sudo groupadd oinstall
sudo groupadd dba
sudo groupadd nobody
sudo useradd -g oinstall -G dba,admin -d /home/oracle -s /bin/bash -m -k /etc/skel oracle
oracle用户是admin的组员的原因是为了使之可以使用sudo命令。
为Oracle用户设置密码
sudo passwd oracle
创建安装和运行Oracle的文件夹。
sudo mkdir -pv /opt/oracle
sudo mkdir -pv /opt/oradata
sudo chown -R oracle:oinstall /opt/ora*
sudo chmod -R 755 /opt/ora*
4. 对系统进行调整,满足安装Oracle的条件。
修改/etc/sysctl.conf,加入下面的代码
kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
fs.file-max = 65536
net.ipv4.ip_local_port_range = 1024 65000
然后运行
sudo /sbin/sysctl -p
修改/etc/security/limits.conf,加入
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft nofile 1024
oracle hard nofile 65536
修改/etc/profile (如果你用的是bash的话)
if [ $USER = "oracle" ]; then
if [ $SHELL = "/bin/ksh" ]; then
ulimit -p 16384
ulimit -n 65536
else
ulimit -u 16384 -n 65536
fi
umask 022
fi
注意:这里稍微解释一下umask的功能,umask的作用是决定你创建的文件或目录的缺省权限。
umask使用的对于目录的最大的权限是777,即rwxrwxrwx;而对于文件最大的权限是666,即rw-rw-rw- ;
如果我们运行命令:umask 033,则033与目录的最大权限值777进行异或运算,所以建立的新目录缺省权限为:744 ,即rwxr–r–。
而对于新建立的文件则是033与文件的最大权限值666进行异或运算,所建立的新文件的缺省权限为:644 ,即rw-r–r–。
简单的说,公式就是:对于文件夹777-umask,对于文件666-umask。
那么由于文件的最大权限是666,则可以看出,对于文件的执行权限(即x权限)使用umask命令是无法使其起作用的。
系统强制关闭文件的x缺省执行权限。如果想使文件有运行权限,只能由 chmod 命令进行设置。
检查/etc/services,确保没有使用1830到1849的端口号。
5. 配置Oracle用户的环境变量
以oracle用户登录,或者在终端下:
su oracle
这时候可能遇到No protocol specified的问题。解决方法是用root用户登录(su root),然后运行xhost +
或者直接退出当前用户的X,用oracle登录。
修改.bashrc文件,加入
umask 022
export ORACLE_BASE=/opt/oracle
export ORACLE_HOME=/opt/oracle
export ORACLE_SID=GIS #这个改成自己想要的即可
export PATH=$ORACLE_HOME/bin:$PATH
然后
source .bashrc
建议使用英文环境来安装,防止乱码
export LANG="en_US.UTF-8"
export LANGUAGE="en_US:en"
6. 安装
将下载的Oracle 10g安装文件解压,进入解压后的目录
./runInstaller
然后就开始安装了,最好选择Advanced 方式安装,便于选择一些配置参数。
接下来的选项就因人而异了,最后有一个选择privilege operation system 的什么选项,然填一个oracle用户所在的组,我填的admin。
下面的图是数据库创建完成之后的图片:

最后会跳出让你执行orainstRoot.sh和root.sh的窗口,打开一个终端,用root用户执行即可,执行完了之后回到这个窗口,点OK。

记下该有的URL,然后就可以退出安装程序了。

7. 启动
这时,oracle已经启动了,例如在浏览器里输入http://localhost:1158/em,则会显示出登录的界面。

输入用户名和密码,则登录进管理页面。

在命令行下输入
sqlplus "sys/你的密码 as sysdba"
则可以登录。
那么,我们需要设置一个脚本,来使oracle之后能够正常启动。
vim oracle.sh
加入这里的内容。
注意,执行的时候,无论是
./oracle.sh start|stop
还是
sudo ./oracle.sh start|stop
都可以,因为已经export了环境变量了。
注意,这里的dbstart和dbshut都是为了给脚本用的,正确的方法是:
sqlplus "sys/你的密码 as sysdba"
然后startup或者shutdown。
那么,为了使dbstart和dbshut可以使用,需要修改/opt/oracle/bin/dbstart,将ORACLE_HOME_LISTNER的值改为oralce的安装路径,在这里是/opt/oracle。
同时还需要修改/etc/oratab文件,将最后的那个N改成Y。
手动启动:
启动和停止监听:
lsnctl start
lsnctl stop
启动和停止数据库:
dbstart
dbshut
或者
sqlplus "sys/你的密码 as sysdba"
startup
shutdown
启动和关闭企业管理器:
emctl start dbconsole
emctl stop dbconsole
如果需要启动的时候就运行,那么先
chmod 755 oracle.sh
同时需要将oracle.sh复制到/etc/init.d下面:
sudo cp oracle.sh /etc/init.d/
然后运行:
sudo update-rc.d oracle.sh defaults 97
经过总结,企业管理器启动和关闭都比较慢,所以可以在oracle.sh文件中注释掉相应的条目,然后启动之后手动打开即可。
OK,应该没有什么问题了。
曾经有一个关于字典排序的题目,它是这样的:
现在有1,2,3,4,5,6,7,8,9,九个数,任意组合排成一个数,这个数中任两位都不一样,这些组合按照字典顺序排列,举例来说,123456789是第一个,123456798是第二个,123456879是第三个,依次下去,现在要求编写一个程序,问第i个数字是什么?
这个问题用到组合数学中的基本知识,无奈这个是计算机系的课程,电子系完全就不沾边,所以我当时也不可能答出来,现在,通过向Tarazed咨询,我写出下面的解法,这个解法是Tarazed告诉我的,非常感谢他,他的Blog在这里。
9个数的排列太多,我们先考虑123三个数字的排列,它们可能的排列如下:
123
132
213
231
312
321
那么,我们考虑其中每个数的百位和十位它右边的数比它小的个数,对于123来说,百位1右边是2和3,比它小的数的个数是0个,十位2右边是3,比它小的数的个数是0个,我们把这两个0几下来,作为一个中介数:00,和123结合起来写作:
123 00
那么相应,我们把剩下的数的中间数也写出来:
132 01
213 10
231 11
312 20
321 21
好,中间数都写完了,那么我们观察这些中介数的排列,我们可以看出,这些数是2进制的(逢二进一)0,1,2,3,……但是又和二进制不同,最高位可以是2,那么这种数在组合数学中是有专门名称的,叫做递增进位制数,它的特点是,从右向左,编号依次为第一位,第二位,第三位,……,第n位,而第n位的进位制是n+1进制,对应上面两位的数,第一位进位制是2进制,所以它最大是1,第二位进位制是3,所以它最大是2,那么我们根据这样的进位制,可以看出,这种中介数转化成十进制之后,就是0,1,2,3,4,5,那么这不就是我们要的第几个数吗?在组合数学中,证明了每一个中介数和相应的排列是一一对应的,那么,我们就可以根据要求的第几位数,得到它的中介数,然后进一步找到其对应的组合,而从第几位数推到中介数是很简单的,现在的问题,就是如何能通过中介数,得到相应得排列。
我们这次用1,2,3,4,四个数字的排列作为例子,我们先按照上面的方法,写出其排列对应的中介数,如下:
1234 000
1243 001
1324 010
1342 011
1423 020
1432 021
……
我们就写这么多,然后我们将4,3,2,1写下来:
4 3 2 1
————————
比如说我们得到的是021这个中介数,那么我们从左往右数021这个数,第一个得到的是0,那么我们从右往左数4321,数0个数,得到的是1,将其标记为1,因为它是我们第一个得到的,如下:
4 3 2 1
————————
1
继续从左往右数021,第二个得到的是2,那么我们继续数还剩下的432,从右往左数2位,得到的是4,那么我们把第二个得到的4标记为2,如下:
4 3 2 1
————————
2 1
再从左往右数021,第三个得到的是1,那么我们还剩下32,从右往左数1位,得到3,标记为3,如下:
4 3 2 1
————————
2 3 1
最后剩下的2我们标记为4,如下:
4 3 2 1
————————
2 3 4 1
下面的一行,是得到的排列序号,我们按照下面一行的序号1234,写出对应的上面一行的数,1对应的1,2对应的4,3对应的3,4对应的2,写出来如下:
1432
这个数正好是中介数021对应的排列。
通过中介数的生成和反推排列,我们解决了这个问题,我用C写了个小程序,输入要求的第i个数,得到相应的排列。
程序在这里。
编译:
gcc -c da.c -o da.o && gcc da.o -o da
运行:
da [你要的第i个排列数]
这个程序并没有要求输入的时候指定位数,但是限制了最大的输入数字为3999999999,得到的结果如果不满足位数,那么将你所要的排列的最高n位(这里n是程序结果的位数),按照程序结果排列,把其他的位数按照从小到大排列在这n位前面即可,例如,如果你要求12345,五位数字的第5个排列,那么你运行程序 da 5,得到的结果是:
You want to find the 5th arrange type.
The intermediary number is: 0 0 0 0 0 0 0 0 0 0 2 1
Length is: 3.
Result is: c b a
然后你选出12345的高3位是345,a对应3,b对应4,c对应5,按照c b a的顺序排列,是5 4 3,那么最后你要的12345的第五个排列是12543。
四月 30th, 2008 in
C,
Program | tags:
字典排序,组合数学 |
No Comments