2008-12-31

桑梓贴吧的临时急救方案

  桑梓贴吧在教育网内还是比较不错的在线视频站点,但是由于完成的比较早,并且采用的核心程序Mplayer和FFMpeg都是一个还没有很完美的release版本的项目,因此在服务器环境不断升级的过程中,后台程序上自然遇到一些问题。

  最近修复的桑梓贴吧由于之前系统严重错误,重新部署了新版的操作系统,在重新构架编码环境的时候就遇到了问题:

my string: MPlayer 1.0rc2-4.2.3 (C) 2000-2007 MPlayer Team
my string: CPU: Intel(R) Xeon(TM) CPU 2.40GHz (Family: 15, Model: 2, Stepping: 5)
my string: CPUflags: MMX: 1 MMX2: 1 3DNow: 0 3DNow2: 0 SSE: 1 SSE2: 1
my string: Compiled with runtime CPU detection.
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote control.
my string:
my string: Playing /813f77fa8bbd055850d1b84d0e95f7e2.wmv.
my string: ASF file format detected.
my string: ID_AUDIO_ID=1
my string: [asfheader] Audio stream found, -aid 1
my string: ID_VIDEO_ID=2
my string: [asfheader] Video stream found, -vid 2
my string: VIDEO: [WMV3] 640x480 24bpp 1000.000 fps 736.0 kbps (89.8 kbyte/s)
my string: Clip info:
my string: name: 鎴戞剾鍙板
my string: ID_CLIP_INFO_NAME0=name
my string: ID_CLIP_INFO_VALUE0=鎴戞剾鍙板
my string: author: MC Hot Dog
my string: ID_CLIP_INFO_NAME1=author
my string: ID_CLIP_INFO_VALUE1=MC Hot Dog
my string: copyright: 婊剧煶鍞辩墖
my string: ID_CLIP_INFO_NAME2=copyright
my string: ID_CLIP_INFO_VALUE2=婊剧煶鍞辩墖
my string: ID_CLIP_INFO_N=3
my string: ID_FILENAME=/813f77fa8bbd055850d1b84d0e95f7e2.wmv
my string: ID_DEMUXER=asf
my string: ID_VIDEO_FORMAT=WMV3
my string: ID_VIDEO_BITRATE=736000
my string: ID_VIDEO_WIDTH=640
my string: ID_VIDEO_HEIGHT=480

Is OK:480


0:-of,1:lavf,2:-lavfopts,3:i_certify_that_my_video_stream_does_not_use_b_frames,4:-oac,5:mp3lame,6:-lameopts,7:abr:br=56,8:-ovc,9:lavc,10:-lavcopts,11:vcodec=flv:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1:vbitrate=500,12:-vf,13:scale=320:240,14:-ofps,15:12,16:-srate,17:22050,18:/8bbd055850d1b84d0e95f7e2.wmv,19:-o,20:/813f77fa8bbd055850d1b84d0e95f7e2.flv
MEncoder 2:1.0~rc2-0ubuntu13 (C) 2000-2007 MPlayer Team
CPU: Intel(R) Xeon(TM) CPU 2.40GHz (Family: 15, Model: 2, Stepping: 5)
CPUflags: Type: 15 MMX: 1 MMX2: 1 3DNow: 0 3DNow2: 0 SSE: 1 SSE2: 1
Compiled with runtime CPU detection.
Option lavfopts: Unknown suboption i_certify_that_my_video_stream_does_not_use_b_frames
Error parsing option on the command line: -lavfopts

Exiting... (error parsing command line)
  其实这里的问题在于 i_certify_that_my_video_stream_does_not_use_b_frames这个参数错误,mencoder在新版中不支持此参数,因为mencoder已经能够正确的生成带有b帧的视频文件了。这一点可以在我前几天发的文章中看到解释。
  其实只需要去除掉这个参数即可,不过严重的问题在于,后台程序转换的参数是写在代码里面的,而后台程序的源代码又遗失了,所以现在只能从外部着手了,于是,我写了个fakemencoder程序,接受原来程序传递个mencoder的参数,之后替换其中无效的参数,再将替换后的参数传递个mencoder进行编码。这样就解决了这个问题。

  事情本来到这里就结束了,不过没想到桑梓贴吧的后台程序出现异常,所有视频都无法成功转换,经过严密的排查,最后定位到,程序在移动视频文件的时候出现异常,导致视频文件无法正常被移动,或者是后台程序在删除临时文件的时候把目标文件也删除了。这样一来,结果变成了转换完成的视频丢失了。
  至此,问题大发了。。。
  由于联系不到其他前辈,现在的问题无法查出原因,所以目前,只能采取临时的应急方案了。
  还是从fakemencoder程序入手,我们在之前的测试中发现,其实桑梓贴吧判断视频转换成功与否的原理只是扫描目标文件夹下是否存在对应的flv文件而已,因此,我的做法是,在fakemencoder文件里面执行完成mencoder的命令后立刻把目标文件从临时文件夹拷贝的目标文件夹,这样以来居然真的成功使得贴吧出现了转换完成的状态。
  但是,果然还是没有这么容易就搞定,结果又发现问题:转换成功的视频无法播放,再次经过测试,发现,贴吧后台程序转换视频用的mencoder的命令是:
mencoder. -vf scale=320:192 -ffourcc FLV1 -lavdopts er=1 -pp 6 -sws 2 -of lavf -ovc lavc -lavcopts vcodec=flv:vhq:turbo:vbitrate=500 -srate 22050 -oac mp3lame -lavcopts acodec=mp3:abitrate=128 /tempvideo/2801493.avi -o /flvtemp/edab85785c473ad7e0d6a6a46b000673.flv
  经过测试发现把--ffourcc FLV1去除我的mplayer即可正常播放,但是google出来的结果称,flash播放器可能不能正常播放,于是,搜索了youtube的原理之后发现只要增加一个metadata即可。
  要增加metadata只需要用一个开源工具flvtool就能轻松搞定:
flvtool2 -U targetfile.flv

这样解决方案就出炉了,看我源代码:



#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
/*
*
*/
int main(int argc , char** argv) {

char *s=" ";
char *st="mencoder.bak ";
char *oadd = new char[100];
char *mencoder;
mencoder = new char[1000];
*mencoder = '\0';
mencoder = strcat(mencoder, st);
for (int i = 1 ; i < argc ; i++) {
if(strcmp(argv[i],"-lavfopts")==0 || strcmp(argv[i], "i_certify_that_my_video_stream_does_not_use_b_frames")==0 || strcmp(argv[i],"-ffourcc")==0 || strcmp(argv[i],"FLV1")==0 ) {
continue;
}
mencoder = strcat(mencoder, argv[i]);
mencoder = strcat(mencoder , s);
}

system(mencoder);
//copy to flv/ directory
char *sss=new char[50];
*sss='\0';
sss = strcat(sss,"cp ");
sss = strcat(sss,argv[argc-1]);
sss = strcat(sss," ");
oadd = strcpy(oadd,argv[argc-1]);
*strrchr(oadd,'/')='\0';
*strrchr(oadd,'/')='\0';
sss= strcat(sss,oadd);
sss= strcat(sss,"/flv");
sss= strcat(sss, strrchr(argv[argc-1],'/'));
cout <<"\nEncoder command Line:"<<"\n"<<mencoder <<"\n: copy file : "<<sss << endl;

system(sss);

//write mate-data into flv file
oadd = strcpy(oadd,"flvtool2 -U ");
oadd = strcat(oadd,argv[argc-1]);
*strrchr(oadd,'/')='\0';
*strrchr(oadd,'/')='\0';
oadd = strcat(oadd,"flv");
oadd = strcat(oadd, strrchr(argv[argc-1],'/'));
cout<< "\nWriting META_DATA by:" << oadd << endl;
system(oadd);
return (EXIT_SUCCESS);
}


  如果需要了解这种在线视频网站的原理,可以看这篇文章,我把主要部分引用过来:

Converting the video for use in the site

The users are asked to upload AVI videos to the site, but we cannot play AVI videos directly in the browser (at least not in a browser-independent manner). A good way to publish videos for viewing in a browser is using FLV (Flash(tm) Video) format. This is what YouTube, Google Video and a whole host of other sites use. If it’s good enough for them, it’s good enough for me!

Converting to FLV

So, how to convert the AVI video the user uploaded to a usable FLV format? Luckily, the OSS package ffmpeg [2] provides this conversion functionality (plus a wide range of other video conversion features one of which we’ll come to later on). The good thing about ffmpeg is that it is controlled entirely from the command-line and can be run in headless environments — this is vital for using it on an application server. Most other FLV conversion tools were either for Windows or came with some form of Gnome or KDE gui which wouldn’t have worked on my hosted Linux box.

The basic command for converting a video into FLV format is (see [1] in resources):

ffmpeg -i [sourcefile.avi] -acodec mp3 -ar 22050 -ab 32 -f flv -s 320×240 [destfile.flv]

Adding FLV metadata

This command creates a simple FLV format file, containing the video and audio streams. In addition, FLV files need meta-information such as duration, frames, etc. FLV movie players use this information to calculate progress bar sliders and allow the user to fast-forward or reverse through the video. For reasons which I didn’t bother to research, ffmpeg does not add this information. But there is a package which can: flvtool2 (see [3]). Using this tool, we can add FLV meta-information with the following command:

flvtool2 -U [flvfile]

(Warning, Djangoists — flvtool2 is written in Ruby. Please check your religious language preferences at the door and pick them up as you leave. Thank you).

Adding a video thumbnail

Blog entries in trogger.de can include pictures uploaded by the users. One of these pictures is displayed as a small preview when showing the blog posting (e.g. in the blog overview, or in the list of the latest blog submissions). Wouldn’t it be nice if we could also add a thumbnail for a video submission, so that the blog’s reader can get a first idea of what to expect? I think it would. And, again, ffmpeg comes to the rescue.

ffmpeg can extract single frames from a video stream, storing them in still image format. The command for doing this is:


ffmpeg -y -i [videofile] -vframes 1 -ss 00:00:02 -an -vcodec png -f rawvideo -s 320×240 [thumbnailimage.png]

Putting it all together

With these individual steps, it’s EASY to put together a video conversion function which kicks in once a user has uploaded a video file. Since we have the information which video the user uploaded with the form, we convert this video into FLV format, add metadata and create a thumbnail image:

def convertvideo (video):
if video is None:
return "Kein Video im Upload gefunden"
filename = video.videoupload
print "Konvertiere Quelldatei: %s" + filename
if filename is None:
return "Video mit unbekanntem Dateinamen"
sourcefile = "%s%s" % (settings.MEDIA_ROOT,filename)
flvfilename = "%s.flv" % video.id
thumbnailfilename = "%svideos/flv/%s.png" % (settings.MEDIA_ROOT, video.id)
targetfile = "%svideos/flv/%s" % (settings.MEDIA_ROOT, flvfilename)
ffmpeg = "ffmpeg -i %s -acodec mp3 -ar 22050 -ab 32 -f flv -s 320x240 %s" % (sourcefile, targetfile)
grabimage = "ffmpeg -y -i %s -vframes 1 -ss 00:00:02 -an -vcodec png -f rawvideo -s 320x240 %s " % (sourcefile, thumbnailfilename)
flvtool = "flvtool2 -U %s" % targetfile
print ("Source : %s" % sourcefile)
print ("Target : %s" % targetfile)
print ("FFMPEG: %s" % ffmpeg)
print ("FLVTOOL: %s" % flvtool)
try:
ffmpegresult = commands.getoutput(ffmpeg)
print "-------------------- FFMPEG ------------------"
print ffmpegresult
# Check if file exists and is > 0 Bytes
try:
s = os.stat(targetfile)
print s
fsize = s.st_size
if (fsize == 0):
print "File is 0 Bytes gross"
os.remove(targetfile)
return ffmpegresult
print "Dateigroesse ist %i" % fsize
except:
print sys.exc_info()
print "File %s scheint nicht zu existieren" % targetfile
return ffmpegresult
flvresult = commands.getoutput(flvtool)
print "-------------------- FLVTOOL ------------------"
print flvresult
grab = commands.getoutput(grabimage)
print "-------------------- GRAB IMAGE ------------------"
print grab
except:
print sys.exc_info()
return sys.exc_info[1]
video.flvfilename = flvfilename
video.save()
return None

2008-12-29

Mencoder转换视频为flv格式命令参数的变化。

早期版本的Mencoder要转换视频到flv必须要加上。-lavfopts i_certify_that_my_video_stream_does_not_use_b_frames这个参数。但是在2007年后期,新发布的mencoder就已经取消了这个参数了。
按照这个邮件里回答者的说法:
get rid of this idiotic i_certify_that_my_video_stream_does_not_use_b_frames muxer_lavf can now generate correct files with b frames as long as the timestamps provided to it are correct (they should be with lavc encoding, expect trouble withstream copy though)
结果就是如此,在那个版本之后的mencoder已经能够使用lavf产生正确的带有b文件和时间轴的视频了。。请各位使用早期类似:
mencoder -of lavf -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -oac
mp3lame -lameopts abr:br=56 -ovc lavc -lavcopts
vcodec=flv:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1:vbitrate=500 -vf
scale=320:240 -ofps 12 -srate 22050 test.wmv -o ttt.flv
这样编码参数的朋友去除这个参数吧。
此外还有兴趣的朋友可以参看这个网页上的讨论:
http://forums.buyscripts.in/viewtopic.php?f=4&t=1173

2008-12-24

Windows 的三大在线同步与存储服务

  如果开始深度接触微软的话你会发现这家公司其实很强悍,该有的他都有,你想不到的他也有,与一个30这样的老迈岁数对比,微软还不失其年轻的创造力。但是为什么微软在互联网业务方面一直被压制,一直都是万年老二的位置呢?个中原因值得仔细研究,待之后对其有更深的了解后再写篇文章吧。不过就这篇post介绍的东西来说,我们可以看到它复杂的产品线究竟对它的推广有多么严重的阻碍了。

  最新的Windows Live提供了三种在线同步与存储的服务,Live Mesh, Windows Live Sync 和Windows Live SkyDrive ,不过三者类似的程度让初心者很难正确判断和使用,现在live mesh还在beta阶段,另外两个都已经是正式服务了。希望将来MS吧这些东西整合了吧。。。毕竟大众用户可未必会去记这么多名字啊。。。

