Dec 312007
 
    /**
     * 2006-6-10
     * @author Sparkle
     */
    public class CryptUtil {
	/**
	 * 将一个数字转换成0-9a-zA-z的62进制
	 */
	public static String encodeLongToString(long i) {
		if (i == 0) {
			return "0";
		} else if (i < 0) {
			throw new IllegalArgumentException();
		}
		StringBuffer buffer = new StringBuffer();
		while (i > 0) {
			buffer.insert(0, encodeIntToChar((int) (i % 62)));
			i /= 62;
		}
		return buffer.toString();
	}
 
	/**
	 * 将0-9a-zA-z的62进制转换成一个数字
	 */
	public static long decodeStringToLong(String s) {
		long i = 0;
		for (int index = 0; index < s.length(); index++) {
			if (index > 0) {
				i *= 62;
			}
			i += decodeCharToInt(s.charAt(index));
		}
		return i;
	}
 
	/**
	 * 将数字作为顺序转换成单位的0-9a-zA-z<br />
	 * 0 -> '0'<br />
	 * 9 -> '9'<br />
	 * 10 -> 'a'<br />
	 * 35 -> 'z'<br />
	 * 36 -> 'A'<br />
	 * 61 -> 'Z'
	 */
	private static char encodeIntToChar(int i) {
		if (i >= 0 && i <= 9) {
			return (char) (i + 48);
		} else if (i >= 10 && i <= 35) {
			return (char) (i + 87);
		} else if (i >= 36 && i <= 61) {
			return (char) (i + 29);
		} else {
			throw new IllegalArgumentException();
		}
	}
 
	/**
	 * 将单位的0-9a-zA-z按顺序转换成数字<br />
	 * '0' -> 0<br />
	 * '9' -> 9<br />
	 * 'a' -> 10<br />
	 * 'z' -> 35<br />
	 * 'A' -> 36<br />
	 * 'Z' -> 61
	 */
	private static int decodeCharToInt(char c) {
		if (c >= '0' && c <= '9') {
			return c - 48;
		} else if (c >= 'a' && c <= 'z') {
			return c - 87;
		} else if (c >= 'A' && c <= 'Z') {
			return c - 29;
		} else {
			throw new IllegalArgumentException();
		}
	}
    }

代码点评:这段代码的作用是,转换出来的字符串会比16进制要短很多

Dec 182007
 

Apache

* 经典的Web服务器
* 除了慢没有别的缺点了
* 对了,Apache2对fcgi支持并不好
* 非常好用的proxy和proxy_ajp(很多人用它作为tomcat的前端)
* 不支持epoll(这年头,epoll几乎是性能的必备)

Nginx

* 速度快,占用资源少
* 杀手级的proxy和rewrite
* 非常不错的静态文件能力
* 最适合作为整个网站的前端服务(将php、svn等不同请求发送往后端apache)
* 其他功能马马虎虎

Lighttpd

* 杀手级的静态文件能力
* 杀手级的fcgi能力
* 不稳定的proxy模块

总体来说,如果你不确定应该用什么服务器,那就应该用Apache
但是稍微可以配置多个服务的情况下,做一个Nginx在最前端,然后把需要的功能转发给Apache是最好的选择
如果你打算跑fcgi,Lighttpd是不二的选择
如果你打算做图片服务器,独立的Lighttpd也是很好的选择

Dec 152007
 

一直在用COS作为上传组件
最近升级其中一个应用到Spring 2.5
竟然报错,找不到org.springframework.web.multipart.cos.CosMultipartResolver类
Spring 2.5把web和webmvc独立出来已经觉得有点奇怪得了
重新检查了一下,在spring-web里面找到

> org.springframework.web.multipart.commons

但是找不到

> org.springframework.web.multipart.cos

立刻查看2.5的reference,里面还在提及怎么使用COS

对比查看2.0和2.5的MultipartResolver的javadoc
在2.5发现这样一句

> There is only one concrete implementation included in Spring, as of Spring 2.5:
> org.springframework.web.multipart.commons.CommonsMultipartResolver for Jakarta Commons FileUpload

COS支持的确被去掉了,晕
一直一来,许多人的观点都是认为commons-fileupload是有内存泄露的,用COS比较保险

为了确认一下被去掉的原因,到spring forum里面search了一番,没有提及到这次的修改

