Recent posts (max 20) - Browse or Archive for more

使用 nginx 实现后端 HTTP 服务器的故障转移(failover)

一般情况下,nginx 的 upstream 通常的用途是实现负载均衡:

但是实际上,nginx 对 upstream 的多个设备可以设置不同的状态参数:

  • weight 默认为1. weight越大,负载的权重就越大;
  • max_fails : 允许请求失败的次数默认为1. 当超过最大次数时,返回proxy_next_upstream 模块定义的错误;
  • fail_timeout: max_fails 次失败后,暂停的时间;
  • down 表示当前的server暂时不参与负载;
  • backup: 其它所有的非 backup 机器 down 或者忙的时候,请求 backup 机器(所以这台机器压力会最轻);

因此,upstream 同样可以用于配置 “故障转移”的服务器集群,一般用于这类场景:一般情况下优先使用服务器1,只有这台服务器出故障后才转移到其他的服务器(因为其他服务器性能、网络速度不如服务器1等原因),下面就是一个配置的例子:

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    default_type  application/octet-stream;
    client_max_body_size 4m;
    
    keepalive_timeout 65;
    upstream backend_servers {
        server localhost:7001 max_fails=3 fail_timeout=30s;
        # 下面两行 "backup" 只有在上面 "7001" 服务器失败后才会被访问(此时两台服务器会采用同样的权重进行负载均衡)
        server localhost:7002 max_fails=3 fail_timeout=30s backup;
        server localhost:7003 max_fails=3 fail_timeout=30s backup;
        # 如果为上面两行 "backup" 设定悬殊的 "weight" 参数,可以控制 failover 的顺序(比如设置只有 "7002" 宕机后才会访问到 "7003")
        #server localhost:7002 max_fails=3 fail_timeout=30s backup weight=2000000000;
        #server localhost:7003 max_fails=3 fail_timeout=30s backup weight=1;
    }

    server {
        listen 8001;
        server_name example.com;

        location / {
            proxy_pass http://backend_servers;
        }
    }
}

附件 test-http-server.pynginx.conf 是用于测试的 Python HTTP 服务器和 nginx 配置:

  • 首先依次通过如下命令启动 3 台后端 HTTP 服务器:
    python test-http-server.py 7001
    python test-http-server.py 7002
    python test-http-server.py 7003
    
  • 然后使用如下命令启动 nginx 服务器:
    # 启动 nginx
    nginx -c `pwd`/nginx.conf
    # 停止 nginx
    nginx -c `pwd`/nginx.conf -s quit
    
  • 通过浏览器访问类似这样的 URL: http://localhost:8001/TEST/001
    • 正常结果应该是 localhost 7001;
    • 如果停止相应的服务器,可以看到浏览器上的输出切换到后续其他服务器(输出 localhost 7002localhost 7003)

历史 - 无意中访问到 jQuery 的 trac

看起来 jQuery 历史上使用过 trac 来进行开发管理,具体地址访问这儿: https://bugs.jquery.com/ , 我是通过 stackoverflow这个问题 不小心进入的.

看起来,这个 trac 两年前就不再使用了.



纪念 Codehaus - ... and eventually GitHub has overtaken all of them

在这个 Github 如火如荼的年代,sf.net 已经式微,而 Google CodeCodehaus 已经不再继续了。


这里有篇文章:《Codehaus: The once great house of code has fallen》:

  • Codehaus was overtaken by SourceForge and Google Code and eventually GitHub has overtaken all of them.

然而有价值的项目仍然在延续,挑几个比较熟悉的:

当然也有不怎么被使用的项目:

看起来,Codehaus 上原来的所有项目都已经转移到了 Github,如果需要去寻找历史的话,可以访问 https://github.com/codehaus

将自己的域名绑定到 GitHub Pages

以近期实现的一个站点 http://bizobj.org/ 为例,记录一下如何实现将自己的域名绑定到 Github 的 Pages

首先,在你的 github 中建立一个 域名.github.io 的库

建立过程中,可以使用系统的自动创建 Page 的功能

可以直接在网页上使用 markdown 语法编辑页面内容

然后可以选择页面风格

为了可以直接使用你自己的域名访问,需要编写一个 CNAME 文件,内容就是域名

然后去设置你的 DSN 指向 github 吧

具体 DSN 应该指向哪个 IP,github 提供了相关的帮助说明

We are back ! - 现在 thinkbase.net 运行在 Docker 之上