首先是大名鼎鼎的Windows Live SkyDrive
  Windows Live SkyDrive是一个在线文件存储的服务,它现在提供了25G的空间。它具有基本的分享功能,你可以和你设定的特定朋友分享某些文件,也可以把某些文件公开下载,每个文件都有它的url,总之,它的特色就是相当大。但是单个文件不允许超过50M。
  Windows Live SkyDrive甚至允许在手机上下载文件。

Windows Live Sync:
  Windows Live Sync是一个在线文件同步服务,被同步的机器必须要安装客户端程序,这项服务是纯粹的文件同步服务,它允许用户在其他计算机上获得被同步的机器上的文件。

Windows Live Mesh:
  Windows Live Mesh是一个还在beta的服务,它包括了Windows Live Sync的所有功能,此外还提供了额外的5G的存储空间和支持直接拷贝粘贴的远程桌面服务。
  要使用Windows Live Mesh ,需要安装一个客户端。装好后你在我的电脑里面右键点击文件夹会出现一个Add Folder to Live Mesh,点下去之后就能够把这个文件夹像Live Sync一样同步了。如果你被同步的机器可能下线,而又需要在别的机器上使用,你就可以利用Live Mesh提供的5G空间把它存在Live Mesh 提供的存储空间上。在安装了一个插件之后甚至支持直接拖拽文件到Live Mesh的LiveDesktop里面。



不过目前windows Live Mesh 在我的windows server 2008上市不能正常运行的。

2008-12-19

Ubuntu KVM 安装windows xp && 解决上网问题

  KVM是一款很不错的虚拟机,它能够很好的利用虚拟化技术。不管怎么样,常用linux的人也是有必要弄一个windows虚拟机用着的。。。


安装KVM:


首先确定你的cpu是否支持虚拟化技术:
egrep '(vmx|svm)' /proc/cpuinfo
如果出现svm 或者 vmx 代表支持虚拟化技术,前者是amd-v 后者是intel的vt,另外注意主板是否开启这些选项。

在确认cpu支持之后就可以开始编译安装了:
http://kvm.qumranet.com/kvmwiki 此处下载最新源代码。
回来之后只需要用普通的./configure --prefix=/usr/local/kvm即可。这个kvm会自己编译安装qemu的。所以不劳我们费心。

kvm主要有2个ko,kvm-intel.ko,kvm-amd.make install 会自动加载,如果用apt装可能需要手动加载一下:
sudo modprobe kvm
sudo modprobe kvm-amd

记得每次重启你真的主机之后都需要重复一遍这两个命令,否则会提示找不到/dev/kvm
这样基本就算装完了。接下来只需要装系统配置windows了。

配置客户机
首先是装系统:
先配置一个虚拟磁盘:
sudo qemu-img create -f qcow2 winxp.img 10G
-f 就是创建的虚拟磁盘的格式,最后那个指定的是初始大小,貌似虚拟磁盘的大小能够自动增加。
之后启动系统开始安装:
sudo qemu-system-x86_64 -hda winxp.img -cdrom /home/test/xpimg.iso -boot d -m 384 -no-acpi -smp 2 -clock rtc -localtime
启动虚拟机用的就是这个qemu-system-x86_64,不管你是32位还是64位
-hda指定虚拟磁盘文件,当然也可以有-hdb -hdc了。
-cdrom 指定的是作为光盘加载的文件,也可以直接加载光盘.
-boot指定启动类型,d是从光盘启动,c是从硬盘启动
-m指定内存多少以m为单位
-no-acpi之所以要用是因为kvm对它支持不好,似乎会使系统很慢。
-smp指定cpu个数,有几核用几个才是上上之选。
-clock rtc指定使用rtc时钟。据说不开这个xp很慢。。。
-localtime使用本地时间,不开的话可能虚拟机时间有问题。



接下来配置网络,我用桥接模式,这样设置:
sudo apt-get install bridge-utils
sudo apt-get install uml-utilities

sudo vi /etc/network/interfaces

按照下面的范例自己配置:
auto eth0
iface eth0 inet static \
address 192.168.1.2
netmask 255.255.255.0
gateway 192.168.1.1

#######################Setting for KVM Guest Machine#######################
auto tap0
iface tap0 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down
tunctl_user kyo

auto br0
iface br0 inet static
bridge_ports eth0 tap0
address 192.168.1.134
netmask 255.255.255.0
network 192.168.1.1
broadcast 192.168.1.255
gateway 192.168.1.1
###########################################################################


之后
sudo ifup tap0
sudo ifup br0

如果出错重启试试

都起来之后
sudo brctl addif br0 tap0
sudo brctl addif br0 eth0

这个命令就是把这两块网卡桥接起来。。。

配置好之后以后启动虚拟机用这个命令:
sudo qemu-system-x86_64 -hda winxp.img -boot c -m 512 -no-acpi -smp 2 -clock rtc -localtime -net nic,vlan=0,macaddr=01-02-03-04-05-06 -net tap,vlan=0,df=h,ifname=tap0,script=no -no-acpi
-net后面的参数表示:使用网络,并连接到一个存在的网络设备tap0,这里的mac地址是自己乱编的。

如果你按照我上面的设置设置好了的话,启动到虚拟机有八成你还是联不上网。为什么?因为一个常识问题:
Note: Make sure the first octet in your MAC address is EVEN (eg. 00:) as MAC addresses with ODD first-bytes (eg. 01:) are reserved for multicast communication and can cause confusing problems for you. For instance, the guest will be able to receive ARP packets and reply to them, but the reply will confuse other machines.
所以,最后,我可以上网的启动命令是这样的:

sudo qemu-system-x86_64 -m 512 -hda /home/kyo/KVM/winxp.img -localtime -net nic,macaddr=00-02-03-04-04-05 -net tap,ifname=tap0,script=no -boot c -smp 2 -clock rtc -no-acpi

自己研究了几个小时才真正成功,也就是一个常识匮乏导致的问题。最后总结下这种虚拟机桥接上网的模型:
简单来说:就是我们的虚拟机通过使用tap0这个虚拟网卡桥接到了主机上。对外主机的ip就是192.168.1.10而那个eth0就不再有ip地址了。逻辑上的连接就是图中蓝色的线。虚拟机也可以通过向网络发起起请求连接到主机。

最后记得一点就是:
官方提供了一个叫kvm-guest-drivers-windows的网卡驱动,据说相当不错,如果用bridge模式的话一定要装。不过注意的是,用了这个以后你需要在虚拟机的启动参数那里加上model=virtio这个参数 。
此外,如果有鼠标移动问题或者vnc下鼠标异常奇怪的问题就加上一个参数:-usbdevice tablet,要使用超过1024分辨率需要增加-vga std


参考资料:
https://help.ubuntu.com/community/KVM
http://forum.ubuntu.org.cn/viewtopic.php?f=65&t=154792&start=0
http://forum.ubuntu.org.cn/viewtopic.php?f=65&t=130210
http://linux.chinaunix.net/bbs/viewthread.php?tid=788174
http://kvm.qumranet.com/kvmwiki/Networking

Ubuntu Server console 编译安装apache2 + php5 + mysql5

  Ubuntu的apt安装apache+php+mysql是很轻松,不过ubuntu的apt安装方式弄出来的东西装的目录很分散,此外apache 配置文件也与其他系统上的不一样。所以为了统一管理,还是必须要全部编译安装过。
  首先还是老规矩,先建立mysql用户和网站用户。。。。
--------------------------------------------------------------------------------------------------------------------------------------------
编译和安装mysql:
一般基本编译环境应该都弄好了,但是必须要检查一下有没有装libncurses5-dev。
弄好之后解压开文件就可以开始编译了。
./configure --prefix=/usr/local/mysql --with-charset=utf8 --with-extra-charsets=all --with-mysqld-user=mysql
make
make install
之后拷贝配置文件,按照自己的需求从源码包里拷吧。我这样的:
cp support-files/my-large.cnf /etc/my.cnf
之后把可执行文件做一下软连接:
ln -s ./bin/mysql /usr/bin/
ln -s ./bin/mysqladmin /usr/bin/
ln -s ./bin/mysqld_safe /usr/bin/
ln -s ./bin/mysql_con /usr/bin/
ln -s ./share/mysql/mysql.server /usr/bin
ln -s ./share/mysql/mysql.server /etc/init.d/

编译安装的同学应该先cd到mysql的安装路径,否则接下来可能会出现Could not find /fill_help_tables.sql的错误,一般这种情况你可以删除/etc/mysql/my.cnf解决。我这里是因为之前有人装了mysql导致的配置不同才报的错。
接下来创建MySQL授权表
./bin/mysql_install_db --user=mysql
记得把mysql目录下的var调成mysql所有,其他所有文件调成mysql组所有。
之后sudo bin/mysql_safe --use=mysql &
重启系统之后记得把root的密码设置好。

2008-12-17

微软Imagine Cup 2009集训营一个下午的收获

  上周末微软中国开了个“Imagine Cup 2009“集训营,我们这些没在北京某处的同学们只有用live meeting 参加在线视频会议了。别说,上次参加全球的那个什么总结的视频会议都有很完美的效果,一时间让我认为现在视频会议的技术如此完美。没想到这次简直堪称垃圾。噪声不断也没有一个有效的控制手段,其中一位演讲的微软员工太欠缺这种视频演说的经验了可能,声音小的不到噪音的一半。我们再三提醒,再三由大变小。。。

  不过这次还是收获了不少东西。今天有空整理一下分享出来:
 
-----------------------------------------
IT解决方案:
  为我们介绍这个的是微软的一明叫做沈旭的技术经理,
  按照那位沈旭的说法,IT解决方案就是用多个产品构建一个架构性的解决问题的项目。

  做一个IT解决方案的需要:

  1. 相关的技术知识,MSF,MOF.(这两个是微软针对IT项目提供的参考方案)
  2. 前期:
    • 了解客户背景、客户需求
    • 解决方案设计,制定验收标准
    • 项目管理相关
      时间表、人员分工、风险评估、备选方案
  3. 中期
    • 部署、实施
    • 调优

  4. 后期
    • 测试:
      构建测试环境
      设计测试用例
    • 撰写项目报告

-----------------------------------------
  以上就是一个IT解决方案的整个业务流程。

接下来是软件开发项目的团队管理:
-----------------------------------------

  其实写软件容易管理一个软件团队高效的做好项目不是一件容易的事情。上图是那天ppt的截图。一个软件项目被分成上面这些成员来完成。之前我一直认为项目经理就是负责统筹整个项目并且参与项目构架的,结果这次才发现不是。
  架构师是从高层全局控制整个项目的,
  项目经理是负责平衡整个项目资源的分配,时间、成本控制,他应该独立于技术之外的。即使他是技术出身的。

  一般一个项目的流程是业务人员从客户那里获得客户需求,经过分析获得用例和非功能性需求之后交与项目经理,之后项目经理按照技术的角度来筛选可行性的功能,再交给开发团队。

  一般由于涉及具体的技术细节,开发人员一旦进入开发就很容易迷失到某一功能的完美当中去,这样就失去了对整体的把握。所以一般负责整体统筹的人就不应该作为Chief Developer。

  但是由于工作中,小团队常常不够人,这个时候就必须要有人兼任工作。
以下是演讲者根据他的经验给出的建议:
一般可以兼任的有:
PM + Architect
Architect + Chief Developer
Developer + Designer + Tester
但是:
PM + Chief Developer = FAIL!

------------------------------------------------------------------------------------

在会议的最后曾经参加过Imagine Cup的姚冬靖给我们介绍了一些他的经验,从声音和表现中可以了解到这位同学虽然现在在微软了,不过还是学姐级的人物哈。》》》好好学习中。。。。
------------------------------------------
组队的经验:
  • 统一的愿景
  • 多样化的成员
创新:
  • 头脑风暴->定案
  • 在已有的成果上
实现:
  胜者,先胜而后求其战
  • 先有保底方案,再逐步完善
  • 折衷,妥协
  提交项目时要考虑很多细节的问题,比如其他机器是不是能够很好的运行在你自己机器上编译好的东西啊。遇到这种冲突的时候如果实在无法解决的话就要想办法变通。。

最后,English Speech Capability
  • How to sell your solution
  • Business Sense
  • English

------------------------------------------


以上是那天记录的笔记,稍微重新整理了一下,但是未必能很完美的体现出那天的精髓所在,那天我们这些远程参加者在极度痛苦的噪音之下从2点拼到了快6点,总的来说这次微软中国的活动做的和微软美国直接开的视频会议还有一定差距。希望他们多多加油吧。。。
2009年5月还有第二轮集训,到时候有兴趣的读者可以参加。。。

如果希望观看录像的话可以到微软学生中心去下载。。。。

2008-12-13

转:篳路藍縷的中文化歷程 專訪台灣微軟 Xbox 360 中文化研發團隊

  这篇报道对不仅对玩家来说有一定的吸引力,而且也可以从中了解到专业的项目管理是如何展开的、如何进行的。所以很值得一读。另外,如果你是对游戏后期/二次开发有爱的同学,那更要一读了。
  本文转自巴哈姆特:http://gnn.gamer.com.tw/5/33895.html
-------------------------------------------
  自 2002 年 Xbox 正式在台推出至今已歷 6 個年頭,期間台灣微軟代理引進了許許多多 Xbox 與 Xbox 360 的遊戲,並積極的進行遊戲的中文化。本次巴哈姆特 GNN 特別訪問到負責一系列遊戲中文化的台灣微軟娛樂暨裝置事業處產品研發部負責人張書良,一探遊戲中文化製作上的秘辛。

註:張書良以下簡稱「

◆ 個人與團隊基本介紹部分