在spring的changelog里面search了一番,终于找到说明的地方

> Changes in version 2.1 M1 (2007-05-13)
> removed CosMultipartResolver (as part of generally removing COS support from the Spring codebase)

但是没有说原因

晕了

看来只能试试用commons-fileupload

Dec 112007
 

在Linux下面部署应用的时候,有时候会遇上Socket/File: Can’t open so many files的问题,其实Linux是有文件句柄限制的(就像WinXP?),而且默认不是很高,一般都是1024,作为一台生产服务器,其实很容易就达到这个数量,因此我们需要把这个值改大一些。

大概知道ulimit这个命令是相关的,上Google搜索了一下,大多数说的很含糊,也没有统一说一下,经过两个小时看了不少文章终于弄清楚ulimit相关的一些配置问题。

我们可以用ulimit -a来查看所有限制值,我只关心文件句柄数量的问题

open files (-n) 1024

这个就是限制数量

这里,有很多ulimit的文章都说的很含糊,究竟这个1024是系统的限制,还是用户的限制呢。其实,这个是用户限制来的,完整的说法,应该是当前用户准备要运行的程序的限制。

1、这个限制是针对单个程序的限制
2、这个限制不会改变之前已经运行了的程序的限制
3、对这个值的修改,退出了当前的shell就会消失

比如说,我先运行了一个程序A,然后通过ulimit修改了限制为2048,然后运行B,然后退出了shell再登录,然后运行C。那就只有B可以打开2048个句柄。

如果我们需要改变整体的限制值,或者我们运行的程序是系统启动的,应该怎么处理呢

其中一个方法,是想ulimit修改命令放入/etc/profile里面,但是这个做法并不好

正确的做法,应该是修改/etc/security/limits.conf
里面有很详细的注释,比如

* soft nofile 2048
* hard nofile 32768

就可以将文件句柄限制统一改成软2048,硬32768

这里涉及另外一个问题,什么是软限制,什么是硬限制
硬限制是实际的限制,而软限制,是warnning限制,只会做出warning
其实ulimit命令本身就有分软硬设置,加-H就是硬,加-S就是软
默认显示的是软限制,如果修改的时候没有加上的话,就是两个一起改

配置文件最前面的一位是domain,设置为星号代表全局,另外你也可以针对不同的用户做出不同的限制

修改了,重新登录用ulimit一开就立刻生效了,不过之前启动过的程序要重新启动才能使用新的值。我用的是CentOS,似乎有些系统需要重启才能生效。

ulimit其实就是对单一程序的限制

那系统总限制呢
其实是在这里,/proc/sys/fs/file-max
可以通过cat查看目前的值,echo来立刻修改

另外还有一个,/proc/sys/fs/file-nr
只读,可以看到整个系统目前使用的文件句柄数量

查找文件句柄问题的时候,还有一个很实用的程序lsof
可以很方便看到某个进程开了那些句柄
也可以看到某个文件/目录被什么进程占用了(umount不了就可以看谁的问题了)

Dec 102007
 

在一个开发团队里面,bug tracker工具是很重要的。软件一定有bug,我们需要一个跟踪bug的工具,谁报告的bug?详细情况怎么样?别人能不能还原?

需要修正吗?优先度?已经修正了吗?等等。虽然我说了那么多需求,但实际上太全面的功能也会导致复杂性,呵呵。

最近需要在公司部署一个bug tracker,目前可以选择的工具也有不少

1、Bugzilla
功能非常强大,定制性也很强,Mylyn支持最好
可惜,安装困难,板式不直观
=====
正好提到Mylyn,其实我的选择,很大程度受到Mylyn的支持情况影响

2、JIRA
功能强大,定制性也非常强
可惜太复杂了

3、Mantis
我一直对这个软件印象非常好
清晰的版面,用上去就非常舒服
可惜新版本迟迟未release
另一个重要的原因是Mylyn对他支持非常差

4、Trac
Mylyn官方支持三个软件,Bugzilla,JIRA,Trac
因此我也在这三个软件中挑选了很久
Trac的安装比较麻烦(当然后来知道并不麻烦)
功能也中规中矩,有最基本的功能(其实我也不想让软件做太多功能,始终团队的能力为主)
定制能力很差(其实我不需要定制)
同时还包含一个wiki(正好还在讨论公司内部要不要弄一个wiki)
对mysql支持不太好(其实sqlite的效果也很不错,备份也方便)
认证模式刚好可以跟svn一样(太棒了)
真是越用越喜欢,呵呵