PortableTrac 已经支持 Linux(从 https://github.com/thinkbase/PortableTrac/commit/bc2702cc9ed56f566a43b9d00137ae81dafdb395 开始),于是 http://thinkbase.net 终于迁移到 Linux 上,并且使用 Docker 来进行运行环境。

基于 Docker 容器技术,要运行 thinkbase.net 的 Trac 系统只需要如下简单的几步即可:

  1. 在服务器上安装 docker:
    # 安装 docker.io
    sudo apt-get install docker.io
    
    # 将当前用户加入 docker 组,以便非 root 用户也可以使用 docker
    sudo gpasswd -a ${USER} docker
    sudo service docker restart
    
  1. 从 github 获取相关的 Dockerfile,构建运行镜像
    # [运行目录: ~/github$] - 获取需要的 Dockerfile 定义
    git clone -v --progress https://github.com/thinkbase/Dockerfiles
    
    # [运行目录: ~/github/Dockerfiles/01-init/01-ubuntu14.04$] - 构建基于 ubuntu 的基础镜像
    docker build --force-rm -t ubuntu:14.04-sshd .
    
    # [运行目录: ~/github/Dockerfiles/90-servers/trac-thinkbase.net$] - 构建 thinkbase.net trac 镜像
    docker build --force-rm -t trac-thinkbase.net:1.0 .
    
  1. 下面就可以创建容器并运行了:
    docker run -d -p 80:8080 -v ~/trac/private:/private --name trac-tk trac-thinkbase.net:1.0
    

Linux shell 之计算圆周率

http://blog.devep.net/virushuo/2014/01/21/delllinux_kernel_bugcpu.html (Dell服务器和Linux kernel bug造成的CPU性能下降问题) 看到的,本意是通过计算圆周率确认服务器的CPU性能,只需要如下简单的一行命令:

time echo "scale=5000; 4*a(1)" | bc -l -q

在我的笔记本上执行的情况如下:

3.141592653589793238462643383279502884197169399375105820974944592307\
81640628620899862803482534211706798214808651328230664709384460955058\
22317253594081284811174502841027019385211055596446229489549303819644\
28810975665933446128475648233786783165271201909145648566923460348610\
45432664821339360726024914127372458700660631558817488152092096282925\
40917153643678925903600113305305488204665213841469519415116094330572\
70365759591953092186117381932611793105118548074462379962749567351885\
75272489122793818301194912983367336244065664308602139494639522473719\
07021798609437027705392171762931767523846748184676694051320005681271\
45263560827785771342757789609173637178721468440901224953430146549585\
37105079227968925892354201995611212902196086403441815981362977477130\
99605187072113499999983729780499510597317328160963185950244594553469\
08302642522308253344685035261931188171010003137838752886587533208381\
42061717766914730359825349042875546873115956286388235378759375195778\
18577805321712268066130019278766111959092164201989380952572010654858\
63278865936153381827968230301952035301852968995773622599413891249721\
77528347913151557485724245415069595082953311686172785588907509838175\
46374649393192550604009277016711390098488240128583616035637076601047\
10181942955596198946767837449448255379774726847104047534646208046684\
25906949129331367702898915210475216205696602405803815019351125338243\
00355876402474964732639141992726042699227967823547816360093417216412\
19924586315030286182974555706749838505494588586926995690927210797509\
30295532116534498720275596023648066549911988183479775356636980742654\
25278625518184175746728909777727938000816470600161452491921732172147\
72350141441973568548161361157352552133475741849468438523323907394143\
33454776241686251898356948556209921922218427255025425688767179049460\
16534668049886272327917860857843838279679766814541009538837863609506\
80064225125205117392984896084128488626945604241965285022210661186306\
74427862203919494504712371378696095636437191728746776465757396241389\
08658326459958133904780275900994657640789512694683983525957098258226\
20522489407726719478268482601476990902640136394437455305068203496252\
45174939965143142980919065925093722169646151570985838741059788595977\
29754989301617539284681382686838689427741559918559252459539594310499\
72524680845987273644695848653836736222626099124608051243884390451244\
13654976278079771569143599770012961608944169486855584840635342207222\
58284886481584560285060168427394522674676788952521385225499546667278\
23986456596116354886230577456498035593634568174324112515076069479451\
09659609402522887971089314566913686722874894056010150330861792868092\
08747609178249385890097149096759852613655497818931297848216829989487\
22658804857564014270477555132379641451523746234364542858444795265867\
82105114135473573952311342716610213596953623144295248493718711014576\
54035902799344037420073105785390621983874478084784896833214457138687\
51943506430218453191048481005370614680674919278191197939952061419663\
42875444064374512371819217999839101591956181467514269123974894090718\
64942319615679452080951465502252316038819301420937621378559566389377\
87083039069792077346722182562599661501421503068038447734549202605414\
66592520149744285073251866600213243408819071048633173464965145390579\
62685610055081066587969981635747363840525714591028970641401109712062\
80439039759515677157700420337869936007230558763176359421873125147120\
53292819182618612586732157919841484882916447060957527069572209175671\
16722910981690915280173506712748583222871835209353965725121083579151\
36988209144421006751033467110314126711136990865851639831501970165151\
16851714376576183515565088490998985998238734552833163550764791853589\
32261854896321329330898570642046752590709154814165498594616371802709\
81994309924488957571282890592323326097299712084433573265489382391193\
25974636673058360414281388303203824903758985243744170291327656180937\
73444030707469211201913020330380197621101100449293215160842444859637\
66983895228684783123552658213144957685726243344189303968642624341077\
32269780280731891544110104468232527162010526522721116603966655730925\
47110557853763466820653109896526918620564769312570586356620185581007\
29360659876486117910453348850346113657686753249441668039626579787718\
55608455296541266540853061434443185867697514566140680070023787765913\
44017127494704205622305389945613140711270004078547332699390814546646\
45880797270826683063432858785698305235808933065757406795457163775254\
20211495576158140025012622859413021647155097925923099079654737612551\
76567513575178296664547791745011299614890304639947132962107340437518\
95735961458901938971311179042978285647503203198691514028708085990480\
10941214722131794764777262241425485454033215718530614228813758504306\
33217518297986622371721591607716692547487389866549494501146540628433\
66393790039769265672146385306736096571209180763832716641627488880078\
69256029022847210403172118608204190004229661711963779213375751149595\
01566049631862947265473642523081770367515906735023507283540567040386\
74351362222477158915049530984448933309634087807693259939780541934144\
73774418426312986080998886874132604720

real	0m21.420s
user	0m21.397s
sys	0m0.001s

Linux shell 获得当前脚本文件所在的目录

这基本上是一个非常常见的需求,不过考虑到兼容性: 1)目录或者文件名中空格;2)对通过link的形式引用;3)通过. ./test.sh这样的方式调用,要做出一个比较完美的方案也不是那么容易。

首先看看主流的做法:

另外一个来源:

  • http://www.cyberciti.biz/faq/unix-linux-appleosx-bsd-bash-script-find-what-directory-itsstoredin/
    #!/bin/bash
    # Name: /tmp/demo.bash : 
    # Purpose: Tell in what directory $0 is stored in
    # Warning: Not tested for portability 
    # ------------------------------------------------
     
    ## who am i? ##
    _script="$(readlink -f ${BASH_SOURCE[0]})"
     
    ## Delete last component from $_script ##
    _base="$(dirname $_script)"
     
    ## Okay, print it ##
    echo "Script name : $_script"
    echo "Current working dir : $PWD"
    echo "Script location path (dir) : $_base"
    
  • 这个脚本只支持 bash,经测试,对空格的支持存在问题。

考虑实际情况(总是在 bash 下面),对上面脚本修改一下,我最终使用的方案是:

#!/bin/bash

if [ -z $BASH ]; then
    echo "This shell script MUST run under bash."
    exit /b -1
fi
_script="$(readlink -f "${BASH_SOURCE[0]}")"
_script_dir="$(dirname "$_script")"
echo "Directory portion of $_script : $_script_dir"

Linux 卡死怎么办

摘自 http://forum.ubuntu.org.cn/viewtopic.php?f=15&t=450926

  • Ctrl-Alt-F1 切换到 TTY1 ;
    • 如果你用的是常规版ubuntu,setsid unity重启下unity基本就解决了
  • Ctrl-Alt-Backspace 关闭 X Server;
  • Alt-SysRq + R、E、I、S、U、B 无敌 reisub
    R:unRaw     将键盘从X Server抢回来
    E:tErminate 给除INIT外的所有进程发送 SIGTERM 信号
    I:kIll      给除INIT外的所有进程发送 SIGKILL 信号
    S:Sync      将所有数据同步至磁盘
    U:Unmount   将所有分区挂载为只读模式
    R:reBoot    重启
    O:shutdOwn  关机
    

Oracle 数据库导入时 ORA-12899 错误的处理

原因分析和 http://wuhuizhong.iteye.com/blog/740553 一样:

一般设计数据库的时候,我们都考虑一个汉字占用两个字节。所以设计数据库的时候,如果认为某字段最长要存四个汉字,该字段都会定义为varchar2(8)。

SQL> create table t1 (col1 varchar2(8));
Table created.

但是测试插入三个汉字的时候就报错了。

SQL> insert into t1 values('一二三');
insert into t1 values('一二三')
*
ERROR at line 1:
ORA-12899: value too large for column T1.COL1 (actual: 9, maximum:8)

检查相关参数及环境变量。

SQL> select * from nls_database_parameters where parameter like 'NLS%CHARACTERSET';
PARAMETER                 VALUE
------------------------- --------------------
NLS_CHARACTERSET          UTF8
NLS_NCHAR_CHARACTERSET    UTF8

原来数据库使用的是UTF8字符集,难怪一个汉字占用3个字节。这样一来原先按一个汉字占两个字节设计的数据库,应用的时候很多字段都会因长度不够,出现ORA-12899错误。

解决办法比较直接,就是直接修改 dump 文件中数据库表定义的文本,把 CHAR(n) 改为 NCHAR(n), VARCHAR2(n) 改为 NVARCHAR2(n):