G:可否請您簡單自我介紹?

張:我是台灣微軟娛樂暨裝置事業處產品研發部負責人張書良,進入台灣微軟工作已經 11 年,最初任職於行銷部門,後轉任產品研發部門。目前領導產品研發部,負責台灣微軟娛樂暨裝置事業處各種軟硬體產品的研發與測試工作中包括遊戲中文化的製作。

台灣微軟娛樂暨裝置事業處產品研發部成員合影

G:產品研發部是何時成立的?

張:這個部門前身主要是負責微軟消費型態的軟體開發,像是《Microsoft Works》和《Picture It!》等。大約是從 11 年前開始負責遊戲的專案,首款中文化的遊戲是很多老玩家所熟悉的 PC 即時戰略遊戲《世紀帝國(Age of Empires)》。

G:目前部門總共有多少成員?分為幾個小組?

張:一般編制大約是 15 人,案子多的時候會增加到 19~20 人。

  編組基本上是動態的,會視當時的工作而定。




G:主要負責的業務包括哪些?

張:產品研發部負責的業務很多,除了包括玩家比較熟悉的遊戲之外,還包括 Xbox 360 系統軟體的研發測試,例如前不久才剛釋出的《Xbox 新體驗(New Xbox Experience)》。而且不只是單純的中文化與測試,我們也會針對繁體中文地區的需求進行新功能的開發。

  舉例來說,目前 Xbox 360 系統軟體只提供注音輸入法,對於不使用注音符號的香港玩家來說,等於是沒有辦法在 Xbox 360 上使用中文輸入功能。因此我們目前正在著手進行 Xbox 360 其他中文輸入法的研發,希望能盡快讓香港玩家也能使用中文輸入功能。

  除了軟體產品之外,產品研發部也負責鍵盤與滑鼠等硬體產品的測試工作。

G:曾經手完成的中文化遊戲有哪些?

張:我個人第 1 款經手的中文化遊戲是 PC《神話世紀(Age of Mythology)》,當時整個部門只有 3 個人,因此工作相當忙碌。之後陸續經手很多遊戲,最近一款中文化遊戲是 Xbox 360《熾焰帝國:毀滅之環(Kingdom Under Fire: Circle of Doom)》。

◆ 遊戲中文化作業部分

G:如何挑選中文化遊戲?

張:原則上我們是希望微軟自家研發與發行的 Xbox 360 遊戲能全面中文化,目前為止大致上都能達成這個目標,只有少部分遊戲例外,像是《質量效應(Mass Effect)》因為原廠的堅持所以最後並沒有中文化。至於協力廠商開發再交由微軟發行的遊戲,我們的專責部門會與協力廠商接洽,根據廠商的意願、銷售量和本部門的人力狀況來決定,也是盡量朝中文化的目標努力,不過這部分的變數較多,較難掌握。

G:每款遊戲的中文化約需花費多少人力、時間與成本?

張:每款遊戲的中文化製作大概需要動用產品研發部 3~5 名工作人員,大致上分為製作人 1 名,中文化經理 1 名,程式與測試人員 2~3 名。人員編制會因為製作規模的不同而增減,像是《失落的奧德賽 (Lost Odyssey)》因為製作規模龐大而且內容包含細膩的文學創作,因此動用了 6~7 人,還請了專門翻譯日本文學作品的專家來幫忙審視內容。
  每款遊戲中文化大約需要 3 個月到半年的時間,原則上是以同步推出為目標,因此在開發階段就必須展開中文化的製作。所需時間除了受製作規模左右外,還要看原廠的規劃而定。像《藍龍 (Blue Dragon)》當初之所以會晚日文版近半年推出,主要是因為原廠決定專注於日文版的上市,因此延後了中文版的製作。後來《失落的奧德賽》雖然還是因為原廠規劃所以無法與日文版同步,不過已經能與英文版同步,比《藍龍》快很多。

  遊戲中文版的成本單從產品研發部這邊很難確切估算。在 Xbox 360 微軟自家遊戲部分,原則上是盡可能達成全面中文化的目標,銷售量高低不是絕對的考量因素。

  以往的遊樂器遊戲,因為玩家大都已經習慣日文或英文,所以中文化這塊並不受重視。不過自從 Xbox 360 推出後這兩年中文化遊戲逐漸普及,玩家的習慣經逐漸改變,中文化也越來越受到大家的重視,我們希望可以繼續提供玩家更多的中文化遊戲的選擇。

G:中文化的作業流程分為哪幾個部分?各需要多少人力與時間?

張:基本上可以分為 3 項主要工作,包括程式規劃、中文化翻譯與測試。

  在人力安排上,除了程式規劃(製作人)是從專案一開始就會投入外,其餘人員會隨著進度而動態調配。每個人手頭上通常不止一個專案在進行,只不過階段不同,


G:哪些部分是由團隊自行完成?哪些部分會外包?

張:會外包的工作主要是翻譯,之外還有遊戲包裝封面的設計。

◆ 文字語言翻譯部分

G:每款中文化遊戲所收錄的字數大概有多少?

張:因為中文化的遊戲也包括 Xbox LIVE Arcade 遊戲,因此範圍很廣。Xbox LIVE Arcade 遊戲大概只有幾千字,一般套裝遊戲通常是 3 萬字起跳,最多可達 50~60 萬字。

G:翻譯過程中會遭遇到哪些問題?

張:最常見的問題是翻譯出來的內容風格不一致。因為遊戲中文化的製作規模龐大且時間緊湊,因此在翻譯外包時通常是多人分工,但是每個人的翻譯風格不同,有時候差異大到一眼就能看得出來。當發生這種狀況時,中文化專案經理就必須去協調外包公司解決。

  另外,因為在翻譯階段並沒有辦法直接對照遊戲內容,因此常常會無法確定像是人物的性別或是武器道具的效用等細節,導致翻譯的結果與實際內容有所偏差, 這部分都需要測試人員在測試階段逐一確認並更正。除此之外,在翻譯日文時還有許多直接翻譯所無法忠實呈現的狀況,這部分在先前的研討會中有舉出很多例子可以參考,這邊就不贅述。

G:在英文與日文的翻譯上會有哪些不同之處?

張:在英日文的差異部分,比較常碰到的問題是究竟要選擇以哪一種語言來中文化。

  當初在進行《生死格鬥 4 (Dead or Alive 4)》的中文化時,因為遊戲是採用英日多國語言同步推出的方式製作,而且部門中大家都熟悉英文,因此是拿英文來翻譯成中文,不過後來玩家有些不好的評價。 後來我們自己也有檢討原因,主要是因為遊戲是日本廠商製作的,從日文翻譯成英文時,例如說原來日文稱謂的尊屬關係就不見了,再從英文翻譯成中文時又再失真 一次,所以效果並不好。

  所以後來我們在遇到類似狀況時,都希望以日文來翻譯成中文,尤其是像《藍龍》、《失落的奧德賽》等文字佔遊戲比重很大的遊戲更是不能輕忽。不過改用日文來翻譯又會產生新的挑戰,包括如何確保日文翻譯人員的質與量,以及遊戲測試時的語言障礙。

  雖然台灣懂日文的人很多,不過遊戲中文化的作業規模龐大,而且不像一般小說是連貫性的,因此翻譯的難度相對來說比較高。當遇到《藍龍》、《失落的奧德賽》之類動輒數十萬字的遊戲時,如何確保夠多足以擔當翻譯工作的人才就是很大的挑戰。

  另一方面,由於產品研發部成員大部分都不懂日文,但是在測試時測試人員必須清楚掌握中文譯文是否正確對應到日文原文,因此語言障礙就成了測試階段的一大挑戰。為了確保中文化的品質,因此製作時我們會聘請專門從事日文翻譯的自由作家來幫忙。

  其中最值得一提的就是《失落的奧德賽》短篇小說《千年之夢》的中文化。

  有玩過《失落的奧德賽》的玩家,應該都會對《千年之夢》感人的文字留下深刻的印象。不過其實當初外包出去的翻譯稿送回來時,讀起來並不動人。因為翻譯 內容雖然正確,但是原文那種細膩感性的筆觸在翻譯後都不見了。因此後來我們請了專門從事日本文學作品翻譯的專業作家下了很大的功夫去潤飾,才得到玩家所看 到的最終成果。

















  其實不只是日文翻譯,就算是比較熟悉的英文,在翻譯過程中也會遇到很多挑戰,像最近推出的《阿邦阿卡大冒險:改造大作戰 (Banjo-Kazooie: Nuts & Bolts)》裡頭用了很多典故跟俚語,沒玩透先前系列作和熟悉歐美文化其實很難理解,要怎麼轉換成台灣玩家習慣用語也是一大考驗。因此當初在中文化時,一群人絞盡腦汁思考最合適的翻譯。

G:如何決定是否要將語音中文化?