Nov 282007
 

一个系统里面经常需要做一些定时任务,比如说定时清空今日得分,或者定时清理临时文件。简单的定时任务很容易实现,用线程或者用Timer就可以了,但是始终需要自己写大量代码才能实现复杂的需求。

于是便有Quartz。不过,Quartz太久没有更新了,而且它太复杂。由于我的系统是基于Spring构建的,所以我希望能使用Spring支持的scheduling类库,可惜Spring只支持commonj和Quartz,正确来说,在Java界,并没有别的scheduling类库了,而commonj只是一个interface,没有具体的实现,似乎在Weblogic之类的里面有实现。

当然,也有另外一个选择,也是轻量级的脚本语言常用的做法,就是使用Linux的crontable,可以实现比较复杂的定时。不过,脚本语言调用数据库并不是很方便(应该说我们的团队技术累积上的问题),如果用crontable启动Java,每次启动的成本又比较高。

在评估过各种方案之后,我还是选择了使用Quartz,首先从Spring的辅助类开始入手吧。

题外话,在一个集群的环境里面(也就是多个Tomcat的环境下),定时任务应该是独立的应用,也就是不应该在每一个Tomcat里面都启动Quartz或者定时线程。另外,在Tomcat的应用里面,也是尽量不要使用线程,有可能一点点小错误就会导致整个Tomcat崩溃(其实我们还是使用很多的,呵呵)。

根据Quartz的使用行为,一个任务我们至少需要一个Job、一个JobDetail、一个Trigger(真复杂)

    JobDetail jobDetail = new JobDetail("myJob",               // job name
                                      sched.DEFAULT_GROUP,   // job group
                                      DumbJob.class);// the java class to execute
    Trigger trigger = TriggerUtils.makeDailyTrigger(8, 30);
    trigger.setStartTime(new Date());
    trigger.setName("myTrigger");
    sched.scheduleJob(jobDetail, trigger);

首先!!我在这里要明确一个事情。Job类是没有状态的!!
这是什么概念呢,就是说,你实现的一个Job(例如上面的代码的DumbJob),并不是由你自己new出来的,留意一下new JobDetail的代码,传入的参数是DumbJob.class,而不是一个具体的job实例。Quartz帮你吧Job new一份出来,并且调用相应的接口,并没有别的功能。

这里会带来一个什么问题呢,我们先来看看Spring的辅助类。

Spring有两个辅助类可以产生JobDetail类,需要留意的是,Spring并不辅助产生Job类,也就是Spring认为Job类不需要管理。

我们先看看第一个,JobDetailBean

    <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
      <property name="jobClass" value="example.ExampleJob" />
      <property name="jobDataAsMap">
        <map>
          <entry key="timeout" value="5" />
        </map>
      </property>
    </bean>

不知道大家有没有看出问题在哪里。property jobClass是一个类名,并不是一个实例名!也就是跟Quartz的调用一样,是Quartz负责帮你new一个example.ExampleJob类出来,也就是说你不能对Job类进行任何形式的注入(IOC),比如说,我们的example.ExampleJob是一个DAO,需要传入DataSource进行DB操作,没辙。

因此,Spring提供了另外一个JobDetail辅助类MethodInvokingJobDetailFactoryBean

    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
      <property name="targetObject" ref="exampleBusinessObject" />
      <property name="targetMethod" value="doIt" />
    </bean>

你可以留意到,property targetObject是一个ref,指向的是一个常规的Spring管理的Bean。

但是!

MethodInvokingJobDetailFactoryBean很不友好。首先,它是通过反射调用的,而不是Interface,因此我们必须要看了Spring的xml才能知道谁被调用了,你还可能会写一大堆property targetMethod=doIt,而且Job Interface是会传入一个JobExecutionContext,这个被miss了。
其次,如果我们需要大量的Job的话(因为我就是做一个专门用来定时的应用),Spring的配置文件会变得非常臃肿,我希望Job和JobDetail不需要Spring专门管理,只要他是一个Spring管理的Bean,并且实现了Job这个接口就ok了。