sed 's/ CHAR(/ NCHAR(/g'  export.dmp > export1.dmp
sed 's/ VARCHAR2(/ NVARCHAR2(/g'  export1.dmp > export2.dmp
# 在 Window 32 位平台上有 NVARCHAR2 不能大于 2000 的限制
sed 's/ NVARCHAR2(3000)/ NVARCHAR2(2000)/g'  export2.dmp > export3.dmp
sed 's/ NVARCHAR2(4000)/ NVARCHAR2(2000)/g'  export3.dmp > export4.dmp
sed 's/ NVARCHAR2(2200)/ NVARCHAR2(2000)/g'  export4.dmp > export5.dmp

PortableTrac 类似的系统

通过对比选择最佳开源的基于Web的项目管理工具 看到:

Trac 目前还无法同时管理多个项目,但如果作为一个单一项目的管理工具,它是一个很好的选择。大家可以使用 BitNamiTurnkey virtual appliance

发现这两个项目和现在 PortableTrac 的目标比较接近:

  • http://bitnami.com/stack/trac
    • 提供了一键安装的 Trac 安装程序和虚拟机映像,甚至还包括在上部署 Trac 服务器。
    • 提供了 Windows、Linux 和 OS X 上的安装程序;
  • http://www.turnkeylinux.org/trac
    • TurnKey Linux是基于Ubuntu的虚拟应用程序库,它将一些最好的开放源码软件集成到完备可用的解决方案中。每一个虚拟应用程序都为易用性进行了优化,并能 在数分钟内就部署在裸机、虚拟机及云中,每一个虚拟应用都可以光盘镜像或是虚拟机镜像的形式获得(来自 http://baike.baidu.com/view/9300243.htm )。

Portable Trac 简单介绍 - 兼谈为什么不选择 Redmine

Trac是一个轻量级的软件项目管理环境,如果在工作中涉及一个开发团队的管理并且关心项目管理工具的话,相信都在 TracRedmine 等工具之间进行过比较,网上简单搜索一下就可以查到以下有代表性的文章:

一般来说这些比较都认为 Redmine 更为优秀,尤其是

  • 可以同时管理多个项目;
  • 安装和部署比较方便(利用rake、rails的db migration安装很方便,Trac则要用到命令行的trac-admin进行配置,以及每个项目有单独的ini配置文件);
  • 使用比较简单(trac的很多功能都需要通过trac-admin在命令行方式下进行配置,不易上手,这方面Redmine则十分方便);

总体来看,缺乏内置的"多项目管理"功能支持算是 Trac 的硬伤,其他方面:

  • 对于开发者而言,命令行不是特别严重的问题,而且 Trac 的插件机制比较完善,http://trac-hacks.org/ 上有大量的插件可以实现基于 Web 界面的管理需求(例如: AccountManagerPlugin实现了内置的用户管理、TracIniAdminPanelPlugin支持通过Web方式调整Trac配置、SvnAuthzAdminPlugin支持Web方式调整SVN权限等等), Python 语言也算比较大众化,自己写一个插件也没有太大压力;
  • Trac 将维基融入了核心组件, 支持的更加彻底,而且其 Wiki 语法 功能强大并且可以通过插件扩展,这对于需要撰写大量文档的情况有不少帮助;而 Redmine 的 Wiki 功能实在太弱,实际使用的时候很不方便;事实上功能丰富的 Wiki 是 Trac 最大的优势;

本文介绍的 PortableTrac 其实是针对官方 Trac 的重新打包发布(目前只支持 Windows),其目的主要是简化 Trac 的安装配置过程,方便用户的使用,这方面的改进和增强包括:

  • 内置中文说明;
  • 自带的Python环境,已安装大量的常用插件,实现开箱即用,不再需要繁琐的安装和配置过程;
  • 已实现与 Apache httpd、SVN、Git 的集成;
  • 提供标准的数据备份和恢复功能;
  • 更详细的介绍可以参见 PortableTrac

下图演示如何简单的在几分钟之内解压并运行一个 Trac 实例(参考 PortableTrac/install),尤其适合初学者:
.

如果需要了解更多信息,请参考 在线演示站点.

动态写入内容到 iFrame 并执行其中脚本

How to - Injecting HTML into an IFrame

其实实现思路比较简单,与动态打开一个窗口并使用 document.write(...) 写入内容的做法类似,IFrame 内部包含的 window 对象也可以使用同样的方法写入 HTML 内容,并且可以执行其中的脚本。

主要参考

  • stackoverflow "Creating an iframe with given HTML dynamically"

  • Dojo 的 Reference Guide 采用类似做法来运行示例代码
    • 经过分析可以看到 Dojo Reference Guide 是采用一个 src=javascript:[页面内容] 的方式来打开 iframe,例如:
      javascript:%20'<!DOCTYPE%20html>\n<html%20>\n<head>\n\n<link%20rel="stylesheet"%20href="../_static/js/dojo/../dijit/themes/claro/claro.css">\n\n<script>dojoConfig%20=%20{async:%20true}</script><script%20src=\'../_static/js/dojo/dojo.js\'></script><script>require([\n%20%20%20%20"dojo/ready",%20"dojo/_base/window",%20"dojo/store/Memory",\n%20%20%20%20"dijit/tree/ObjectStoreModel",%20"dijit/Tree"\n],%20function(ready,%20win,%20Memory,%20ObjectStoreModel,%20Tree){\n\n%20%20%20%20//%20Create%20test%20store,%20adding%20the%20getChildren()%20method%20required%20by%20ObjectStoreModel\n%20%20%20%20var%20myStore%20=%20new%20Memory({\n%20%20%20%20%20%20%20%20data:%20[\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'world\',%20name:\'The%20earth\',%20type:\'planet\',%20population:%20\'6%20billion\'},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'AF\',%20name:\'Africa\',%20type:\'continent\',%20population:\'900%20million\',%20area:%20\'30,221,532%20sq%20km\',\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20timezone:%20\'-1%20UTC%20to%20+4%20UTC\',%20parent:%20\'world\'},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'EG\',%20name:\'Egypt\',%20type:\'country\',%20parent:%20\'AF\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'KE\',%20name:\'Kenya\',%20type:\'country\',%20parent:%20\'AF\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'Nairobi\',%20name:\'Nairobi\',%20type:\'city\',%20parent:%20\'KE\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'Mombasa\',%20name:\'Mombasa\',%20type:\'city\',%20parent:%20\'KE\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'SD\',%20name:\'Sudan\',%20type:\'country\',%20parent:%20\'AF\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'Khartoum\',%20name:\'Khartoum\',%20type:\'city\',%20parent:%20\'SD\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'AS\',%20name:\'Asia\',%20type:\'continent\',%20parent:%20\'world\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'CN\',%20name:\'China\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'IN\',%20name:\'India\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'RU\',%20name:\'Russia\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'MN\',%20name:\'Mongolia\',%20type:\'country\',%20parent:%20\'AS\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'OC\',%20name:\'Oceania\',%20type:\'continent\',%20population:\'21%20million\',%20parent:%20\'world\'},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'EU\',%20name:\'Europe\',%20type:\'continent\',%20parent:%20\'world\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'DE\',%20name:\'Germany\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'FR\',%20name:\'France\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'ES\',%20name:\'Spain\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'IT\',%20name:\'Italy\',%20type:\'country\',%20parent:%20\'EU\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'NA\',%20name:\'North%20America\',%20type:\'continent\',%20parent:%20\'world\'%20},\n%20%20%20%20%20%20%20%20%20%20%20%20{%20id:%20\'SA\',%20name:\'South%20America\',%20type:\'continent\',%20parent:%20\'world\'%20}\n%20%20%20%20%20%20%20%20],\n%20%20%20%20%20%20%20%20getChildren:%20function(object){\n%20%20%20%20%20%20%20%20%20%20%20%20return%20this.query({parent:%20object.id});\n%20%20%20%20%20%20%20%20}\n%20%20%20%20});\n\n%20%20%20%20//%20Create%20the%20model\n%20%20%20%20var%20myModel%20=%20new%20ObjectStoreModel({\n%20%20%20%20%20%20%20%20store:%20myStore,\n%20%20%20%20%20%20%20%20query:%20{id:%20\'world\'}\n%20%20%20%20});\n\n%20%20%20%20//%20Create%20the%20Tree.%20%20%20Note%20that%20all%20widget%20creation%20should%20be%20inside%20a%20dojo.ready().\n%20%20%20%20ready(function(){\n%20%20%20%20%20%20%20%20var%20tree%20=%20new%20Tree({\n%20%20%20%20%20%20%20%20%20%20%20%20model:%20myModel\n%20%20%20%20%20%20%20%20});\n%20%20%20%20%20%20%20%20tree.placeAt(win.body());\n%20%20%20%20%20%20%20%20tree.startup();\n%20%20%20%20});\n});</script>\n</head>\n<body%20class="claro">\n%20%20%20%20\n</body>\n</html>'
      

实现实例

参考 fill-iframe-with-string.html, 要点解释如下:

  1. 基本流程就是:
    • IFrame 内部 document.open();
    • IFrame 内部 document.write(...);
    • IFrame 内部 document.close();
  2. 示例中实现了向 IFrame 内部窗口传递参数的功能。注意在 IE 中,document.open() 会清除 IFrame 内部 window 对象上的属性,因此参数的设置需要在 document.open() 之后进行;
  3. 关于脚本的执行:
    • 按照一般的习惯,在页面头部会使用诸如 <script src="./jquery-ui-1.10.3/jquery-1.9.1.js"></script> 这样的写法引入外部 js,然后在 HTML 元素之后使用嵌入的 javascript 脚本执行与页面元素相关的操作;
    • 在 Firefox 和 Chrome 中,<script> 的执行是按照在页面中的先后顺序进行的, 因此处于文档后部的嵌入 javascript 脚本可以使用外部 js 中定义的变量(比如:jQuery 的 "$");
    • 但是在 IE 中情况正好相反,在执行嵌入的 javascript 脚本时,引入的外部 js 还没有执行,这样就造成嵌入 javascript 脚本出现 "找不到 $ 对象" 之类的错误;
    • 解决方法是在 IFrame 内部页面上定义 bodyonload 事件,在这个事件中执行页面中的嵌入 javascript 脚本。

实际效果见 在线演示

讨论:不同浏览器的差异

总的来说 IE9、Firefox 和 Chrome 的差别很小,倒是 IE8 与其他浏览器存在不小的差别,主要包括:

  • 安全模型:上面提到的示例 html 文件无法通过 IE8 直接在本地打开(如下图),但是其他浏览器可以正常打开,而发布到 Web 服务器上之后,IE8 也可以正常打开;

    另外如果 IFrame 中引用的是来自网络的 js 外部文件,IE8 可以正常运行(见这个例子:fill-iframe-with-string-online-test.html);
  • IFrame 相关对象, 以及页面高度检测:下面对不同浏览器中 iframe.documentWindowiframe.contentWindowiframe.Documentiframe.contentDocument.body.offsetHeightiframe.Document.body.scrollHeight进行了比较,可以看出来,IE8 与其他几个浏览器的差异比较明显:
    • IE8, WindowsXP:
    • IE9, Windows 7:
    • Firefox19, Windows 7:
    • Firefox20, Ubuntu:
    • Google Chrome 6, Windows 7:
    • Chromium25, Ubuntu:

END

PortableTrac and thinkbase.net - upgrade to Python 2.7.4

Original plan was upgrade to Portable Python 2.7.3.2, unfortunately this version is incompatible to mod_wsgi(See below error screenshot), the error message in stdout is :

>>> call "I:\thinkbase.net\github\PortableTrac\httpd\Apache2.2\bin\httpd.exe"
httpd.exe: Syntax error on line 504 of I:/thinkbase.net/github/PortableTrac/httpd/Apache2.2/conf/httpd.conf: Cannot load B:/Apache2.2/modules/mod_wsgi-win32-ap22py27-3.3.so into server: \xb6\xaf\xcc\xac\xc1\xb4\xbd\xd3\xbf\xe2(DLL)\xb3\xf5\xca\xbc\xbb\xaf\xc0\xfd\xb3\xcc\xca\xa7\xb0\xdc\xa1\xa3

The reason is the version of the Microsoft C/C++ compiler(http://code.google.com/p/modwsgi/wiki/InstallationOnWindows#Compiling_From_Source_Code To compile from source code you will need Microsoft C/C++ compiler for Windows. This must be the same version of the compiler as used to build the version of Python being used.).

So finally we chose the official python distribution(Python 2.7.4) and embeded the python runtime into PortableTrac, this is the construction of embeded python runtime, following is the way to embed official python:

  1. Download Python 2.7.4 Windows Installer''(python-2.7.4.msi)'', and install it to C:\Python27(Or else other folder you like);
  1. Download setuptools 0.6c11''(setuptools-0.6c11.win32-py2.7.exe)'' and install it.
  1. Copy files and folders in C:\Python27 into PortableTrac's PortablePython\App folder;
    • Before copy, delete *.pyc and *.pyo files;
  1. Copy %windir%\system32\python27.dll into PortableTrac's PortablePython\App folder;
  1. To make it work on Windows XP, copy following files into PortablePython\App folder:
    • VC90.CRT:
      c:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375\msvcm90.dll
      c:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375\msvcp90.dll
      c:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375\msvcr90.dll
      
    • The manifest(PortablePython\App\Microsoft.VC90.CRT.manifest), Copy from:
      c:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest
      
  1. That's all, we commit the whole python runtime into PortableTrac Repository.

The benefit of embeding python runtime is the easy-trac-deploy: Just download from Github and uncompress it, python runtime is there ready.

Please follow the installation guide, download, then play it, best regards!

在 Basic 认证下多个 Trac 服务器之间的单点登陆

由于 Trac 目前只能支持单个项目,所以,很多时候需要在多个 Trac 服务器间进行链接等的互相关联,Trac 对这种需求的回答是 InterTrac(中文) ,不过 InterTrac 并不处理多个 Trac 服务器之间的统一登陆问题;

原理

在使用 Apache httpd 的Basic 认证模式下,如果多个 Trac 服务器使用相同的认证来源(比如使用相同的LDAP服务器,或者相同的passwd文件),那么就可以比较方便的实现多个 Trac 服务器间的单点登陆,要点包括:

  1. 一般情况下与 Trac 服务器集成的 httpd 会设置对 .../login 地址的用户认证,如果在当前服务器中设置一个指向其他服务器的反向代理,并且对反向代理后的 url 设置一个当前服务器的 LocationMatch 认证控制,那么:
    1. 如果用户已经登陆当前 Trac,那么访问反向代理后的 url 就可以自动获得已有的 Basic 认证信息(因为二者是一个服务器地址);
    2. 在已经具有 Basic 认证信息的情况下,通过反向代理访问到真正的服务器时,Basic 认证信息同样可以传递,从而可以直接登陆到外部服务器,不需要用户再次输入用户名/密码;
  2. 借用 Trac 处理 .../login 时对 referer HTTP 参数的 redirect_back 特性,如果在访问 .../login 时使用参数 ?referer=....,那么完成 Basic 认证后,可以重定向到需要显示的页面;
  3. 通过在 trac.ini 中设置类似 prj1.url = /trac/prj1/login?referer=/trac/prj1InterTrac URL,可以保证使用 InterTrac 语法编写的 TracLinks 通过 .../login URL 实现自动登陆然后跳转到具体页面,从而完成单点登录;

具体实现举例

  • httpd.conf
    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_http_module modules/mod_proxy_http.so
    ProxyRequests Off
    # ================================================= 
    # Proxy to http://another-server/trac/prj1
    # =================================================
    <proxy http://another-server/trac/prj1>
        AllowOverride None
        Order Deny,Allow
        Allow from all
    </proxy>
    ProxyPass /trac/prj1 http://another-server/trac/prj1
    ProxyPassReverse /trac/prj1 http://another-server/trac/prj1
    # ================================================= 
    # Proxy to http://another-server2/trac/prj2
    # =================================================
    <proxy http://another-server2/trac/prj2>
        AllowOverride None
        Order Deny,Allow
        Allow from all
    </proxy>
    ProxyPass /trac/prj2 http://another-server2/trac/prj2
    ProxyPassReverse /trac/prj2 http://another-server2/trac/prj2
    
    # 这里定义的 AuthType 必需是 Basic, 其他的配置项与当前 Trac 要保持一致,即这几个 Trac 需要使用同一个用户认证机制和认证来源
    # 具体需要几个 LocationMatch,取决于反向代理配置是的 URL 设置情况,这里因为反向代理的两台服务器都被定义在 /trac 下,所以可以使用一个 LocationMatch 来进行用户认证
    <LocationMatch "/trac/[^/]+/login">
        AuthType Basic
        AuthBasicProvider "ldap"
        AuthLDAPUrl "ldap://domain-server/DC=thinkbase,DC=net?sAMAccountName?sub?(objectClass=*)"
        AuthLDAPBindDN "CN=trac-ldap-user,OU=ServerAccountsGroup,DC=thinkbase,DC=net"
        AuthLDAPBindPassword "**********"
    
        AuthName "Trac 1.0.1"
    
        Require valid-user
    </LocationMatch>
    
  • trac.ini
    [intertrac]
    P1 = prj1
    prj1.title = Project 1 Trac
    prj1.url = /trac/prj1/login?referer=/trac/prj1
    P2 = prj2
    prj2.title = Project 2 Trac
    prj2.url = /trac/prj2/login?referer=/trac/prj2
    

另外一种方式

如果两个 Trac 服务器的认证来源不一致(比如一个使用 LDAP 认证,一个使用 passwd 文件认证,用户名和密码都不相同),是否可以实现类似的效果呢?

答案是可以的,不过需要有一个前提,就是使用固定的用户/密码访问另外那个 Trac 服务器,具体来说,在 httpd.conf 中可以省去 <LocationMatch 定义认证方式的部分,而是在 <proxy 中通过 HTTP Header Authorization 写死 Basic 认证的用户名和密码:

  • httpd.conf
    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_http_module modules/mod_proxy_http.so
    LoadModule headers_module modules/mod_headers.so
    ProxyRequests Off
    # ================================================= 
    # Proxy to http://another-server/trac/prj1
    # =================================================
    <proxy http://another-server/trac/prj1>
        AllowOverride None
        Order Deny,Allow
        Allow from all
        # 这里固定设置反向代理转发时的 Basic 认证用户名和密码, dXNlcjE6cGFzc3dkMQ= 是命令行 'echo -n user1:passwd1 | base64' 的运行结果
        RequestHeader set Authorization "Basic dXNlcjE6cGFzc3dkMQ="
    </proxy>
    ProxyPass /trac/prj1 http://another-server/trac/prj1
    ProxyPassReverse /trac/prj1 http://another-server/trac/prj1
    

演示

就 thinkbase.net 站点的两个 trac 环境作了一个示例。当然,因为这两个 trac 环境是部署在同一个 apache httpd 上的,所以不需要在 httpd.conf 中增加配置。

参考

END

nginx 反向代理功能介绍及在Windows下使用的Portable版本

想做一个公网访问内网的反向代理,但是在公网机器上的 Apache 怎么也安装不上(那个机器已经安装了两个 Apache,不知道是不是这方面的原因),于是就研究了一下如何使用 nginx 来实现反向代理。

搜索了一下资料,发现 nginx 似乎比 Apache 更好用,包括:

  • 配置的方式类似 json,比较容易阅读;
  • 运行简单,Windows 平台上的二进制文件本身就是解压即可运行,够 “Portable”;
  • 在网上找到了使用 Windows Service Wrapper 将 nginx 转为一个 Windows 服务的方法,经试验简单有效,见 Nginx Windows Service

nginx 自然在其 官网下载,本文使用的是 Stable version, nginx/Windows-1.2.7;

winsw(Windows Service Wrapper)原来 host 在 https://kenai.com/,不过Oracle 收购 Sun 之后这个网站似乎受到一些影响,目前已经转到了 github 上: https://github.com/kohsuke/winsw/ (是用 C# 编写的,呵呵), 其二进制文件可以在 Nginx Windows Service 文中提到的地点 http://download.java.net/maven/2/com/sun/winsw/winsw/ 下载(似乎是一个 maven 的仓库?);

本文附件中的 “PortableNgnix” 只是简单的将解压后的 nginx Windows 程序和 winsw 合并在一起而已,其中包含一个简单的 proxy 写法示例(将 http://localhost/main/browser/AdminShells/ 反向代理连接到 http://thinkbase.net/main/browser/AdminShells/ ):

#user  nobody;
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location /main/browser/AdminShells/ {
                proxy_pass              http://thinkbase.net/main/browser/AdminShells/;
                proxy_redirect          off;
                proxy_set_header        X-Real-IP       $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

其实很多时候还有更简单的写法,比如下面这个反向代理 svn 服务器的例子:

#user  nobody;
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;

    keepalive_timeout  65;

    server {
        listen       81;
        server_name  localhost;

        location / {
                proxy_pass              http://192.168.0.77:8080/;
                proxy_redirect          off;
                proxy_set_header        X-Real-IP       $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

使用 winsw 创建 Windows Service 的过程也很简单,附件 PortableNginx.7z 中的例子是假设 nginx 解压后在 D:\PortableNginx 目录下的情况:

<service>
    <id>nginx-reverse-proxy</id>
    <name>nginx reverse proxy</name>
    <description>nginx reverse proxy</description>
    <executable>D:\PortableNginx\nginx\nginx.exe</executable>
    <logpath>D:\PortableNginx\nginx\logs</logpath>
    <logmode>roll</logmode>
    <depend></depend>
    <startargument>-p D:\PortableNginx\nginx -c D:\PortableNginx\nginx\proxy.conf</startargument>
    <stopargument>-p D:\PortableNginx\nginx -s stop</stopargument>
</service>

更多细节可以继续研究附件(参见其中的 readme.txt),或者把附件解压后自己体验,下面是 winsw 产生的 Windows Servicce 以及 Service 运行时产生的相关文件截图,供参考: .

Windows: 定义以较短时间间隔重复运行的计划任务

增强Windows计划任务执行用户的安全性 里面提到了如何增强计划任务的安全性, 其实计划任务中存在很多灵活应用的地方, 比如如何让一个任务以较短的时间间隔重复运行, 比如一个每小时执行一次的任务, 呆板地为每次运行设置一个日程安排条目(那么就有12个日程安排, 如果是每隔5分钟运行一次呢?!!!)显然不是最好的方案.

附图是在 Windows Server 2008(Win7、Win8 的计划任务设置界面也是这样的)中设置每5分钟执行一次的任务时的触发器定义, 供参考.

附加说明:截图中的"1430分钟"是这样计算的:1430/60 = 23.83 小时 = 23 小时 50 分钟.

在 Trac 中集成 Git 版本库

按照 [TracGit] 的说明, 把现有的几个 Github 上的 Git Repositories( https://github.com/thinkbase )集成到 thinkbase.net.

主要操作记录如下:

  1. 将 Git 引入运行环境. git.exe 来自 Git for Windows, 具体的安装版本是 Git-1.8.1.2-preview20130201.exe, 安装后只需要保留 bin 目录即可.
  2. 修改 trac.ini 及 增加版本库:
    • 按照 [TracGit] 的说明, 在 trac.ini[components] 小节增加:
      tracopt.versioncontrol.git.* = enabled
      
    • 在 Trac 的 管理: 版本库 中添加版本库:
    • 注意, 在添加版本库时, Trac 会提醒需要执行 trac-admin $ENV repository resync ... 命令(参见上图的提示), 经过实际检验, 这些命令并不是必须的, 可以通过设置 [trac] 小节的 repository_sync_per_request 属性来自动同步源代码库(可能对性能有一定的影响), 例如:
      repository_sync_per_request = PortableTrac, dev-thinkbase.net, trac-thinkbase.net
      
    • 详细的修改参见 97b08e...

thinkbase.net 升级到 Trac 1.0.1

Trac 1.0 分支已经发布版本 1.0.1(参见 http://trac.edgewall.org/wiki/TracDev/ReleaseNotes/1.0#MaintenanceReleases), 因此今天将 http://thinkbase.net 站点(及其运行环境 PortableTrac)升级到这个版本.

主要工作

升级过程的主要工作包括升级 Trac 1.0.1 egg 包升级 Trac 环境, 详细步骤参见 TracUpgrade 或者 http://trac.edgewall.org/wiki/TracUpgrade ;

1. 安装 Trac 1.0.1

由于安装之前需要测试, 因此安装过程是在测试系统中进行的, 安装测试成功后, 将调整后的 PortableTrac 提交到 PortableTrac GitHub Repositories, 然后在正式系统中更新这个运行环境;

  • 主要命令:
    I:\thinkbase.net\github\PortableTrac-git>easy_install --upgrade Trac==1.0.1
    

2. 升级 Trac 环境

升级过程直接在正式系统中进行(此前已经在测试系统中经过测试了).

  • 主要命令:
    D:\thinkbase.net\PortableTrac-git>set SITE_BASE=D:\thinkbase.net\trac-thinkbase.net-git
    D:\thinkbase.net\PortableTrac-git>trac-admin.cmd trac upgrade
    D:\thinkbase.net\PortableTrac-git>trac-admin.cmd main upgrade
    D:\thinkbase.net\PortableTrac-git>trac-admin.cmd trac wiki upgrade
    D:\thinkbase.net\PortableTrac-git>trac-admin.cmd main wiki upgrade
    

详细操作日志

I:\thinkbase.net\github\PortableTrac-git>easy_install --upgrade Trac==1.0.1
[Current timestamp: 2013-02-18 周一 13-52-58.93]

>>> call easy_install.exe --prefix="I:\thinkbase.net\github\PortableTrac-git\trac" --upgrade Trac==1.0.1
Searching for Trac==1.0.1
Reading http://pypi.python.org/simple/Trac/
Reading http://trac.edgewall.org/
Reading http://trac.edgewall.org/wiki/TracDownload
Reading http://trac.edgewall.com/
Reading http://projects.edgewall.com/trac
Reading http://projects.edgewall.com/trac/wiki/TracDownload
Best match: Trac 1.0.1
Downloading http://download.edgewall.org/trac/Trac-1.0.1.win32.exe
Processing Trac-1.0.1.win32.exe
Deleting d:\temp\easy_install-yza88_\Trac-1.0.1-py2.7-win32.egg.tmp\EGG-INFO\scripts\trac-admin-script.py
Deleting d:\temp\easy_install-yza88_\Trac-1.0.1-py2.7-win32.egg.tmp\EGG-INFO\scripts\trac-admin.exe
Deleting d:\temp\easy_install-yza88_\Trac-1.0.1-py2.7-win32.egg.tmp\EGG-INFO\scripts\tracd-script.py
Deleting d:\temp\easy_install-yza88_\Trac-1.0.1-py2.7-win32.egg.tmp\EGG-INFO\scripts\tracd.exe
creating 'd:\temp\easy_install-yza88_\Trac-1.0.1-py2.7-win32.egg' and adding 'd:\temp\easy_install-yza88_\Trac-1.0.1-py2.7-win32.egg.tmp' to it
Moving Trac-1.0.1-py2.7-win32.egg to i:\thinkbase.net\github\portabletrac-git\trac\lib\site-packages
Removing trac 1.0 from easy-install.pth file
Adding Trac 1.0.1 to easy-install.pth file
Installing trac-admin-script.pyc script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing tracd-script.pyc script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing trac-admin-script.py script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing trac-admin.exe script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing trac-admin.exe.manifest script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing tracd-script.py script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing tracd.exe script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts
Installing tracd.exe.manifest script to I:\thinkbase.net\github\PortableTrac-git\trac/Scripts

Installed i:\thinkbase.net\github\portabletrac-git\trac\lib\site-packages\trac-1.0.1-py2.7-win32.egg
Processing dependencies for Trac==1.0.1
Finished processing dependencies for Trac==1.0.1

I:\thinkbase.net\github\PortableTrac-git>

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

D:\thinkbase.net\PortableTrac-git>set SITE_BASE=D:\thinkbase.net\trac-thinkbase.net-git

D:\thinkbase.net\PortableTrac-git>trac-admin.cmd trac upgrade
[Current timestamp: 2013-02-18 星期一 15-12-12.14]

>>> call python.exe  "D:\thinkbase.net\PortableTrac-git\trac\Scripts\trac-admin-script.py" "D:\thinkbase.net\trac-thinkbase.net-git\tracenv\trac" upgrade
Database is up to date, no upgrade necessary.

D:\thinkbase.net\PortableTrac-git>trac-admin.cmd main upgrade
[Current timestamp: 2013-02-18 星期一 15-12-26.48]

>>> call python.exe  "D:\thinkbase.net\PortableTrac-git\trac\Scripts\trac-admin-script.py" "D:\thinkbase.net\trac-thinkbase.net-git\tracenv\main" upgrade
Database is up to date, no upgrade necessary.

D:\thinkbase.net\PortableTrac-git>trac-admin.cmd trac wiki upgrade
[Current timestamp: 2013-02-18 星期一 15-12-35.62]

>>> call python.exe  "D:\thinkbase.net\PortableTrac-git\trac\Scripts\trac-admin-script.py" "D:\thinkbase.net\trac-thinkbase.net-git\tracenv\trac" wiki upgrade
  CamelCase is already up to date
  InterMapTxt already exists
  InterTrac is already up to date
  InterWiki is already up to date
  PageTemplates imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\PageTemplates
  RecentChanges is already up to date
  SandBox is already up to date
  TitleIndex is already up to date
  TracAccessibility is already up to date
  TracAdmin is already up to date
  TracBackup is already up to date
  TracBatchModify is already up to date
  TracBrowser is already up to date
  TracCgi is already up to date
  TracChangeset is already up to date
  TracEnvironment is already up to date
  TracFastCgi imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracFastCgi
  TracFineGrainedPermissions is already up to date
  TracGuide is already up to date
  TracImport is already up to date
  TracIni is already up to date
  TracInstall is already up to date
  TracInterfaceCustomization imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracInterfaceCustomization
  TracLinks is already up to date
  TracLogging is already up to date
  TracModPython is already up to date
  TracModWSGI is already up to date
  TracNavigation imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracNavigation
  TracNotification is already up to date
  TracPermissions imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracPermissions
  TracPlugins is already up to date
  TracQuery is already up to date
  TracReports imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracReports
  TracRepositoryAdmin imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracRepositoryAdmin
  TracRevisionLog is already up to date
  TracRoadmap is already up to date
  TracRss is already up to date
  TracSearch is already up to date
  TracStandalone imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracStandalone
  TracSupport is already up to date
  TracSyntaxColoring is already up to date
  TracTickets is already up to date
  TracTicketsCustomFields is already up to date
  TracTimeline is already up to date
  TracUnicode is already up to date
  TracUpgrade imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracUpgrade
  TracWiki is already up to date
  TracWorkflow imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracWorkflow
  WikiDeletePage is already up to date
  WikiFormatting is already up to date
  WikiHtml is already up to date
  WikiMacros imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\WikiMacros
  WikiNewPage imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\WikiNewPage
  WikiPageNames is already up to date
  WikiProcessors imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\WikiProcessors
  WikiRestructuredText is already up to date
  WikiRestructuredTextLinks is already up to date

D:\thinkbase.net\PortableTrac-git>trac-admin.cmd main wiki upgrade
[Current timestamp: 2013-02-18 星期一 15-12-46.85]

>>> call python.exe  "D:\thinkbase.net\PortableTrac-git\trac\Scripts\trac-admin-script.py" "D:\thinkbase.net\trac-thinkbase.net-git\tracenv\main" wiki upgrade
  CamelCase is already up to date
  InterMapTxt already exists
  InterTrac is already up to date
  InterWiki is already up to date
  PageTemplates imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\PageTemplates
  RecentChanges is already up to date
  SandBox is already up to date
  TitleIndex is already up to date
  TracAccessibility is already up to date
  TracAdmin is already up to date
  TracBackup is already up to date
  TracBatchModify is already up to date
  TracBrowser is already up to date
  TracCgi is already up to date
  TracChangeset is already up to date
  TracEnvironment is already up to date
  TracFastCgi imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracFastCgi
  TracFineGrainedPermissions is already up to date
  TracGuide is already up to date
  TracImport is already up to date
  TracIni is already up to date
  TracInstall is already up to date
  TracInterfaceCustomization imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracInterfaceCustomization
  TracLinks is already up to date
  TracLogging is already up to date
  TracModPython is already up to date
  TracModWSGI is already up to date
  TracNavigation imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracNavigation
  TracNotification is already up to date
  TracPermissions imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracPermissions
  TracPlugins is already up to date
  TracQuery is already up to date
  TracReports imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracReports
  TracRepositoryAdmin imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracRepositoryAdmin
  TracRevisionLog is already up to date
  TracRoadmap is already up to date
  TracRss is already up to date
  TracSearch is already up to date
  TracStandalone imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracStandalone
  TracSupport is already up to date
  TracSyntaxColoring is already up to date
  TracTickets is already up to date
  TracTicketsCustomFields is already up to date
  TracTimeline is already up to date
  TracUnicode is already up to date
  TracUpgrade imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracUpgrade
  TracWiki is already up to date
  TracWorkflow imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\TracWorkflow
  WikiDeletePage is already up to date
  WikiFormatting is already up to date
  WikiHtml is already up to date
  WikiMacros imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\WikiMacros
  WikiNewPage imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\WikiNewPage
  WikiPageNames is already up to date
  WikiProcessors imported from C:\Documents and Settings\Administrator\Application Data\Python-Eggs\trac-1.0.1-py2.7-win32.egg-tmp\trac\wiki\default-pages\WikiProcessors
  WikiRestructuredText is already up to date
  WikiRestructuredTextLinks is already up to date

D:\thinkbase.net\PortableTrac-git>

使用 x3dom 框架及 WebGL 在浏览器上显示 3 维模型

如果需要在浏览器上显示 3D 画面的话, 现在一般会使用 WebGL, 典型的例如 three.js(http://mrdoob.github.com/three.js/), 但是 WebGL 对浏览器版本以及显卡的要求比较高, 很多客户端无法正常使用 ———— 当然现在新买的机器是毫无压力的.

后来在网上查找到了 X3DOM(http://www.x3dom.org/), 通过使用不同的后端(backend), X3DOM 可以兼容较低版本的浏览器(主要是指 IE 系列), 也可以在没有显卡支持的情况下运行, 常用的非 WebGL 后端是 Adobe Flash Player 11, IE 也可以通过使用 Google Chrome Frame 实现对 WebGL 的支持, 注意 Flash 11 以下的版本是不能正常运行的.

X3DOM 通过在标准的 HTML5 DOM 中加入 X3D 格式的 XML 元素, 实现将 X3D 格式的 3D 模型嵌入到 HTML 页面的功能; 在 http://www.x3dom.org/ 网站上有丰富的示例以及较详细的文档, 不过彻底搞懂估计需要一些 3D 建模的知识, 比如 fieldOfView 这样的专业术语.

另外 web3D Consortium 也有大量关于 X3D 的资料, 尤其是 X3D tooltips(http://www.web3d.org/x3d/content/X3dTooltips.html), 是一份全面的 X3D 节点元素速查手册(有中文版)(不知道为什么 www.web3d.org 被 GWF 了, 如果要下载也可以到 这里);

在初步了解 x3dom 的基础上, 以显示货物装箱为例, 对 x3dom 进行了简单的封装, 重点关注 货物在集装箱等容器中的堆放方式的显示, 可以实现以 "Box" 的方式加入不同尺寸的货物(长方体), 以不同的角度查看堆放情况, 以及对这些长方体的选择/加亮显示等等, 具体代码可以到 https://github.com/thinkbase/dev-thinkbase.net/tree/master/.research/x3dom-container-fill 下载, 注意测试用的 html 文件不能直接在本地打开, 必须部署到 HTTP 服务器上才能正常运行; 如果不想麻烦的话,在这里可以看到 在线演示.

实际显示的效果如下图所示(使用 Flash backend 的效果, 如果系统支持 WebGL 的话效果应该要好很多):

DocFetcher 1.1.5 试用及 zip 文件中文问题解决

自从接触 DocFetcher 以来, 就一直使用它来搜索自己的各类文档, 当时也总结过这个软件存在的问题, 详见 桌面搜索工具 DocFetcher 试用笔记;

概况

最近发现 DocFetcher 已经发布了 1.1.5 版本, 根据网站上的介绍, 1.1 版本经过完全重写(rewritten from scratch), 增加了大量的新特征(详见 http://docfetcher.sourceforge.net/wiki/doku.php?id=changes_in_v1.1), 包括我在 桌面搜索工具 DocFetcher 试用笔记 中所关注的:

  • Archive indexing: DocFetcher can now traverse archives. The following archive formats are supported: zip and zip-derived formats, 7z, rar, SFX zip, SFX 7z - 实际实验确认可以支持搜索压缩包中的内容, 而且支持嵌套压缩, 具体可以查看下面的截图;
  • Indexing of and searching in filenames - 可以按照文件名查找;

经过简单试用, 觉得这个版本还是值得升级的, 随后发现在搜索 zip 格式压缩包里面的内容时, 中文文件名会变成乱码. 具体现象如下图所示, 虽然可以索引压缩文件中的内容, 但是 zip 格式压缩包中中文文件名显示为乱码, 而 7z 和 rar 格式则显示正确:

下载代码(git clone http://git.code.sf.net/p/docfetcher/code docfetcher-code)研究了一下, 发现 DocFetcher 使用了 truezip 来进行 zip 格式压缩文件的搜索(支持 jar|tar|tar.bz2|tar.gz|tb2|tbz|tgz|zip 等多种格式), 而在 truezip 中, zip 格式默认使用的字符集为 IBM437:

... ...
public class ZipDriver
extends FsCharsetArchiveDriver<ZipDriverEntry>
implements ZipOutputStreamParameters, ZipFileParameters<ZipDriverEntry> {

    private static final Logger logger = Logger.getLogger(
            ZipDriver.class.getName(),
            ZipDriver.class.getName());

    /**
     * The character set for entry names and comments in &quot;traditional&quot;
     * ZIP files, which is {@code "IBM437"}.
     */
    private static final Charset ZIP_CHARSET = Charset.forName("IBM437");

    private final IOPool<?> ioPool;

    /**
     * Constructs a new ZIP driver.
     * This constructor uses {@link #ZIP_CHARSET} for encoding entry names
     * and comments.
     *
     * @param ioPoolProvider the provider for I/O entry pools for allocating
     *        temporary I/O entries (buffers).
     */
    public ZipDriver(IOPoolProvider ioPoolProvider) {
        this(ioPoolProvider, ZIP_CHARSET);
    }
    ... ...

而日常我们使用的 zip 压缩文件都是使用本地字符集进行压缩的(在中文环境下, 一般就是GBK), 继续研究发现, DocFetcher 使用 truezipTFile, 实现与 java.io.File 相似的方式统一访问文件系统目录和压缩文件(也就是把压缩文件看作一个目录), 而 TFile 内部则通过一个 TArchiveDetector 类型的成员变量来依据后缀名确定使用那种 "Driver" 操作具体的压缩文件;

默认情况下, 通过 TConfig.getArchiveDetector() 得到的 ArchiveDetector 是 TArchiveDetector.ALL, 此时在 TArchiveDetector 中实际使用 FsDriverLocator.SINGLETON 来获得所有加载的 "Driver", FsDriverLocator 会通过 ServiceLocator 查找并加载所有可用的 Driver, zip 格式相关 Driver 的定义实现在 ZipDriverService 中:

@Immutable
public final class ZipDriverService extends FsDriverService {

    private static final Map<FsScheme, FsDriver>
            DRIVERS = newMap(new Object[][] {
                { "zip", new ZipDriver(IOPoolLocator.SINGLETON) },
                { "ear|jar|war", new JarDriver(IOPoolLocator.SINGLETON) },
                { "odt|ott|odg|otg|odp|otp|ods|ots|odc|otc|odi|oti|odf|otf|odm|oth|odb", new OdfDriver(IOPoolLocator.SINGLETON) },
                { "exe", new ReadOnlySfxDriver(IOPoolLocator.SINGLETON) },
            });

    @Override
    public Map<FsScheme, FsDriver> get() {
        return DRIVERS;
    }
}

从上面这段代码可见, 系统默认得到的 ZipDriver 使用的是默认字符集 IBM437, 所以, 会产生中文问题;

另外, 从源代码还可以看到, JarDriver 默认使用的字符集是 UTF-8;

解决这个问题的方式是对 ZipDriverService 进行一定的调整, 以便通过环境变量或者 Java 系统属性来调整 ZipDriver 的默认字符集, 然后把这个 class 以 jar 补丁的形式, 放到 CLASSPATH 的最前面. 修改后的 ZipDriverService 代码如下:

@Immutable
public final class ZipDriverService extends FsDriverService {
        private static final String SYS_PROP_ZIP_CHARSET = ZipDriverService.class.getPackage().getName() + ".ZIP_CHARSET";
        private static final String ENV_VAR_ZIP_CHARSET = "TRUEZIP_ZIP_CHARSET";

        private static final ZipDriver buildZipDriver(){
                String charset = System.getProperty(SYS_PROP_ZIP_CHARSET);
                if (null!=charset && charset.trim().length() > 0){
                        return new ZipDriver(
                                        IOPoolLocator.SINGLETON, Charset.forName(charset.trim()));
                }
                charset = System.getenv(ENV_VAR_ZIP_CHARSET);
                if (null!=charset && charset.trim().length() > 0){
                        return new ZipDriver(
                                        IOPoolLocator.SINGLETON, Charset.forName(charset.trim()));
                }
                
                return new ZipDriver(IOPoolLocator.SINGLETON);
        }

    private static final Map<FsScheme, FsDriver>
            DRIVERS = newMap(new Object[][] {
                { "zip", buildZipDriver() },
                { "ear|jar|war", new JarDriver(IOPoolLocator.SINGLETON) },
                { "odt|ott|odg|otg|odp|otp|ods|ots|odc|otc|odi|oti|odf|otf|odm|oth|odb", new OdfDriver(IOPoolLocator.SINGLETON) },
                { "exe", new ReadOnlySfxDriver(IOPoolLocator.SINGLETON) },
            });

    @Override
    public Map<FsScheme, FsDriver> get() {
        return DRIVERS;
    }
}

编译后的 jar 补丁可以到附件中下载, 此 jar 文件可以放到 DocFetcher-1.1.5 的 patches 目录下, 然后按照下图的样子修改 DocFetcher.sh:

修改后即可正常处理 zip 格式压缩文件中的中文文件名了:

总结

补充说明

本文所提供的补丁没有在 Windows 系统上进行测试, 如果要在 Windows 系统中使用, 建议设置系统环境变量 set TRUEZIP_ZIP_CHARSET=GBK 后运行, 效果应该与 Linux 下一致;

顺便说一下, DocFetcher 对 rar 文件的解析是通过 java-unrar 实现的, 与 zip 压缩文件不同, rar 和 7z 压缩文件是通过所谓 SolidArchiveFactory 来处理的, SolidArchive 模式需要将文件解压到临时目录后再进行索引处理;

END