張:關於語音是否要中文化的問題,在網路上是個爭論已久的議題,對我們來說也是個複 雜的決策過程,究竟是好是壞其實很難有個標準答案。而且真正著手進行時還必須考慮到台灣以外的另一個繁體中文市場,也就是香港和澳門,因為港澳玩家並不習 慣國語配音。所以像《最後一戰 3》最後我們在港澳推出了英文語音搭配中文字幕的版本來解決。

  考慮語音是否要中文化的準則大致上可歸納為兩點,其一是玩家需求,其二是玩家年齡層。前者以《最後一戰 3》為例,因為遊戲中非常依賴語音的指示,而且這些訊息大都不會以字幕呈現,如果語音不中文化的話,玩家可能會漏失很多的訊息和遊戲的樂趣。

  後者以《超級大明星 (You're in the Movies)》為例,因為遊戲的玩法是照著導演指示來演出,如果聽不懂這些誇張逗趣的語音指示,遊玩的氣氛會大打折扣。而且這類闔家歡樂的派對遊戲必須考慮到低年齡層玩家,只把文字訊息中文化很難讓這類玩家接受。

  也正因為語音對《超級大明星》的整體氣氛影響很大,因此這款遊戲我們也特別針對港澳玩家製作了粵語字幕與配音,不但聘請香港配音員來台錄製語音,還找了在台工作的香港人替粵語翻譯品質把關,檢視字幕與配音是否真正符合當地的習慣。

  其實不只是《超級大明星》,有些遊戲的翻譯也會特別針對香港玩家的需求調整,像撲克牌遊戲很多術語在台灣與港澳就不一樣,製作時會個別處理。

  另外,當我們決定只中文化文字訊息而不中文化語音時,都會要求原廠一定要在遊戲中所有語音內容都提供字幕顯示功能,以免玩家聽不懂語音又沒有中文字幕可以參考而一頭霧水。不過我們的合作廠商很多,並不是每一家都能滿足這樣的需求。

G:語音部分的中文化作業會遭遇到哪些問題?

張:電視電影的配音是線性連貫的,配音員拿著翻譯好的台詞搭配演出畫面來配音,因此 可以明確掌握演出情境。但是遊戲的配音卻是離散零碎的,除了小部分劇情演出場面可能是線性的之外,其餘配音很多都是依照遊玩狀況即時組合的,因此配音時都 是零散的東配一句西配一句,很少有明確畫面可以配合,只能參考原音的語氣,比較難以掌握。

  如果時間充裕時,還可以在最後檢視階段找出哪些配音不符需求並加以修正,不過很多遊戲在中文化時進度很趕,而且原廠經常有自己的顧慮而造成一些阻礙。

  例如在製作《最後一戰 2 (Halo 2)》與《最後一戰 3 (Halo 3)》時,原廠 Bungie 是不讓開發中的遊戲離開開發總部的,只允許在最後測試階段派人親自到他們總部確認製作成果。所以在翻譯和配音期間我們並沒有看過遊戲的畫面內容,因此沒辦 法去一一確認配音合不合適,只能瞎子摸象般把數萬句台詞一股腦全部配完送過去。

◆ 測試除錯部分

G:將翻譯完成的文字套用到遊戲上時,會遇到哪些問題?

張:主要會碰到的有排版問題與程式運作問題。

  排版問題在先前的研討會中有舉出很多例子,像是未正確斷行導致訊息超出框線外、斷行位置不正確、翻譯後詞彙位置變動導致字框位置不正確等。之外還有一些因為中英文字型設定不同所導致的問題,像是缺字、字型基準線高低不同導致字體超出框線等。

  在程式運作問題部分,因為遊樂器平台遊戲並不像一般 PC 遊戲那樣,是由作業系統統一提供字型,而是各自將所需要的字型包含在遊戲資料中。每個中文字都必須佔用一定記憶體,而且一般常用的中文字至少要一兩千個字,相較於其他語言來說,中文版本的記憶體需求通常都是最高的。但是有些遊戲在開發階段並沒有考慮到此一額外需求,因此在中文化時就會碰到問題。

  有些遊戲的設計也會導致中文化的困難,像《世界街頭賽車 3 (Project Gotham Racing 3)》,因為原廠只收錄主程式有使用到的字的圖檔,導致後來追加的下載內容都只能用原有的字集,不能追加新字。因此原本有個照原文翻譯應該是「貓捉老鼠」 的下載內容,因為主程式裡沒有「貓」跟「鼠」這兩個字,後來只好改翻成「你跑我追」。

G:測試過程中需要注意哪些部分?

張:測試時除了中文化相關部分之外,還必須同時進行功能測試,以確保遊戲在中文化之後能滿足 Xbox 360 平台的技術認證規範(TCR,Technical Certification Requirements)。

  很多狀況都是在最後進行測試時才會浮現出來,舉例來說,在開發過程中遊戲都是存放在硬碟上進行測試,但是在最後測試階段時會使用 DVD 光碟模擬程式來測試遊戲實際存放到 DVD 光碟上讀取時的運作狀況,此時就可能會爆出一堆先前沒碰過的問題。

  例如先前在進行《極限競速 2 (Forza Motorsport 2)》的中文化時,原本在硬碟上測試時一切正常,但是模擬 DVD 光碟運作時,中文字型的顯示速度會緩慢到像幻燈片換頁般一個一個慢慢浮現,檢查後才發現原來是字型顯示部分的程式運作出問題。
G:如何克服同步上市的進度壓力?

張:答案很簡單,就是「加班」(笑)。

◆ 甘苦談部分

G:在經手過的中文化遊戲中,印象最深刻或是曾經遭遇到的最大挑戰是?

張:印象最深刻的通常都是挑戰最多最難搞的(笑)。

  真要說的話,大概就是《最後一戰 2》吧,因為這款遊戲讓我們留下深刻痛苦的印象(笑),痛苦的原因並不是因為加班,而是因為製作過程中無時不在擔心最後的中文化的品質是否可以符合玩家期望,每一次修改也不敢確定是不是有放到最後的產品中。大概就是混雜了無奈與無力的工作情緒吧?

  再來應該就是剛接手 Xbox 的時候。一來當時部門人手少,3 個人要負責所有事情,非常辛苦。二來當時對於遊樂器平台遊戲的開發完全沒經驗,很多東西都是從無到有跌跌撞撞的做,還要忙著發各種不同的道歉信函(笑),因為有錯要更正的東西實在太多了。

G:中文化過程中覺得哪部分是最辛苦之處?

張:最辛苦的大概就是測試過程中繁雜的確認步驟,因為需要測試的東西非常多,但是遊戲在開發階段又隨時都在變,如何確認每個需要注意的步驟是件相當辛苦的例行工作。

  另外就是必須要面對形形色色不同的遊戲開發商。微軟自己的商用軟體開發有一套明確的流程與規範,很容易就能掌握。但是擔任遊戲開發的通常是外部的工作室或廠商,每個廠商都有他們自己的一套,要如何去面對這麼多不同的廠商,協調並溝通中文化的流程,是件反覆且辛苦的工作。有時才剛與前一個廠商協調完,結果後一個廠商又犯相同的錯誤,甚至同一家廠商不同一批人馬、或同一款遊戲不同代狀況都可能不一樣。

  不過反過來說,正因為遊戲開發的狀況多變富挑戰性,所以也比一切按部就班的商用軟體開發來得有趣多了。雖然有許多辛苦與挑戰,不過我們部門中的成員都是對遊戲懷有高度熱情的“玩家”,所以一路都能堅持下去,信守我們對於玩家的承諾,以全面提供優質的中文化遊戲為目標持續努力,也希望玩家能多多支持並給 予我們建議與指教。

  目前我們手頭上還有很多中文化遊戲專案正在進行中,包括即將於明年初推出的《最後一戰 星環戰役(Halo Wars)》與《忍者狂刀(Ninja Blade)》,請玩家期待!

2008-12-10

Ubuntu安装NX

  据说NX是所谓下一代图形远程登录技术,是不是就不知道了,但肯定比直接用vnc强悍的多。上次系统有毛病,装不成功,这次继续。

首先要装好ssh服务。
sudo apt-get install openssh-server

配置:
vi /etc/ssh/sshd_config
该怎么改就改成啥样吧。

配置认证用的密钥:
可以直接用服务器生成再分发或者由客户端生成传到服务器上:
前者:
ssh-keygen -b 4096 -t dsa
生成完以后保存一份公钥为
~/.ssh/authorize_keys
之后分发这份公钥。

后者:
比如用secureCRT生成的公钥,传到服务器上要先转换一下格式:
ssh-keygen -i -f identity.pub > openssh.pubkey
之后还是拷贝到~/.ssh/authorize_keys
即可

接下来就开始安装nx了。
因为nx本身和ssh一样是商业技术,所以我们用freenx
根据ubuntu的wiki,我们这样做:
先添加源:
vi /etc/apt/source.list
因为是ubuntu 8.10,所以安装freenx需要添加这个更新源:

deb http://ppa.launchpad.net/freenx-team/ubuntu intrepid main
deb-src http://ppa.launchpad.net/freenx-team/ubuntu intrepid main
ubuntu 8.04之后的版本直接
sudo apt-get update
sudo apt-get install freenx

装好后先修改一下ssh的配置,因为nx是基于ssh的,所以需要改一些东西,把相应内容设置成如下:
PubkeyAuthentication yes
AuthorizedKeysFile %h/.ssh/authorized_keys2


如果你ssh监听的不是默认的22端口,还需要修改一下nx的设置:
vi /etc/nxserver/node.conf
注意这个配置文件不允许等号之间有空格。

好了,这样nx server就搞定了。剩下就是客户端的问题:
windows下必须要 nomechine的nxclient 1.5版本,据说新版本无法连接freenx的服务器。
可以到http://www.industrial-statistics.com/info/nxclients?IndStats=bbf349f90a518f5a54f290e887d9c768下载。
我这里用windows 1.5.0 build 114是不能用的。但是windows 1.5.0 build 103可以用。
至于那个windows 1.5.0 build 106就没有试了。

在使用上,freenx server默认安装好使用的是standard NoMachine key来进行认证。很明显,只需要保持默认设置输入密码就可以登录了。先体验一把美妙的安全的gui远程界面吧。
不过如果ssh设置禁用密码认证的话就比较麻烦。而且有时候还需要使用用户自己的公钥来认证。另外,因为默认使用,如果一旦客户使用若口令的话无疑是一种大风险。
这样就需要进行如下配置:
sudo dpkg-reconfigure freenx-server
选择create new custom key
之后
su root
cd /var/lib/nxserver/home/custom_keys
cp client.id_dsa.key /home/testuser/.ssh/nx_client.id_dsa.key

这样就把生成的key拷贝出来了。不过由于权限问题不能下载,所以重新分配一下权限:
sudo chmod 644 /home/testuser/.ssh/nx_client.id_dsa.key
之后分发到你需要登录此服务器的电脑上,在client那里导入这个key就可以登录了。

  不过这里我还是没能解决ssh公钥验证登录的要求。因为nxclient只能导入一个密钥。而那个密钥,应该是nx这个用户home目录下的.ssh目录中的公钥,对应的是nx帐户的私钥authority_keys2,但是,问题在于按照nx的模式,应该是先登录到主机用nx的用户。之后再转成各个帐户的session。所以这里不能通过修改那个customkey来实现其他帐户的ssh验证登录。
不过目前我了解的只是nx的表面上的东西,也许其他设置能够配置出我们的需求吧。

最后上传一张google到的nx的架构图:

2008-12-09

Ubuntu 使用笔记更新

  之前为了学习的目的装的ubuntu结果变成了主要操作系统了。但是当时分区的情况不足以用的爽,于是就全盘格了重新规划分区。
  这一次使用ubuntu有了经验,安装东西明显知道原理了。不过还是选择了不少新的东西。

首先ATI的显卡驱动。
  虽然之前很麻烦,不过如果是刚装好的操作系统,直接装就可以了。重启以后
sudo aticonfig --initial
之后就可以了。

opera:
  这个东西前一篇帖子有介绍。新的opera的字体机制让我终于在linux下看到了漂亮的opera。。。。

mldonkey:
  传说中的最快的p2p下载软件。支持各种协议,又能同时连接多个edonkey server似乎很无敌的样子,不过装起来比较麻烦:
先去项目官方页面:http://mldonkey.sourceforge.net/Main_Page
下个源码或者包回来安装。
再弄个gui前端sancho:
装好以后稍微配置一下就好了。
此外,如果不想要gui的话直接用浏览器输入localhost:4080也可以用网页管理mldonkey

自己编译mplayer和ffmpeg:
  这两个东西其实都不大好编译,主要是包依赖关系相当复杂,不过ubuntu下还是好解决的。去apt里面看看它依赖哪些包。你装上就是。另外还有一个关键的问题是,那个x264和libx264好像不是一个东西,必须要装x264才能用。最后一点就是如果没有编译mplayer的gui的话一些参数是不能用的。

  开始编译的时候如果需要libdvdnav和libdvdread的支持,可以编译libdvdnav libdvdread,不过这个东西新版的编译比较不同,先装好autoconf包。
之后sh autogen.sh再之后
make &&make install
  安装了dvdnav之后就可以使用dvd的菜单功能了。这里只需要在编译mplayer的时候增加一个个参数 --enable-dvdnav
注意的是,如果使用dvdnav的话两者都要是svn的新版。

Opera 9.62 比较完善的中文显示解决&一些其他问题的解决方案

  Opera这个浏览器实话说windows的版本很好用,linux的版本却莫名其妙我是经常卡死。不过由于我的需求我还是要用的,现在的最新版是9.62 2244,10也出beta了,希望以后能够比较更加完善。
  目前还是老问题,首先是中文字体显示的问题,这里按照opera中文社区的方法的确解决了不少问题。从9.5之后引入的font.ini的机制解决了不少问题,相当不错。
方法:vi /usr/share/opera/ini/font.ini

Opera Preferences version 2.1
; Do not edit this file while Opera is running
; This file is stored in UTF-8 encoding


[matches]

; One can disable xft or core fonts if necessary
;engine:xft=blacklist
;engine:x11=blacklist

; Foundries that are generally known to have bad implementations
;foundry:urw|abiword|ultimo=bad

; Generic settings - families with known glyph types
family:times|nimbus roman no9 l|times new roman|bitstream vera serif=serif
family:helvetica|nimbus sans l|arial|bitstream vera sans=sans-serif
family:courier|courier new|bitstream vera sans mono=monospace

; Generic settings - generic families which have good, readable designs,
; but where we can't be sure the implementation is any good.
family:times|times new roman=good
family:helvetica|arial=good
family:courier|courier new=good
family:clearlyu *=good

; Known high-quality fonts
family:arial|verdana|times new roman;foundry:microsoft|monotype=excellent
family:Nimbus*=verygood
foundry:bitstream=excellent

; Known fonts that solves specific problems
family:mincho|gothic=japanese good try-first
family:kochi*=japanese good try-first
family:₩ヨᄚ¥ᆴヒ¦ᄑモ=chinese-s good try-first
family:baekmuk*=korean good try-first

; Fonts that we know should not be used for displaying text
family:*dingbat*|*dings*|agathadaimon=symbol

; Specific fonts at the bottom
; Known bad implementations
engine:x11;foundry:mdk;family:helvetica=blacklist


[default]
usage=by-request
preference=5

[serif]
style=serif

[sans-serif]
style=sans-serif

[monospace]
style=monospace

[cursive]
style=cursive

[fantasy]
style=fantasy

[japanese]
style=japanese

[chinese-t]
style=chinese-t

[chinese-s]
style=chinese-s

[korean]
style=korean

[try-first]
usage=try-first

[excellent]
preference=8
usage=try-first

[verygood]
preference=7
usage=try-first

[good]
preference=6

[bad]
preference=4

[avoid]
usage=by-request

[symbol]
usage=by-request

[blacklist]
usage=blacklist

  这个文件是从opera中文社区那里下载的内容,如果希望使用其他字体自己修改
family:₩ヨᄚ¥ᆴヒ¦ᄑモ=chinese-s good try-first
这一行换成其他的。记住这个文件是使用utf8编码保存的。如果想要改成使用系统字体显示中文就把那一行改成
family:AR PL UMing*=chinese-s good try-first


  这样一修改基本中文就能够很漂亮的显示了,再也不会有当年恶心的中文了。。。

  一般如果想正常使用opera的话还需要解决一些问题:

flash的问题:
  其实现在的opera不管32位还是64位都能很好的使用flash了。不过效率。。。的确垃圾。。。
  去http://get.adobe.com/flashplayer/ 下载tar.gz的包。
  直接解压开,把里面那个 libflashplayer.so拷贝到/usr/lib/opera/plugins里面。

2008-12-08

Windows2008/Vista免重装开启NCQ

  NCQ是一种提高硬盘磁头读写效率的技术,总体来说对硬盘的性能是有一定的提高的,那么开启它只需要在BIOS里面把SATA类型设置成AHCI就可以了,不过,如果之前装了系统,这会儿改成AHCI的话,不管是Vista还是XP不出意外都会蓝屏。
  XP下要改比较麻烦,不过如果是Vista或者Windows Server 2008下要改就相当简单了。根本不需要重装系统。
用regedt32打开HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Msahci
之后把其中start的键值由4改成0,重启之后就会自动加载驱动了。

2008-12-06

大学阶段的第一次主导项目失败

  基本上,从2个月前的开始策划到今天比赛的结束,我们的整个项目可以说是失败了,当然,失败并不是没有做出东西,而是在项目开发过程中直至今天比赛这整个过程中项目管理的失败。
  这次的项目是我在大学阶段第一次以项目主导者的身份运行的项目,也是本博客这个归档的第一篇,将来所有的项目经验也都将记录于这个归档。
  这次的项目是一个纯粹软件范畴的case,大略的说是一个在自动队列处理的模型上构建起的三层架构的视频编码控制系统。
  整个项目被我们分成两部分,一个是我全权负责的数据层上的编码调度程序,一个是由另外一名协作者负责的表现层和业务层上的JSP页面程序。具体的技术分工是:我负责的编码调度程序完全使用Java来处理,通过RMI或序列化数据来获取JSP的控制指令来控制视频文件的编码状态,而数据交互使用的XML扫描类也由我构建,我选择的是DOM,目前阶段我负责的这部分代码约在1000行左右。而另外一部分即JSP页面则有协作者负责,他这个阶段达到的目标是:使用我构建的XML扫描类来读取队列信息以及已处理文件信息,添加待转换项目,将这三部分以表格的形式呈现给用户。
  项目规划是做到了,但是,整个过程中,我们出了很多的错误和疏漏,直至最终今天比赛,我们急急忙忙的debug程序,编写介绍资料、答辩幻灯片以及构建展示平台。更甚的是,到了最后一天,我的协作者没能写出任何JSP页面。
  从这次经验中,从我们遇到的问题中,我们总结了一些失败的教训和项目经验:

  • 项目文档是一个项目的指导性文件,应该在项目开始实行之前就编写好。并且各成员应该明确了解整个项目的情况,共同探讨定出项目文档。此外如果是软件类项目还必须要有开发文档,以使各项目协作者能够轻松的根据开发文档来模块化的编写程序。
  • 作为一个软件项目,必须要有明确的项目日程安排,分发到各个成员处,确保所有人都明白、并且确认了自己的职责和时限。并且在各个项目日程规定的审核时间时对各个负责人的职责部分进行评估,并调整日程安排的一些细节。
  • 项目的展示平台等在比赛时需要用到的基础设备需要预先几天几次调整,确保项目展示不出问题。
  • 答辩幻灯片与项目介绍资料需要根据项目文档的指导尽早准备好,并在过程中不断改进。
  • 项目协作者的选择上,应该在项目开始之前就预先了解到各个成员的能力水平如何,能做出何种东西,长处是什么短处是什么。这样才能对应的分配好各个成员的职责,高效果的完成项目。
  • 最后,还有一点最容易被我们忽视的是,如果参加国内的一些比赛,在写项目资料的时候最好用一些对比的数据,即使结果是显而易见的,或是公认的。

  在这次的项目上,我在以上几点都没能做到应该要做的。以至于有现在的失败。诚可为鉴,后当戒之!当审慎处之!

2008-12-02

Warning: Smarty error: unable to read resource: "index.htm" in .../smarty/libs/Smarty.class.php on line 1092 的解决方法

站点用到了smarty,迁移到其他服务器的时候结果不能使用提示类似于这样:

Warning: Smarty error: unable to read resource: "index.htm" in .../smarty/libs/Smarty.class.php on line 1092

这种情况就是无法读取到index.htm文件,所以只需要修改一下这个Smarty.class.php中的配置信息即可。

打开文件找到下面这些内容的位置:


    /**
* The name of the directory where templates are located.
*
* @var string
*/
var $template_dir = 'templates';

/**
* The directory where compiled templates are located.
*
* @var string
*/
var $compile_dir = 'templates_c';


只需要把这两个变量的值改成模板文件夹的相对路径即可。这里是相对于站点路径的相对路径,
我如说我的域名lab.xxx.xxx.xxx这个站点就是使用smarty的站点。现在我的模板文件的路径是
lab.xxx.xxx.xxx/smarty/templates,所以我要把上面两个设置成
smart/templates和smarty/templates_c

2008-11-26

Java中的MD5的使用

  一般来说定长度输出只需要用hash方法就可以了,不过有时候需要用md5的值来进行一些特别的处理工作,所以要用到它,自己写md5比较麻烦,所以可以用java.security包里面的东西。以下是网上搜到的一个范例:


  1. public class HashUtils {
  2. /**
  3. * 文件SHA-1 Hash值生成器
  4. *
  5. * @param in
  6. * @return
  7. * @throws Exception
  8. */
  9. public static String generateFileSHA1(String fileUrl) throws Exception {
  10. InputStream in = new BufferedInputStream(new FileInputStream(fileUrl));
  11. MessageDigest md = MessageDigest.getInstance("SHA-1");
  12. byte[] buffer = new byte[8192];
  13. int length = -1;
  14. while ((length = in.read(buffer)) != -1) {
  15. md.update(buffer, 0, length);
  16. }
  17. return StringBytesTransformUtils.bytesToHexString(md.digest());
  18. }
  19. /**
  20. * 文件MD5 Hash值生成器
  21. *
  22. * @param fileUrl
  23. * @return
  24. * @throws Exception
  25. */
  26. public static String generateFileMD5(String fileUrl) throws Exception {
  27. InputStream in = new BufferedInputStream(new FileInputStream(fileUrl));
  28. MessageDigest md = MessageDigest.getInstance("MD5");
  29. byte[] buffer = new byte[8192];
  30. int length = -1;
  31. while ((length = in.read(buffer)) != -1) {
  32. md.update(buffer, 0, length);
  33. }
  34. return StringBytesTransformUtils.bytesToHexString(md.digest());
  35. }
  36. }

2008-11-23

Solaris下php+curl编译

  由于需要重新规划服务器的管理,所以,我们需要按照一定要求重新编译PHP。
在Solaris下编译php需要以下软件包的支持:

gcc
make
flex
bison
m4
autoconf
automake
perl
gzip
tar
GNU sed

此外,根据需求我还增加了这些软件包:

libiconv
libidn
openssl-0.9.8i
curl
expat
fontconfig
freetype
gd
libiconv
jpeg
mysql
libpng
sasl
libxml2
zlib
xpm

我的编译参数是这样:

./configure --prefix=/usr/php --with-mysql=/usr/mysql/ --with-mysqli=/opt/Webservices/mysql/bin/mysql_config --with-apxs2=/usr/httpd/bin/apxs --with-apache=/usr/httpd/ --enable-fastcgi --with-zilb=shared --with-gd=shared --with-curl=shared --with-curlwrappers=shared --with-libxml-dir=/usr/local/ --with-openssl-dir=/usr/local/ssl/ --enable-ftp --with-jpeg-dir=/usr/local/ -with-png-dir=/usr/local/ --with-xpm-dir=/usr/local/ --with-freetype-dir=/usr/local/ --enable-gd-native-ttf --enable-zip

在网上搜到with-mysqli和enable-embedded-mysqlii同时用会出错,所以我就没有测试了
这样编译完成后记得make test通过了就ok。接下来就可以了。
如果你希望不重新编译php就添加curl模块则看下面这段文章
在Wordpress中启用插件Flickr Photo Album for WordPress时,需要系统支持cURL。大多数资料里都是比较简单的方法,即在编译php时加上cURL支持但由于我的系统里已经安装有php,并且有一些其他配置,重新安装太麻烦于是找到了这篇《linux php 扩展库编译》,验证通过,转载以记录一下以php curl 扩展库编译为例。
假如原先编译的php目录在/oracle/php4目录下;apache在/oracle/apache2目录下;php源代码在/home/wugw目录下。如果实际目录与假定的目录不一致,则在下面的命令中做调整。
1. 找到当前运行的php版本的源代码目录,如 php-4.4.7。进入curl扩展库目录。$cd /home/wugw/php-4.4.7/ext/curl
2. 调用phpize程序生成编译配置文件。$/oracle/php4/bin/phpize
3. 编译扩展库,分别执行下面的configure和make命令。$./configure –with-php-config=/oracle/php4/bin/php-config
##configure这一步执行通过后,再执行make命令,如果configure执行不通过,则查找错误原因。$make
##make成功执行后,生成的扩展库文件在当前目录的 modules 子目录下,如 /home/wugw/php-4.4.7/ext/curl/modules/curl.so
4. 配置php.ini文件##将编译好的扩展库文件复制到apache2 modules目录下。$cp /home/wugw/php-4.4.7/ext/curl/modules/curl.so /oracle/apache2/modules/.
##找到php.ini文件所在目录位置,然后编辑。可以通过查看phpinfo信息来确定php.ini文件位置。##在php.ini文件中找到设置扩展目录的位置,然后将扩展路径设置到apache2 modules目录下extension_dir = “/oracle/apache2/modules/”
##在php.ini的设置扩展库位置,设置要添加的扩展库。extension=curl.so
##以后如果还要添加别的扩展库的话,则只需先将php扩展库编译好,然后copy到apache2 modules目录下,##然后再在这个位置,另取一行将编译后的扩展库文件名加上即可
5. 重启apache,查看phpinfo信息,即可看到刚才添加进去的curl扩展库。

2008-11-17

apache的两种虚拟主机配置

  Apache有两种配置虚拟主机的方法,一种是基于IP的,一种是基于域名的。一下是官方文档中的说明:
  术语"虚拟主机"是指在一个机器上运行多个网站(比如:www.company1.com和www.company2.com)。如果每个网站拥有不同的IP地址,则虚拟主机可以是"基于IP"的;如果只有一个IP地址,也可以是"基于主机名"的,其实现对最终用户是透明的。
Apache是率先支持基于IP的虚拟主机的服务器之一。1.1及其更新版本同时支持基于IP和基于主机名的虚拟主机,今后,不同的虚拟主机有时会被称为"基于主机"或"非IP虚拟主机"。


基于域名的虚拟主机和基于IP的虚拟主机比较
  基于IP的虚拟主机使用连接的IP地址来决定相应的虚拟主机。这样,你就需要为每个虚拟主机分配一个独立的IP地址。而基于域名的虚拟主机是根据客户端提交的HTTP头中标识主机名的部分决定的。使用这种技术,很多虚拟主机可以共享同一个IP地址。
  基于域名的虚拟主机相对比较简单,因为你只需要配置你的DNS服务器将每个主机名映射到正确的IP地址,然后配置Apache HTTP服务器,令其辨识不同的主机名就可以了。基于域名的服务器也可以缓解IP地址不足的问题。所以,如果没有特殊原因使你必须使用基于IP的虚拟主机,您最好还是使用基于域名的虚拟主机。下列情况下,你可能会想要使用基于IP的虚拟主机:

  • 一些古董级的客户端与基于域名的虚拟主机不兼容。为了与基于域名的虚拟主机兼容,客户端必须发送"Host"头。HTTP/1.1规范中对此做了要求。而所有现在常见的仅支持HTTP/1.0的旧版本浏览器都以附加的方式实现了这个要求。如果你又想支持这些老浏览器,又想使用基于域名的虚拟主机。我们提供了一个技术方案,你可以在本文末尾看到它。
  • SSL协议先天特性决定了基于域名的虚拟主机无法成为SSL安全服务器。
  • 一些操作系统和网络设备实现的带宽管理技术无法在多个主机共享一个IP的情况下区别它们。

因为我只需要配置基于域名的虚拟主机,所以就只参考了这部分内容,其余部分可以参考文档:http://eduunix.ccut.edu.cn/index2/man/apache2/vhosts/index.html

  为了使用基于域名的虚拟主机,你必须指定服务器IP地址(和可能的端口)来使主机接受请求,这个可以用NameVirtualHost指令来进行配置。如果服务器上所有的IP地址都会用到,你可以用"*"作为NameVirtualHost的参数。如果你打算使用多端口(如运行SSL)你必须在参数中指定一个端口号,比如"*:80"。请注意,在NameVirtualHost指令中指定IP地址并不会使服务器自动侦听那个IP地址。请参阅设置Apache使用的地址和端口一章获取更多详情。另外,这里设定的IP地址必须对应服务器上的一个网络接口。
  下一步就是为每个虚拟主机建立段。的参数与NameVirtualHost的参数必须是一样的(比如说,一个IP地址或"*"代表的所有地址)。在每个段中,至少要有一个ServerName指令来指定伺服哪个主机和一个DocumentRoot指令来说明这个主机的内容位于文件系统的什么地方。

取消中心主机(Mainhost)
如果你想在现有的web服务器上增加虚拟主机,你必须也为现存的主机建造一个定义块。这个虚拟主机中ServerName和DocumentRoot所包含的内容应该与全局的ServerName和DocumentRoot保持一致。还要把这个虚拟主机放在配置文件的最前面,来让它扮演默认主机的角色。

  比如说,假设你正在为域名www.domain.tld提供服务,而你又想在同一个IP地址上增加一个名叫www.otherdomain.tld的虚拟主机,你只需在httpd.conf中加入以下内容:
NameVirtualHost *:80ServerName www.domain.tldServerAlias domain.tld *.domain.tldDocumentRoot /www/domainServerName www.otherdomain.tldDocumentRoot /www/otherdomain
当然,你可以用一个固定的IP地址来代替NameVirtualHost指令中的"*"号,以达到一些特定的目的。比如说,你可能会希望在一个IP地址上运行一个基于域名的虚拟主机,而在另外一个IP地址上运行一个基于IP的或是另外一套基于域名的虚拟主机。
很多服务器希望自己能通过不只一个域名被访问。我们可以把ServerAlias指令放入小节中来解决这个问题。比如说在上面的第一个配置段中ServerAlias指令中列出的名字就是用户可以用来访问同一个web站点的其它名字:
ServerAlias domain.tld *.domain.tld
这样,所有对域domain.tld的访问请求都将由虚拟主机www.domain.tld处理。通配符标记"*"和"?"可以用于域名的匹配。当然你不能仅仅搞个名字然后把它放到ServerNameServerAlias里就算完了。你必须先在你的DNS服务器上进行配置,将这些名字和您服务器上的一个IP地址建立映射关系。
最后,你可以把其他一些指令放入段中,以更好的配置一个虚拟主机。大部分指令都可以放入这些段中以改变相应虚拟主机配置。如果您想了解一个特定的指令是否可以这样运用,请参见指令的作用域。主服务器(main server)范围内的配置指令(在所有配置段之外的指令)仅在它们没有被虚拟主机的配置覆盖时才起作用。
这样,当一个请求到达的时候,服务器会首先检查它是否使用了一个能和NameVirtualHost相匹配的IP地址。如果能够匹配,它就会查找每个与这个IP地址相对应的段,并尝试找出一个与请求的主机名相同的ServerNameServerAlias配置项。如果找到了,它就会使用这个服务器。否则,将使用符合这个IP地址的第一个列出的虚拟主机。
综上所述,第一个列出的虚拟主机充当了默认虚拟主机的角色。当一个IP地址与NameVirtualHost指令中的配置相符的时候,主服务器中的DocumentRoot将永远不会被用到。所以,如果你想创建一段特殊的配置用于处理不对应任何一个虚拟主机的请求的话,你只要简单的把这段配置放到段中,并把它放到配置文件的最前面就可以了。,

我配置的例子:



NameVirtualHost 10.2.3.4:80
<VirtualHost 10.2.3.4:80>

NameVirtualHost 10.2.3.4:80
<VirtualHost 10.2.3.4:80>
DocumentRoot /home/testroot
ServerName test.test.test.com
ErrorLog logs/test.test.test.com_err_log
TransferLog logs/test.test.test.com_access_log
</VirtualHost>

<VirtualHost 10.2.3.4:80>
DocumentRoot /home/testroot/bbs
ServerName tttt.test.test.com
ErrorLog logs/tttt.test.test.com_err_log
TransferLog logs/tttt.test.test.com_access_log
</VirtualHost>

2008-11-15

Solaris10 05/08 在服务器上安装的管理 Part.2

  昨天手工之后没有检查,结果今天刚开始配置就遇到问题了,tar不支持gzip,没有make等等,原来安装的时候选了核心组件版本,结果现在很多包都要装过。

  还是老规矩直接上http://www.sunfreeware.com/去下载包,安装就直接用:

pkgadd -d filename

安装好。装好后记得要修改一下系统变量:

vi /etc/profile

添加下面蓝色的

#ident "@(#)profile 1.19 01/03/13 SMI" /* SVr4.0 1.3 */
# The profile that all logins get before using their own .profile.
trap "" 2 3

export LOGNAME PATH

PATH=/usr/local/bin:/usr/local/sbin: /usr/bin:/usr/sbin:/usr/ccs/bin;

expotr PATH


改完以后登录的用户的path都变成上面设置的了,不过如果你su到root的话。他的path却还是/usr/bin /usr/sbin,难度root账户的path设置无效了吗?不是,其实是这样:


如果以root用户身份登录或者用 su - 命令进行切换,则修改 home目录下.profile是管用的. 如果是简单的 用 su 进行切换, 则系统保留除 PATH之外的所有环境变量, PATH被设为 /etc/default/su 中的 SUPATH ,缺省时为/usr/sbin:/usr/bin
所以,如果要让PATH还是你设置的那个你就需要用su -命令来switch user。

此外,一个一定要注意的地方是,默认gun的那些包安装都会把执行文件装到/usr/local/bin下面,而sun自带的相同名称的软件都会放到/usr/bin下面,所以,要谨防因为调用错了程序而导致的奇怪问题。因为我一般都用gun版本的,所以只要保证path里面/usr/local/bin在后者之前即可。

之后编译安装mysql,这里,我遇到的问题是出现了libstdc++.so.6 找不到文件的错误,其实就是make的时候它搜寻的是/usr/lib/libstdc++.so.6但是在Solaris里面,这个文件在/usr/local/libstdc++.so.6,所以只需要做一个软连接就搞定了。

安装完之后,为了能够使用SMF来管理mysql的启动,需要做这些:
在/var/svc/manifest/network目录建立文件mysql.xml内容



<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">

<!-- MySQL Manifest - cuddletech, benr, August 28th, 2005 -->

<service_bundle type='manifest' name='cuddletech:mysql'>

<service
name='application/mysql'
type='service'
version='4'>

<create_default_instance enabled='false'/>

<single_instance/>

<dependency name='fs-local'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri value='svc:/system/filesystem/local'/>
</dependency>

<exec_method
type='method'
name='start'
exec='/etc/sfw/mysql/mysql.server start'
timeout_seconds='60' />

<exec_method
type='method'
name='stop'
exec='/usr/sfw/bin/mysqladmin shutdown'
timeout_seconds='60' />

<exec_method
type='method'
name='refresh'
exec='/usr/sfw/bin/mysqladmin refresh'
timeout_seconds='60' />

<stability value='Unstable' />

<template>
<common_name>
<loctext xml:lang='C'>MySQL Server</loctext>
</common_name>
<documentation>
<doc_link name='mysql.org'
uri='http://dev.mysql.com/doc/mysql/en/index.html' />
</documentation>
</template>
</service>

</service_bundle>


之后使用svccfg验证并导入配置文件
#svccfg validate /var/svc/manifest/network/mysql.xml
#svccfg import /var/svc/manifest/network/mysql.xml
这样就ok了。
基本的配置mysql:
获得配置文件:
cp /opt/mysql/share/mysql/my-large.cnf /etc/my.cnf
之后初始化数据库:
/opt/mysql/bin/mysql_install_db --user=mysql
之后启动数据库:
/opt/mysql/bin/mysqld_safe --user=mysql &
测试关闭数据库:
/usr/sfw/bin/mysqladm -u root shutdown
如果要使数据库的编码改为utf8
那么
vi /etc/my.cnf
在[client]后面添加一行
default-character-set=utf8
在[mysqld]之后添加同样的一行。
default-character-set=utf8
之后启动以后mysql -uroot
show variables like 'character%';
会显示如下结果
+--------------------------+----------------------------+
Variable_name Value
+--------------------------+----------------------------+
character_set_client utf8
character_set_connection utf8
character_set_database utf8
character_set_filesystem binary
character_set_results utf8
character_set_server utf8
character_set_system utf8
character_sets_dir /usr/share/mysql/charsets/
+--------------------------+----------------------------+
不过可惜的是,这样加使用mysqladmin就会提示
/usr/sfw/bin/mysqladmin: unknown variable 'default-character-set=utf8'
暂时没有去找新的方法。

接下来安装apache,参考之前的一篇文章。装好之后同样为了整合到SMF,这样做:

vi /var/svc/manifest/network/apache2.xml



<?xml version="1.0"?>

<!-- Copyright 2004 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. ident "@(#)http-apache.xml 1.2 04/11/11 SMI" -->

<service_bundle name="SUNWapchr:apache" type="manifest">

<service name="network/http" type="service" version="1">

<!-- Because we may have multiple instances of network/http provided by different implementations, we keep dependencies and methods within the instance. -->

<instance name="apache" enabled="false">
<dependency name="physical" type="service" restart_on="error" grouping="optional_all">
<service_fmri value="svc:/network/physical:default">
</dependency>

<exec_method name="start" type="method" timeout_seconds="60" exec="/etc/init.d/apache start">

<exec_method name="stop" type="method" timeout_seconds="60" exec="/etc/init.d/apache stop">

<exec_method name="refresh" type="method" timeout_seconds="60" exec="/etc/init.d/apache restart">

<property_group name="httpd" type="application">
<stability value="Evolving">
<propval name="ssl" type="boolean" value="false">
</property_group>

<property_group name="startd" type="framework">
<!-- sub-process core dumps shouldn't restart session -->
<propval name="ignore_error" type="astring" value="core,signal">
</property_group>

</instance>

<stability value="Evolving">

<template>
<common_name>
<loctext lang="C">
Apache HTTP server
</loctext>
</common_name>
<documentation>
<manpage title="apache" section="1M">
<doc_link name="apache.org" uri="http://httpd.apache.org">
</documentation>
</template>
</service>

</service_bundle>



之后导入svccfg:
# svccfg validate /var/svc/manifest/network/apache2.xml
# svccfg import /var/svc/manifest/network/apache2.xml

2008-11-14

Solaris10 命令行下卸载StarSuit

  因为装了些没用的东西,所以现在要卸载,在命令行下,要做到这个,我们需要做一些工作:
这是官方给的说明文件:
De-Installation
Do not delete the StarOffice files from the file system. We strongly recommend to use the StarOffice Java setupor the pkgrm tool to de-install StarOffice. The Solaris package repository would become inconsistent when youremove the StarOffice files via rm -r .

  • Become root if necessary
    su -
  • Create a file 'so_packages' with all StarOffice related Solaris packages
    pkginfo -x cut -f1 -d " " grep staroffice > /tmp/so_packages
  • For a silent installation of StarOffice it is necessary to create an admin file. This avoids recurringqueries at the installation.
    echo action=nocheck > /tmp/admin
    echo conflict=nocheck >> /tmp/admin
    echo rdepend=nocheck >> /tmp/admin
  • Remove all packages listed in the file
    pkgrm -a /tmp/admin -n `cat /tmp/so_packages`
  • There are a number of packages in the StarOffice installation set does not use the term 'staroffice'.These packages including a font server (SUNWfreetype2) and an Ababas D database server(SUNWadabas). To remove these packages :
    pkgrm SUNWfreetype2 SUNWadabas
    Depending on your Java environment you have to deinstall following three packages which includingthe Java Runtime Environment which was installed by the StarOffice installer. De-Installing thesepackages could damage you Java environment.
    pkgrm SUNWj5rt SUNWj5man SUNWj5cfg

Solaris10 05/08 在服务器上安装的管理 Part.1

  之前一直使用01/08版的,这次为了直接支持ZFS boot特地弄了个05/08版的,结果变化还真不少,而且能在核心机房的时间不多,所以还是需要一些配置的。
  安装过程之中设置好网络,装好后立马把SSH配置调整好,之后立马添加普通用户:
useradd -u 22222 -g adm -d /export/home/testuser -m -s /usr/bin/bash testuser
  这样一来就可以远程用SSH登录了,记得修改一下root账户的默认shell
vi /etc/passwd
  之后收起光盘走人了。。。。

DNS配置
  回到自己的机器上,SSH过去,好了,现在我们开始修改吧。首先,有时候你会发现,虽然在安装过程中指定了DNS服务器,但是在实际装好后DNS用的/etc/resolv.conf这个文件更本不存在。据说这是Solaris 2系列历史上遗留下来的毛病,即安装过程中你可以指定 DNS服务器 和 默认路由,但他不会自动保存。必须在安装完成系统之后手工建立和设定/etc/resolv.conf 和 /etc/defaultrouter 两个文件。不过我这里倒是有后者。
  要使DNS生效,还需要一番配置,首先是resolv.conf文件:
vi /etc/resolv.conf
--------------------------content to be added--------------------------------
; Sample resolv.conf file for the machine polaris
domain doc.com
; try local name server
nameserver 127.0.0.1
; if local name server down, try these servers
nameserver 123.45.6.1
nameserver 111.22.3.5
; sort the addresses returned by gethostbyname(3c)
sortlist
130.155.160.0/255.255.240.0
130.155.0.0
--------------------------------------------------------------------------

以上内容是从docs.sun.com转来的范例,注意domain后面的域名必须马上回车不允许任何空白字符出现。一般如果只需制定dns服务器的话只用nameserver。
编辑完后保存,下一步,编辑/etc/nsswitch.conf找到host这一行改成这样:
hosts: files dns
当然,根据使用的名字服务不同官方给出的配置是这样的:
-----------------------------------------------------------------------------
Specify DNS as a source of hosts information.
DNS can be the only source or an additional source for the hosts information. Locate the hosts line and use DNS in one of the ways shown below:
hosts: files dns
or
hosts: nisplus dns [NOTFOUND=return] files
or
hosts: dns nisplus [NOTFOUND=return] files
Do not use the above syntax for NIS clients, since they will be forced to search for unresolved names twice in DNS.
----------------------------------------------------------------------

最后启动服务:
svcadm enable network/dns/client
这样就可以使用DNS了。
Solaris解析域名的顺序是这样的:

  1. 查询/etc/nsswitch.conf以确定解析顺序,因为之前我们制定的是file dns ,所以先根据本地配置来查询,之后才是dns服务器,因此接下来
  2. 查询/etc/hosts如果没有再下来
  3. 从/etc/resolv.conf查询dns服务器和搜索顺序。之后提交请求。

接下来,由于我发现系统的时间不对。于是同步一下,有两种方法:
rdate time-a.nist.gov
ntpdate time-a.nist.gov


接下来处理一下服务,因为之前我害怕有软件包没有装好,之后还得回来装,所以就安装了entire版本的。现在需要去除一些服务。
online         10:33:13 svc:/application/graphical-login/cde-login:default
online 19:30:36 svc:/network/cde-spc:default
online 10:33:39 svc:/network/rpc/cde-ttdbserver:tcp
online 10:33:39 svc:/network/rpc/cde-calendar-manager:default
online 10:33:41 svc:/application/cde-printinfo:default

第一个cde-login是cde登录的服务,我们只用console,所以关了。
第二个的解释是:The CDE Subprocess Control Service is a network daemon that accepts requests from clients to execute commands and launch applications remotely.貌似它会打开6112端口,我试着关闭了,不影响ssh,所以也disable。
第三个是ToolTalk database server,tooltalk是xstart的一个组件,而这个server干下面这些活
  1. ToolTalk objects specs.
  2. ToolTalk session IDs of sessions with clients that have joined a file using the tt_file_join call.
  3. File-scoped messages that are queued because the message disposition is TT_QUEUED and a handler that can handle the message has not yet been started.

所以也禁用了。

第五个看了就知道没用。

第六个是CDE Print Viewer,The CDE Print Viewer program provides a graphical interface that displays the status of print queues and print jobs。所以也禁用了。

为什么要禁用这些?原因很简单,第一我不用gui 第二,你google一下就会发现这些服务都有漏洞报告,虽然比较早,但是也未必就被修复了。

要管理这些服务在Solaris里面非常方便,直接使用SMF管理。
inetadm :提供观察或配置由 inetd 控制的服务的功能
svcadm :提供执行常见服务管理任务(如启用、禁用或重新启动服务实例)的功能
svccfg :提供显示和处理服务配置系统信息库内容的功能
svcprop :从服务配置系统信息库中检索属性值,并采用适用于 shell 脚本的输出格式
svcs :提供服务配置系统信息库中所有服务实例的服务状态的详细视图

这样稍微处理了一下之后系统就干净了。可以开始接下来的工作了。

2008-11-12

一篇关于raidz配置的文章

  WHEN TO (AND NOT TO) USE RAID-Z


RAID-Z is the technology used by ZFS to implement a data-protection scheme
which is less costly than mirroring in terms of block
overhead.

Here, I'd like to go over, from a theoretical standpoint, the
performance implication of using RAID-Z. The goal of this technology
is to allow a storage subsystem to be able to deliver the stored data
in the face of one or more disk failures. This is accomplished by
joining multiple disks into a N-way RAID-Z group. Multiple RAID-Z
groups can be dynamically striped to form a larger storage pool.

To store file data onto a RAID-Z group, ZFS will spread a filesystem
(FS) block onto the N devices that make up the group. So for each FS
block, (N - 1) devices will hold file data and 1 device will hold
parity information. This information would eventually be used to
reconstruct (or resilver) data in the face of any device failure. We
thus have 1 / N of the available disk blocks that are used to store
the parity information. A 10-disk RAID-Z group has 9/10th of the
blocks effectively available to applications.

A common alternative for data protection, is the use of mirroring. In
this technology, a filesystem block is stored onto 2 (or more) mirror
copies. Here again, the system will survive single disk failure (or
more with N-way mirroring). So 2-way mirror actually delivers similar
data-protection at the expense of providing applications access to
only one half of the disk blocks.

Now let's look at this from the performance angle in particular that
of delivered filesystem blocks per second (FSBPS). A N-way RAID-Z
group achieves it's protection by spreading a ZFS block onto the N
underlying devices. That means that a single ZFS block I/O must be
converted to N device I/Os. To be more precise, in order to acces an
ZFS block, we need N device I/Os for Output and (N - 1) device I/Os for
input as the parity data need not generally be read-in.

Now after a request for a ZFS block has been spread this way, the IO
scheduling code will take control of all the device IOs that needs to
be issued. At this stage, the ZFS code is capable of aggregating
adjacent physical I/Os into fewer ones. Because of the ZFS
Copy-On-Write (COW) design, we actually do expect this reduction in
number of device level I/Os to work extremely well for just about any
write intensive workloads. We also expect it to help streaming input
loads significantly. The situation of random inputs is one that needs
special attention when considering RAID-Z.

Effectively, as a first approximation, an N-disk RAID-Z group will
behave as a single device in terms of delivered random input
IOPS. Thus a 10-disk group of devices each capable of 200-IOPS, will
globally act as a 200-IOPS capable RAID-Z group. This is the price to
pay to achieve proper data protection without the 2X block overhead
associated with mirroring.

With 2-way mirroring, each FS block output must be sent to 2 devices.
Half of the available IOPS are thus lost to mirroring. However, for
Inputs each side of a mirror can service read calls independently from
one another since each side holds the full information. Given a
proper software implementation that balances the inputs between sides
of a mirror, the FS blocks delivered by a mirrored group is actually
no less than what a simple non-protected RAID-0 stripe would give.

So looking at random access input load, the number of FS blocks per
second (FSBPS), Given N devices to be grouped either in RAID-Z, 2-way
mirrored or simply striped (a.k.a RAID-0, no data protection !), the
equation would be (where dev represents the capacity in terms of
blocks of IOPS of a single device):

Random
Blocks Available FS Blocks / sec
---------------- --------------
RAID-Z (N - 1) * dev 1 * dev
Mirror (N / 2) * dev N * dev
Stripe N * dev N * dev


Now lets take 100 disks of 100 GB, each each capable of 200 IOPS and
look at different possible configurations; In the table below the
configuration labeled:

"Z 5 x (19+1)"

refers to a dynamic striping of 5 RAID-Z groups, each group made of 20
disks (19 data disk + 1 parity). M refers to a 2-way mirror and S to a
simple dynamic stripe.


Random
Config Blocks Available FS Blocks /sec
------------ ---------------- ---------
Z 1 x (99+1) 9900 GB 200
Z 2 x (49+1) 9800 GB 400
Z 5 x (19+1) 9500 GB 1000
Z 10 x (9+1) 9000 GB 2000
Z 20 x (4+1) 8000 GB 4000
Z 33 x (2+1) 6600 GB 6600

M 2 x (50) 5000 GB 20000
S 1 x (100) 10000 GB 20000


So RAID-Z gives you at most 2X the number of blocks that mirroring
provides but hits you with much fewer delivered IOPS. That means
that, as the number of devices in a group N increases, the expected
gain over mirroring (disk blocks) is bounded (to at most 2X) but the
expected cost in IOPS is not bounded (cost in the range of [N/2, N]
fewer IOPS).

Note that for wide RAID-Z configurations, ZFS takes into account the
sector size of devices (typically 512 Bytes) and dynamically adjust
the effective number of columns in a stripe. So even if you request a
99+1 configuration, the actual data will probably be stored on much
fewer data columns than that. Hopefully this article will contribute
to steering deployments away from those types of configuration.

In conclusion, when preserving IOPS capacity is important, the size of
RAID-Z groups should be restrained to smaller sizes and one must
accept some level of disk block overhead.

When performance matters most, mirroring should be highly favored. If
mirroring is considered too costly but performance is nevertheless
required, one could proceed like this:

Given N devices each capable of X IOPS.

Given a target of delivered Y FS blocks per second
for the storage pool.

Build your storage using dynamically striped RAID-Z groups of
(Y / X) devices.

For instance:

Given 50 devices each capable of 200 IOPS.

Given a target of delivered 1000 FS blocks per second
for the storage pool.

Build your storage using dynamically striped RAID-Z groups of
(1000 / 200) = 5 devices.

In that system we then would have 20% block overhead lost to maintain
RAID-Z level parity.

RAID-Z is a great technology not only when disk blocks are your most
precious resources but also when your available IOPS far exceed your
expected needs. But beware that if you get your hands on fewer very
large disks, the IOPS capacity can easily become your most precious
resource. Under those conditions, mirroring should be strongly favored
or alternatively a dynamic stripe of RAID-Z groups each made up of a
small number of devices.


ZFS混合存储池特性浅析

  我对磁盘阵列相关的东西不是很熟,但是可惜的是前天正好遇到这个问题,亟需解决,无奈之下思考起了解决方案,但是可惜的是,我的查询zfs和raidz的一些情况的时候要不找到它的原理说明,要不找到配置方法,但是总觉得差什么,就是没办法把整个过程联通起来,最后发现,这些资料都缺乏对整体架构的解释。无奈的在unix-center中求助也没人回答,不过今天正好有人发了一帖倒解决的我的问题,下文即转自此帖:http://www.unix-center.net/bbs/viewthread.php?tid=7053&extra=page%3D1

------------------------------------------
ZFS混合存储池特性浅析

  被称作“史上最后一个文件系统” 的ZFS,作为Solaris 10和OpenSolaris的新特性引进后,吸引了开源社区里无数人的关注,Linux之父Linus Torvalds公开表示对Solaris软件的ZFS(Zettabyte文件系统)特别感兴趣,苹果公司也宣称Mac OSX采用ZFS文件系统。从OpenSolaris 2008.05发布版开始,SUN在ZFS中引进了对Intel® High-Performance Solid State Drives (SSDs)的支持,提供混合存储池(Hybrid Storage Pools),本文将对此进行介绍。

1. 什么是SSD?
  SSD即固态硬盘(solid state disk),SSD由控制单元和存储单元(FLASH芯片)组成,简单的说就是用固态电子存储芯片阵列而制成的硬盘。闪存设备记忆数据不需要电源,而且读写数据的速度非常快,就像 DRAM一样。随着全球电价上涨,以及海量数据存取速度的需求,SSD越来越被看好。
  以下是SSD、HDD的存取速度、单位造价和耗电量的对比,IOPS 即Input/Output Operations PerSecond。

  • SSD:7000 write IOPS, 35000 read IOPS, $2/GB ,15-20w
  • HDD:180 write IOPS, 320 read IOPS ,$30/GB,2.5w
2. 改进存储系统的动机
  计算机系统设计的黄金法则——加快经常性事件(make the common case fast),道理十分明显,使经常性事件的处理速度加快能明显提高整个系统的性能。遵循大师们提出的这一准则,RISC、RAID等大批技术横空出世。前辈们屡试不爽的准则,在ZFS的创新过程中,又一次得到成功验证。
  从下图可以看出,随着多核技术的发展,特别是服务器端多核技术的广泛应用,磁盘存取速度的增长远远跟不上处理器处理速度的增长,整个系统的性能越来越拖累于慢速的磁盘。同时服务器端海里数据存取,产生的大量磁盘操作已经成了经常性事件。磁盘访问已经成为系统的瓶颈,而且数据中心大量磁盘产生的电费也是一笔不菲的成本。


SUN的ZFS+SSD解决方案

解决上述问题有以下一些方案

  • 增加内存缓存一些数据来提高数据存取性能,但是存储容量的增长速度远高于内存容量的增长,并且代价十分高昂。
  • 利用闪存比DRAM便宜且存取速度相当,将闪存设备作为另一层级的存储器引进数据中心。但这会出现转换成本或操作成本,使数据中心产生新的成本开支,面临新的管理纷争。
  可见以上方案均不是很合理,那么如何实现高性能、低能耗、对用户和操作者透明的解决方案呢?答案就是SUN ZFS+SSD混合存储池。

  ZFS+SSD的体系结构如下图,在系统中,使用少量高速SSD作为内存与磁盘的缓存,将经常使用的数据放到快速的缓存,加快了访问速度,同时保留了HDD的大容量存储能力。同时ZFS对SSD进行了无缝整合,可以把SSD 作为文件系统的二级缓存(L2 ARC)以及ZIL(ZFS Intent Log),自动优化系统充分利用快速SSD提供系统读写吞吐率。


ZFS+SSD的体系结构

  1. Read/Write Cache Pool:ZFS利用主存和SSD作为自动伸缩缓存(Adaptive Replacement Cache),并能检测访问模式
  2. ZIL(ZFS Intent Log) Pool:ZFS一般使用NVRAM,SSD(Solid State Disk)作为 ZIL存储池。ZFS是事务型文件系统,对于同步写操作,ZFS为系统中每一个ZFS文件系统维护一个ZIL。同步写操作的数据会先写入ZIL,并且会把磁盘的write cache的数据同步到磁盘上,然后应用的写操作返回。当文件提交命令发生时,ZFS会把ZIL里该文件的数据同步到磁盘上。
  3. DISK Pool:ZFS利用disk pool为文件系统自动分配HDD空间
3. 总结
ZFS文件系统是对传统文件系统的一次革命性的创新设计,ZFS解决了文件系统的完整性、安全性和可伸缩性以及管理困难等重要难题。ZFS文件系统是世界上第一个128位的文件系统,其存储容量和文件数量几乎只受硬件的限制,增加了混合存储池后ZFS功能将更强大,心动了吧,赶紧去装个OpenSolaris体验下吧!
参考文献:

[1] Solaris zfssolutionbrief
http://www.sun.com/software/solaris/pdf/solariszfs_solutionbrief.pdf
[2] Sun Intel flash pitch April 08, Bill Moore
[3] http://blogs.sun.com/jonathan_zh/date/20080620, Jonathan(Sun CEO)

2008-11-07

学习DOM

  虽然大概已经了解了DOM,不过对我来说org.w3c.dom的奇怪扫描方式让我对其易使用性大有疑惑,所以还是必须要学习一下DOM。
  这篇文章是在学习DOM的过程中整理出来的资料:

  DOM 文档是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档并且构造层次结构,然后才能做其他事情。由于它是基于信息层次的,因而 DOM 被认为是基于树基于对象的。
  XML 文件的基本组成部分包括:

  • XML 声明:基本的声明 将这个文件定义为 XML 文档。在声明中指定一种字符编码的情况并不鲜见,如下所示。通过这种方式,不管该 XML 文件使用的语言或字符编码是什么,只要解析器理解特定的编码,它就能够正确地读取该 XML 文件。
  • DOCTYPE 声明:XML 是人机之间交换信息的便利手段,但是要使它能够顺利地工作,必须要有一个公共的词汇表。可选的 DOCTYPE 声明可用于指定一个应该用来与此文件做比较的文档(在本例中为 orders.dtd),以确保不会产生任何混淆或丢失信息(例如,丢失一个 userid 或错误拼写某个元素名称)。以这种方式处理过的文档称为有效的文档。成功的有效性检查并不是 XML 所必需的,后面的例子实际上从文档中省略了 DOCTYPE 声明。
  • 数据本身:XML 文档中的数据必须包含在单个根元素内,比如下面的 orders 元素。要使 XML 文档得到处理,它必须是格式良好的(well-formed)


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ORDERS SYSTEM "orders.dtd">

<orders>

<order>
<customerid limit="1000">12341</customerid>
<status>pending</status>
<item instock="Y" itemid="SA15">
<name>Silver Show Saddle, 16 inch</name>
<price>825.00</price>
<qty>1</qty>
</item>
<item instock="N" itemid="C49">
<name>Premium Cinch</name>
<price>49.00</price>
<qty>1</qty>
</item>
</order>
<order>
<customerid limit="150">251222</customerid>
<status>pending</status>
<item instock="Y" itemid="WB78">
<name>Winter Blanket (78 inch)</name>
<price>20</price>
<qty>10</qty>
</item>
</order>

</orders>

  DOM 本质上是节点的集合。 由于一个文档中可能包含不同类型的信息,因此要定义不同类型的节点。
  

基本的节点类型:文档、原始、属性和文本

XML 中最常见的节点类型包括:

  • 元素:元素是 XML 的基本构造模块。通常,元素拥有子元素、文本节点,或两者的组合。元素节点也是能够拥有属性的唯一节点类型。
  • 属性:属性节点包含关于元素节点的信息,但是并不实际认为是元素的孩子,比如在下面的例子中: <customerid limit="1000">12341</customerid>
  • 文本:文本节点就是名副其实的文本。它可以由更多信息组成,也可以只包含空白。
  • 文档:文档节点是文档中其他所有节点的父亲。
-----------------------------------------
接下来是处理XML文档的说明了:

将文件解析为document

  为了使用 XML 文件中的信息,必须解析文件以创建一个 Document 对象

  Document 对象是一个接口,因而不能直接将它实例化;一般情况下,应用程序会相应使用一个工厂。准确的过程因实现而异,但是基本思想是相同的。(同样,Level 3 标准化了这个任务。)在这个例子 Java 环境中,解析文件是一个三步过程:

  1. 创建 DocumentBuilderFactory。 DocumentBuilderFactory 对象创建 DocumentBuilder。
  2. 创建 DocumentBuilder。 DocumentBuilder 执行实际的解析以创建 Document 对象。
  3. 解析文件以创建 Document 对象。

现在您可以开始构建应用程序了。

一个例子:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import org.w3c.dom.Document;

public class OrderProcessor {
public static void main (String args[]) {
File docFile = new File("orders.xml");
Document doc = null;
try {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(docFile);

} catch (Exception e) {
System.out.print("Problem parsing the file: "+e.getMessage());
}
}
}

  这里应用程序创建了 DocumentBuilderFactory,然后再使用它来创建 DocumentBuilder。 最后,DocumentBuilder 解析文件以创建 Document。

解析器设置:

  使用 DocumentBuilder 创建解析器的优点之一在于能够控制 DocumentBuilderFactory 创建的解析器上的各种设置。例如,可以设置解析器验证文档:


Java 的 DOM Level 2 实现允许通过以下方法控制解析器的参数:

  • setCoalescing():决定解析器是否要将 CDATA 节点转换为文本,以及是否要和周围的文本节点合并(如果适用的话)。其默认值为 false。
  • setExpandEntityReferences(): 确定是否要展开外部实体引用。如果为 true,外部数据将插入文档。其默认值为 true 。(请参阅参考资料以了解关于使用外部实体的技巧。)
  • setIgnoringComments():确定是否要忽略文件中的注释。其默认值为 false。
  • setIgnoringElementContentWhitespace():确定是否要忽略元素内容中的空白(类似于浏览器对待 HTML 的方式)。其默认值为 false。
  • setNamespaceAware():确定解析器是否要注意名称空间信息。其默认值为 false。
  • setValidating():默认情况下,解析器不验证文档。将这个参数设置为 true 可打开验证功能。

单步调试文档:

  获取根元素:

  一旦解析了文档并创建了一个 Document,应用程序就能单步调试该结构以审核、查找或显示信息。这种导航功能是将要在 Document 上执行的许多操作的基础。

 对文档的单步调试首先从根元素开始。格式良好的文档仅有一个根元素,也称为 DocumentElement。应用程序首先检索这个元素。



import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import org.w3c.dom.Document;

import org.w3c.dom.Element;


public class OrderProcessor {
...
System.exit(1);
}

//STEP 1: Get the root element

Element root = doc.getDocumentElement();
System.out.println("The root element is " + root.getNodeName());

 //STEP 2: Get the children
 NodeList children = root.getChildNodes();
 System.out.println("There are "+children.getLength()
+" nodes in this document.");


//STEP 3: Step through the children
for (Node child = root.getFirstChild();
child != null;
child = child.getNextSibling())
{
System.out.println(start.getNodeName()+" = "
+start.getNodeValue());
}


}
}