这里补充一个事情,我们跳过了Trigger的部分,每一个JobDetail必须配备一个相应的Trigger,因此配置文件是你之前想象中的两倍那么大,而且你还得给每一个Bean命名一个ID,而这个类你以后都不会用到。

我的目标是:
1、只要是实现了Job接口的Spring管理的Bean,自动加入scheduling,根本不用关心JobDetail的存在,也不会有注入的问题
2、所有Job均使用CronTrigger,并且通过配置文件设定Cron Expressions

通过研究MethodInvokingJobDetailFactoryBean和Quartz的代码,我明白到JobDetail是有状态的,而MethodInvokingJobDetailFactoryBean正是利用这点来实现具体效果的,于是便有了我一下这些辅助代码

首先
,需要一个DummyJob,由于Quartz的主入口始终是Job类

    public class DummyJob implements Job {
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		Job job = (Job) context.getMergedJobDataMap().get("methodInvoker");
		if (job != null) {
			job.execute(context);
		}
	}
    }

jobDataMap就是JobDetail存储状态的地方,DummyJob唯一要做的就是,知道实际的Job类,并且调用它

接下来是戏玉了

		Map<string, Job> jobMap = context.getBeansOfType(Job.class);
		for (Map.Entry<string, Job> entry : jobMap.entrySet()) {
			String taskName = entry.getKey();
			String cronExpression = props.getProperty(taskName);
			if (cronExpression == null) {
				logger.warn("[{}] don't have a cronExpression", taskName);
				continue;
			}
 
			try {
				Trigger trigger = new CronTrigger(taskName + "Trigger", null,
						cronExpression);
				JobDetail jobDetail = new JobDetail(taskName + "Job", null,
						DummyJob.class);
				jobDetail.getJobDataMap()
						.put("methodInvoker", entry.getValue());
				scheduler.scheduleJob(jobDetail, trigger);
			} catch (ParseException e) {
				logger.error("", e);
			} catch (SchedulerException e) {
				logger.error("", e);
			}
		}

从Spring context里面读取所有实现了Job的类遍历,props是从文件里面读取相应的cronExpression配置。

				JobDetail jobDetail = new JobDetail(taskName + "Job", null,
						DummyJob.class);
				jobDetail.getJobDataMap()
						.put("methodInvoker", entry.getValue());

这两句是关键

于是,Quartz变得更sexy了

Oct 252007
 

中国的南北网络问题,是许多做网站的人的心病
除了使用双通或者多通机房以外,还可以通过多台镜像服务器的方法来提高用户的访问速度
但是,如果使用的双通机房并不是单IP的,或者使用多台镜像的做法,就会面临多个不同的服务器IP的问题
最早的时候,很多镜像服务器都是使用不同的域名的,比如说www1和www2,或者www和cnc
这样的做法,对用户相当不友好,而且不方便推广,比如说,你要告诉朋友一个网站,还要问他是什么线路才能给网址吗,或者可能你还不知道这个网站有一个专为网通的线路所设置的镜像
用统一的域名,然后根据用户的线路自动判断引导到不同的镜像服务器,正是智能DNS的功用(智能DNS其实是CDN的其中一部分,是最前端的部分)

现在还是有一些地方使用多域名,比如说下载网站的下载镜像服务器,因为智能DNS有一定的缺陷(并不一定选择到正确的线路,下面说),也提供给用户自由选择线路的权利

目前也有一些公司提供智能DNS解释服务,把你的域名NS指到他们服务器,然后输入网通和电信服务器的IP就ok了
也有一些公司提供比较完整的CDN服务,比如比较出名的就是chinacache,不少中型网站是使用他们的服务

最初的bind是不支持通过来源选择不同的ip的,因此只能通过iptables的辅助来实现智能DNS,方法也很简单,在DNS服务器上面启动两份bind,都不要用默认的端口,两份分别解析不同的线路,然后再用iptables将默认端口的DNS请求根据来源IP发送到不同的bind实例
这种方法缺点很明显,需要多个bind实例,如果需要更多的线路解释那就还要再开bind实例,另外iptables对来源的解释也会导致iptables太多规则

现在的bind支持了根据来源选择的功能,我不确定是不是bind9的新特性,不过bind9是重写过的,相比漏洞百出的bind8来说,稳定和安全很多,以下的配置以bind9为基础