获取节点的子节点:

  一旦应用程序确定了根元素,它就把根元素的子节点的列表作为一个 NodeList 来检索。NodeList 类是一系列的项,应用程序将逐个迭代这些项。 在本例中,为简洁起见,应用程序通过仅显示有多少元素出现在结果 NodeList中,从而获取子节点和验证检索结果。

  注意这里的文档仅有两个元素,但是 NodeList 包含五个子节点,包括包含换行的三个文本节点 ―― 还要注意节点和元素在 DOM 中不是等价的。


使用 getFirstChild() 和 getNextSibling():

  父子和兄弟关系提供了迭代某个节点的所有孩子的替代方法,它在某些场合下可能更为适宜,比如在这些关系和孩子的出现顺序对理解数据至关重要的时候。

  在 Step 3 中,for 循环首先从根元素的第一个子节点开始。 应用程序迭代第一个孩子的所有兄弟,直至已全部对它们求值。

  每次应用程序执行该循环,它都要检索一个 Node 对象,输出其名称和值。注意 orders 的五个孩子包括 order 元素和三个文本节点。还要注意元素具有一个 null 值,而不是预期的文本。包含实际内容作为其值的,是作为元素的孩子的文本节点。

  还可以通过使用递归来获取所有节点.此外,需要注意的是,在dom中,属性不是任何节点的子节点。用w3c school提供的图示可以理解:

  当你实际使用dom扫描器去获取document的时候,你会发现它会将element之间的空白也作为一个节点包存在文档树中,本来,按照ibm这篇文章的说法是只要设置了setIgnoringElementContentWhitespace(true)就可以让扫描器无视掉这些空白,但是却没有提到,要让这个功能正常工作还必须要有定义过的dtd文件,同理,Node.normalized()方法也是一样的。关于文档标准化的内容,可以读读这一段:

-----------------------------------------

文档标准化

在 DOM Level 3 中定义的一个新方法是 Document 接口的 normalizeDocument 方法。从方法名可以看出,您可以使用这个方法来标准化文档。在默认情况下,这个方法完成以下工作:

  • 标准化 Text 节点,将相邻的 Text 节点整合为一个 Text 节点。
  • 根据 EntityReference 节点所引用的实体更新它们的内容。
  • 验证和修复文档中的命名空间信息,使命名空间格式是格式良好的。

一定要注意,在这个方法中使用的命名空间标准化算法(在 Appendix B 中定义)只对 namespace-aware 节点起作用,这些节点是使用具有“NS”后缀的方法(例如 createElementNS )创建的。 Namespace unaware 节点,即用 DOM Level 1 方法(如 createElement )创建的节点,与所有依赖于 XML 命名空间的处理不完全兼容。如果在文档中有 DOM Level 1 节点,那么在尝试进行命名空间标准化时, normalizeDocument 会失败并报告一个错误。一般来说,如果希望使用 XML 命名空间,并且要对文档执行任何需要 XML 命名空间支持的操作,那么就不应该用 DOM Level 1 方法创建节点。对于其他的操作,例如针对 XML Schema 重新验证内存中的文档,也是如此。

还可以通过 DOMConfiguration 配置 normalizeDocument ,以便对文档执行其他操作。例如,可以使用这个方法去掉注释,将 CDATASection 节点转换为 Text 节点,或者放弃树中所有命名空间声明属性。还可以通过它一次完成上述所有工作,从而轻松地使文档具有可以自然地映射到 XML Infoset 的形式。下面的程序片段展示了如何用 Document.config 控制 normalizeDocument 。

使用 Document.config 控制 normalizeDocument

// retrieve document configuration
DOMConfiguration config = document.getConfig();
// remove comments from
config.setParameter("comments", false);
// remove namespace declarations
config.setParameter("namespace-declarations", false);
// transform document
core.normalizeDocument();
// put document into a form closest to the XML Infoset
config.setParameter("infoset", true);
// transform document
core.normalizeDocument();

normalizeDocument 方法还允许您用 XML Schema 或者 DTD 对内存中的文档进行重新验证。过去,要在文档修改后对它进行重新验证,必须将它保存为一个文件,再用验证解析器读回去。有了这种新方法,现在就可以通过让 DOM 实现重新验证内存中的文档从而更有效地完成这项工作。为此,首先需要将 DOMConfiguration 的 validate 参数设置为 true 。然后需要实现一个 DOMErrorHandler 对象,验证错误将报告给这个对象,再用 error-handler 参数将这个对象注册到 Document 上。这与对 SAX 解析器所做的工作很类似。最后,可以通过调用 normalizeDocument 检查文档是否有效。

目前,还没有访问 XML Schema Post-Schema Validation Infoset(PSVI)的标准 API。不过,DOM Level 3 允许您获取一些 PSVI 信息。例如,如果希望获得 PSVI 标准化的模式值属性,那么就将 DOMConfiguration 上的 datatype-normalization 和 validate 参数设为“true”,并用经过 XML Schema 标准化的值调用 normalizeDocument 以更新树 ——这意味着文档中属性值和元素内容现在表示的是 PSVI 标准化的模式值属性。