我使用的是CentOS,由于bind服务没有其他服务(比如apache,squid)要求那么高,所以也没有必要自己编译一份,我是用过yum安装的,版本不是最新的稳定版

装好之后,可以用过/etc/init.d/named start成功启动服务
有一个文件named.conf出现在/etc下,就是bind的主config文件,我们只要改动他
(我只说明智能DNS实现部分,其他的bind设置自行修改,呵呵)
另外一个小技巧,可以通过/usr/sbin/named -g -u named来启动bind,可以立刻看到输出信息,方便调试

我们是通过bind9的view功能来分离判断的
首先,我们需要一份网通的IP列表,这是一份在网上流传的列表,呵呵

acl “CNC” {
58.16.0.0/16;
58.17.0.0/17;
58.17.128.0/17;
58.18.0.0/16;
58.19.0.0/16;
58.20.0.0/16;
58.21.0.0/16;
58.22.0.0/15;
58.240.0.0/15;
58.242.0.0/15;
58.244.0.0/15;
58.246.0.0/15;
58.248.0.0/13;
60.0.0.0/13;
60.8.0.0/15;
60.10.0.0/16;
60.11.0.0/16;
60.12.0.0/16;
60.13.0.0/18;
60.13.128.0/17;
60.14.0.0/15;
60.16.0.0/13;
60.24.0.0/14;
60.30.0.0/16;
60.31.0.0/16;
60.208.0.0/13;
60.216.0.0/15;
60.218.0.0/15;
60.220.0.0/14;
61.48.0.0/13;
61.133.0.0/17;
61.134.96.0/19;
61.134.128.0/17;
61.135.0.0/16;
61.137.128.0/17;
61.138.0.0/17;
61.138.128.0/18;
61.139.128.0/18;
61.148.0.0/15;
61.156.0.0/16;
61.159.0.0/18;
61.161.0.0/18;
61.161.128.0/17;
61.162.0.0/16;
61.163.0.0/16;
61.167.0.0/16;
61.168.0.0/16;
61.176.0.0/16;
61.179.0.0/16;
61.181.0.0/16;
61.182.0.0/16;
61.189.0.0/17;
125.32.0.0/16;
125.40.0.0/13;
202.96.0.0/18;
202.96.64.0/21;
202.96.72.0/21;
202.97.128.0/18;
202.97.224.0/21;
202.97.240.0/20;
202.98.0.0/21;
202.98.8.0/21;
202.99.64.0/19;
202.99.96.0/21;
202.99.128.0/19;
202.99.160.0/21;
202.99.168.0/21;
202.99.176.0/20;
202.99.208.0/20;
202.99.224.0/21;
202.99.232.0/21;
202.99.240.0/20;
202.102.128.0/21;
202.102.224.0/21;
202.102.232.0/21;
202.106.0.0/16;
202.107.0.0/17;
202.108.0.0/16;
202.110.0.0/17;
202.111.128.0/18;
203.93.8.0/24;
203.93.192.0/18;
210.13.128.0/17;
210.14.160.0/19;
210.14.192.0/19;
210.15.32.0/19;
210.15.96.0/19;
210.15.128.0/18;
210.21.0.0/16;
210.52.128.0/17;
210.53.0.0/17;
210.53.128.0/17;
210.74.96.0/19;
210.74.128.0/19;
210.82.0.0/15;
218.8.0.0/14;
218.12.0.0/16;
218.21.128.0/17;
218.24.0.0/14;
218.56.0.0/14;
218.60.0.0/15;
218.67.128.0/17;
218.68.0.0/15;
218.104.0.0/14;
219.154.0.0/15;
219.156.0.0/15;
219.158.0.0/17;
219.158.128.0/17;
219.159.0.0/18;
220.252.0.0/16;
221.0.0.0/15;
221.2.0.0/16;
221.3.0.0/17;
221.3.128.0/17;
221.4.0.0/16;
221.5.0.0/17;
221.5.128.0/17;
221.6.0.0/16;
221.7.0.0/19;
221.7.32.0/19;
221.7.64.0/19;
221.7.96.0/19;
221.8.0.0/15;
221.10.0.0/16;
221.11.0.0/17;
221.11.128.0/18;
221.11.192.0/19;
221.12.0.0/17;
221.12.128.0/18;
221.13.0.0/18;
221.13.64.0/19;
221.13.96.0/19;
221.13.128.0/17;
221.14.0.0/15;
221.192.0.0/15;
221.194.0.0/16;
221.195.0.0/16;
221.196.0.0/15;
221.198.0.0/16;
221.199.0.0/19;
221.199.32.0/20;
221.199.128.0/18;
221.199.192.0/20;
221.200.0.0/14;
221.204.0.0/15;
221.206.0.0/16;
221.207.0.0/18;
221.207.64.0/18;
221.207.128.0/17;
221.208.0.0/14;
221.212.0.0/16;
221.213.0.0/16;
221.216.0.0/13;
222.128.0.0/14;
222.132.0.0/14;
222.136.0.0/13;
222.160.0.0/15;
222.162.0.0/16;
222.163.0.0/19;
222.163.32.0/19;
222.163.64.0/18;
222.163.128.0/17;
};