-----------------------------------------

添加节点:

  一般要先构造一个element 并且要把它的值作为子节点append到这个element下。

删除节点:

  直接把要删除的节点remove就可以,子节点会跟着消除


最后就是写入XML文档了。

  如果用递归的方法去遍历文档树是个很不实用的方法。所以看这篇文章:http://www.ibm.com/developerworks/cn/java/l-javaxml/


  在JAXP中所提供的标准的更新原始XML文档的方法就是调用XSLT引擎,亦即使用TransformerFactory和Transformer类。请看下面的Java代码片断:

 //首先创建一个DOMSource对象,该构造函数的参数可以是一个Document对象doc代表更改后的DOM Tree。
DOMSource doms = new DOMSource (doc);

//创建一个File对象,代表DOM Tree所包含的数据的输出介质,这是一个XML文件。
File f = new File ("XMLOutput.xml");
//创建一个StreamResult对象,该构造函数的参数可以取为File对象。
StreamResult sr = new StreamResult (f);
//下面调用JAXP中的XSLT引擎来实现输出DOM Tree中的数据到XML文件中的功能。
//XSLT引擎的输入为DOMSource对象,输出为StreamResut对象。
try
{
//首先创建一个TransformerFactory对象,再由此创建Transformer对象。Transformer类相当于一个XSLT引擎。通常我们使用它来处理XSL文件,但是在这里我们使用它来输出XML文档。
TransformerFactory tf=TransformerFactory.newInstance();
Transformer t=tf.newTransformer ();
//关键的一步, 调用Transformer对象 (XSLT引擎)的transform()方法,该方法的第一个参数是DOMSource对象,第二个参数是StreamResult对象。
t.transform(doms,sr);
}
catch (TransformerConfigurationException tce)
{
System.out.println("Transformer Configuration Exception\n-----");
tce.printStackTrace();
}
catch (TransformerException te)
{
System.out.println ("Transformer Exception\n---------");
te.printStackTrace ();
}

  在实际的应用中,我们可以应用传统的DOM API从XML文档中获取DOM Tree,然后根据实际的需求对DOM Tree执行各种操作,得到最终的Document对象,接下来可以由此Document对象创建DOMSource对象,剩下的事情就是照搬上面的代码 了,程序运行完毕后, XMLOutput.xml就是你所需要的结果(当然了,你可以随意更改StreamResult类构造函数的参数,指定不同的输出介质,而不必是千篇一 律的XML文档)。

  这个方法最大的好处在于可以随心所欲的控制DOM Tree中的内容输出到输出介质中的格式,但是光靠TransformerFactory类和Transformer类并不能实现这个功能,还需要依赖OutputKeys类的帮助。 完整的例子请参考下列文件: AddRecord2.javauser.xml。该例子的运行环境为:Windows XP Professional、JDK 1.3.1。为了能够正常编译运行AddRecord2.java这个程序,你需要到网址 http://java.sun.com去下载安装JAXP 1.1或者Java XML Pack(Java XML Pack已经内含JAXP了)。

OutputKeys类

javax.xml.transform.OutputKeys类和java.util.Properties类配合使用,可以控制JAXP的XSLT引擎(Transformer类)输出XML文档的格式。请看下面的代码片断:

 //首先创建一个TransformerFactory对象,再由此创建Transformer对象。
tf=TransformerFactory.newInstance();
Transformer t=tf.newTransformer ();

//获取Transformser对象的输出属性,亦即XSLT引擎的缺省输出属性,这是一个java.util.Properties对象。
Properties properties = t.getOutputProperties();
//设置新的输出属性:输出字符编码为GB2312,这样可以支持中文字符,XSLT引擎所输出
//的XML文档如果包含了中文字符,可以正常显示,不会出现所谓的"汉字问题"。
//请留意OutputKeys类的字符串常数OutputKeys.ENCODING。
properties.setProperty(OutputKeys.ENCODING,"GB2312");
/更新XSLT引擎的输出属性。
t.setOutputProperties(properties);
//调用XSLT引擎,按照输出属性中的设置,输出DOM Tree中的内容到输出介质中。
t.transform(DOMSource_Object,StreamResult_Object);


从 上面的程序代码,我们不难看出,通过设置XSLT引擎(Transformer类)的输出属性,可以控制DOM Tree中的内容的输出格式,这对于我们定制输出内容是很有帮助的。那么JAXP的XSLT引擎(Transformer类)有那些输出属性可以设置呢? javax.xml.transform.OutputKeys类定义了很多字符串常数,它们都是可以自由设置的输出属性,常用的输出属性如下所示:

  1. public static final java.lang.String METHOD
    可以设为"xml"、"html"、"text"等值。
  2. public static final java.lang.String VERSION
    所遵循规范的版本号,如果METHOD设为"xml",那么它的值应该设为"1.0",如果METHOD设为"html",那么它的值应该设为"4.0",如果METHOD设为"text",那么这个输出属性会被忽略。
  3. public static final java.lang.String ENCODING
    设置输出时所采用的编码方式,比如"GB2312"、"UTF-8"等等,如果将其设置为"GB2312",可以解决所谓的"汉字问题"。
  4. public static final java.lang.String OMIT_XML_DECLARATION
    设置输出到XML文档中时是否忽略XML声明,亦即类似于:
    <?xml version="1.0" standalone="yes" encoding="utf-8" ?>
    这样的代码。它可选的值有"yes"、"no"。
  5. public static final java.lang.String INDENT
    IDENT设定XSLT引擎在输出XML文档时,是否自动添加额外的空格,它可选的值为"yes"、"no"。
  6. public static final java.lang.String MEDIA_TYPE
    MEDIA_TYPE设定输出文档的MIME类型。

如果设定XSLT引擎的输出属性呢?下面我们来总结一下:

首先是获取XSLT引擎(Transformer类)的缺省输出属性的集合,这需要使用Transformer类的getOutputProperties()方法,返回值是一个java.util.Properties对象。

Properties properties = transformer.getOutputProperties();
然后是设定新的输出属性,比如:
properties.setProperty(OutputKeys.ENCODING,"GB2312");
properties.setProperty(OutputKeys.METHOD,"html");
properties.setProperty(OutputKeys.VERSION,"4.0");
………………………………………………………

最后是更新XSLT引擎(Transformer类)的缺省输出属性的集合,这需要使用Transformer类的setOutputProperties()方法,参数是一个java.util.Properties对象。

我们编写了一个新的程序,其中应用了OutputKeys类,用以控制XSLT引擎的输出属性,该程序的架构和前一个程序(AddRecord3.java)大致相同,不过输出结果略有不同。完整的代码请参考下列文件: AddRecord3.java(见附件)、 user.xml(见附件)。该例子的运行环境为:Windows XP Professional、JDK 1.3.1。为了能够正常编译运行 AddRecord3.java这个程序,你需要到网址 http://java.sun.com去下载安装JAXP 1.1或者Java XML Pack(Java XML Pack内含JAXP了)。