如果觉得有哪些部分不是很准确的话,可以自行修改

由于这个部分太长了,所以写在一个专门的文件然后include进named.conf比较容易维护
例如,我们把上面这段配置保存为cnc_acl.conf
然后在named.conf写入

include “/data/named/cnc_acl.conf”;

即可

接下来就是view的配置,假设域名是xyz.com,呵呵

view “view_cnc” {
match-clients { CNC; };
zone “xyz.com” {
type master;
file “/data/named/cnc_xyz”;
};
};

view “view_any” {
match-clients { any; };
zone “xyz.com” {
type master;
file “/data/named/any_xyz”;
};
};

就这样就足够了

然后我们需要cnc\_xyz和any_xyz两个具体的zone file
两个文件基本是一模一样的,就是指向不同的ip

$TTL 2h
xyz.com. IN SOA ns.xyz.com. root.xyz.com. (
20071022
2h
40m
1w
2h
)

this6.com. IN NS ns.xyz.com.
this6.com. IN MX 1 mail.xyz.com.
ns IN A 1.1.1.1
mail IN A 1.1.1.1

www IN A 1.1.1.1

根据不同的线路把www指向不同的IP即可
这样,我们就实现了自己的智能DNS

后话,目前许多大型网站都是用这个来实现不同的线路来源访问不同的镜像,甚至还做到地区级别,例如,在佛山机房部署了一台镜像服务器,就将广东省的访问都指向这台服务器
另外,刚刚提到智能DNS也有缺陷,这里详细描述一下,bind9通过用户的来源IP来返回不同的解释IP,而实际上,并不是实际用户直接访问我们的DNS,而是用户所使用的DNS(比如说广州ADSL用户的默认DNS)负责访问我们的DNS,网通的DNS就保存有我们网通的IP,如果用户自行修改了DNS,比如说电信的用户硬要用网通的DNS,或者网通的用户使用国外的DNS(比如opendns之类的),就会导致判断失误

 Posted by at 6:20 am  Tagged with:

trac续

 Uncategorized  No Responses »
Oct 092007
 

上一篇blog介绍了怎么在windows下安装并使用trac,但是只能说基本能用
在公司里面使用trac,我们还有一些额外的需求
比如,如何用windows service来运行,可以用web server的方式来跑吗,如何身份认证

其实,所有的这些问题,都可以用apache跑trac解决,而且效果也非常好。下面我就来说说我现在用的配置

首先说一下trac可以使用的运行方式,一共有4种
第一种就是tracd,就是上一篇文章所说的,使用相当便捷,可以作为调试使用,不推荐生产环境
第二种是cgi模式,cgi出名低效率了,直接跳过

第三种是mod_python,一开始我也是使用这种,配置也很方便,不过遇上一个专有问题了
在TracInstall中有描述

> if using mod_python together with xml-related things (like 0.11, or xml-rpc plugin), use python-2.5. expat is namespaced there and does not cause apache to crash any more(see here for details).

刚好我们需要使用xml-rpc,然后apache不停报错,虽然不影响使用,但是还是挺烦的

最后一种方法,就是我们目前在使用的方法,就是使用mod_wsgi
wsgi是最近python提出的一种类似cgi的模型,希望能统一web server <-> app server的通讯,似乎很多人都很推崇,不过因为比较新生,所以似乎用的人也不是很多,刚好trac提供了支持。搜索到mod_wsgi for windows编译好的包,使用非常稳定,下面是我的apache配置

LoadModule wsgi_module modules/mod_wsgi.so
WSGIScriptAlias /trac d:/trac/apache/trac.wsgi

Order deny,allow
Allow from all

呵呵,很简单

然后就是认证问题,好像有多种方法,我使用的是apache内置的认证,trac直接支持
刚好我们的svn也是用这种方式来配置认证的,可以使用同一个user文件,这样svn和trac的帐号就可以互通了


AuthType Basic
AuthName “xxxx”
AuthUserFile d:/svn-repo/users
Require valid-user

Sep 102007
 

trac是一个很不错的wiki+bug tracker软件
公司急需一个bug tracker软件来管理bug和一些小任务
经过评估之后,终于决定使用trac(有空我再写一篇比较和评估的依据)

trac的安装有点麻烦,因为他用了不少外部库,有些还是c写的。这个也是影响我评估的其中一个因素,差点就不打算使用它了
经过几天的研究,发现trac在windows下的安装异常简单,但是有好多需要注意的细节,于是变有了这篇文章
说实在话,现在我甚至觉得windows下的安装比linux还要简单,呵呵

Trac Guide里面的[Trac Installation Guide](http://trac.edgewall.org/wiki/TracInstall)仅仅是一个通用的描述,并不是具体的步骤,所以这个页面只能当成是参考的部分。不过,页面里面也穿插了很多细节,什么可以用什么不能用,所以最后还是得详细看一遍才清楚

真正正确和详细的步骤其实有一份[Trac on Windows](http://trac.edgewall.org/wiki/TracOnWindows)的文档,但是藏的比较隐蔽,具体的入口在Trac FAQ中

让我们来看看这份文档吧,基本上,跟随这份文档你就能在windows安装一个trac

首先是python的版本选择问题,虽然python2.5已经出了很久,但是,因为trac依赖了一个c写的模板引擎ClearSilver,并没有for python2.5 windows的编译好的包,因此,你必须自己编译ClearSilver,那是相当的麻烦。linux呢?linux反正是要编译的,所以就算用python2.4情况也是一样(所以linux的安装还是比较麻烦的)
除了ClearSilver以外,其他包也可能遇到类似的问题,因此,使用python2.4是最佳的选择

题外话,其实还有另外一个选择,就是使用trac 0.11dev。不知道具体的原因,trac已经决定使用Genshi来代替ClearSilver作为模板引擎,具体的文档是[Trac with Python 2.5](http://trac.edgewall.org/wiki/TracOnWindows/Python2.5),不过我觉得还是不要使用测试版比较好。另外说一下,Genshi也是trac出品的

接下来,文档提供了4种安装方法。我最后选择了第二种,也比较推荐这种,使用单独的Installer,Setuptools 的确是一个好工具,但是感觉不太好控制

下载并运行trac-0.10.4.win32.exe、clearsilver-0.9.14.win32-py2.4.exe,Installer会自动找到python的位置。trac使用sqlite来做后端存储,所以你还需要pysqlite-2.3.4.win32-py2.4.exe,如果你还想用trac的svn browser功能,你还需要svn-python-1.4.4.win32-py2.4.exe

关于数据库的选择,trac最初只支持sqlite,现在支持postgresql和mysql,但是官方不推荐使用mysql,说是因为字符集问题?(这个理由有点烂),但是官方不推荐是比较麻烦的,专门装个postgresql也不太现实。其实,sqlite的表现相当好,而且,备份起来相当方便,所以最后还是决定用sqlite

然后。。。没有了,呵,其实trac已经安装完成了
运行

\Python24\Scripts\trac-admin.py my-project initenv
\Python24\Scripts\tracd.py -p 8000 my-project

开始试用你的trac吧

总结一下:
1. 使用python2.4
2. 安装trac-0.10.4.win32.exe、clearsilver-0.9.14.win32-py2.4.exe、pysqlite-2.3.4.win32-py2.4.exe
3. 开始使用!

Jul 262007
 
	public String keywordMask(String str) {
		StringBuffer buffer = new StringBuffer(str);
		for (String keyword : getKeyword()) {
			int index = 0;
			while (true) {
				index = buffer.indexOf(keyword, index);
				if (index < 0) {
					break;
				}
				for (int i = 0; i < keyword.length(); i++) {
					buffer.setCharAt(index + i, '*');
				}
			}
		}
		return buffer.toString();
	}