乐者为王

Do one thing, and do it well.

Cannot launch the remote parser because port 49153 is already in use

在Windows Server 2008中使用ANTLRWorks调试语法时,总是提示说什么端口49153已被占用。使用netstat -an发现这个端口正在被别的进程监听着,虽然知道问题原因,但是不知道如何解决。后来从如果AntlrWorks的Debug报错“当前端口已被占用”,可能是防火墙的原因得到启示:AntlrWorks是可以修改远程调试端口的。不过我没有像该文作者介绍的那样将调试端口改成49253,因为那样可能需要设置防火墙。而是直接将之设置到了一个不在使用的端口49151上。

设置调试端口的路径如下:

File -> Preferences -> Debugger -> Default local port

在Lunarpages虚拟主机上安装Radiant

安裝Rails、Mongrel和Radiant

1
2
3
gem install rails --include-dependencies
gem install mongrel --include-dependencies
gem install radiant --include-dependencies

建立Radiant项目

1
radiant --database mysql rcms

登录Lunarpages cPanel面板,创建数据库rcms及访问该数据库的账号,设置该帐号的访问权限。然后进入MySQL的数据库管理工具phpMyAdmin,导入在本地建立好的创建数据表的SQL文件,执行之。然后修改config/database.yml文件,填入前面创建的访问数据库的用户名和密码。

将config/environment.rb中的

1
#ENV['RAILS_ENV'] ||= 'production'

去掉注释

1
ENV['RAILS_ENV'] ||= 'production'

再将public/dispatch.fcgi中的

1
#!/usr/bin/env ruby

修改为

1
#!/usr/local/bin/ruby

然后将rcms上传到Lunarpages虚拟主机的根目录下,上传后的目录结构如下:

1
2
3
/public_html
/www
/rcms

在cPanel中点击“Cron Jobs” -> “Advanced (Unix Style)”进入Cron管理页面。

lunarpages-cron-jobs

创建如下Cron指令(使用默认时间设置,指令会每分钟被执行一次)。

1
2
3
4
5
6
7
cd /home/<username>
mv public_html public_html.bak
ln -s /home/<username>/rcms/public public_html
mkdir public_html/cgi-bin
chown -R username:<username> /home/<username>/rcms
chmod 755 /home/<username>/rcms
chmod 755 /home/<username>/rcms/public

再填好接收执行结果的邮件地址,然后等个2-3分钟后查收邮件就可以了,在收到邮件后切记要删除上面创建的指令。

现在就可以通过浏览器访问部署在Lunarpages上的Radiant了。

如果你想让Radiant跑在类似http://example.com/rcms这样的域名下,那么可以将上面的指令替换成:

1
2
3
4
cd /home/<username>/public_html
ln -s ../rcms/public rcms
chown -R username:<username> /home/<username>/rcms
chmod 755 /home/<username>/rcms

如果Radiant不工作的话可以在Cron Jobs中使用下列指令检查出了什么问题。

1
2
3
4
cd /home/<username>/rcms/public
chmod 777 dispatch.fcgi
./dispatch.fcgi
chmod 644 dispatch.fcgi

在Windows上面跑Mongrel和Radiant

安裝Rails、Mongrel和Radiant

1
2
3
gem install rails --include-dependencies
gem install mongrel --include-dependencies
gem install radiant --include-dependencies

建立Radiant项目

1
radiant --database mysql rcms

编辑数据库配置文件config/database.yml,填写连接数据库时的用户名和密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
development:
  adapter: mysql
  database: rcms_development
  username: root
  password: 123456
  host: localhost

test:
  adapter: mysql
  database: rcms_test
  username: root
  password: 123456
  host: localhost

production:
  adapter: mysql
  database: rcms_production
  username: root
  password: 123456
  host: localhost

在MySQL中创建数据库,因为是开发环境,所以创建的数据库名为rcms_development。

进入rcms目录,在命令行下敲入以下命令创建数据表:

1
rake development db:bootstrap

一切都准备好后,就可以启动Mongrel服务器了:

1
mongrel_rails start

不出意料的话,应该会看到如下图的信息。

mongrel-rails

如果在启动Mongrel时遇到下面的错误:

1
2
!!! Path to log file not valid: log/mongrel.log
mongrel::start reported an error. Use mongrel_rails mongrel::start -h to get help.

可以通过指定应用目录来解决,因为Mongrel在创建log之前会先验证应用目录。

1
mongrel_rails start -c rcms

如果想把Mongrel作为Windows服务的形式启动,需要安装mongrel_service插件:

1
gem install mongrel_service

注意:某些文档会叫你用gem install win32-service安装win32-service,但实际上不是很必要。因为在安装mongrel_service时系统会问你是否要安装win32-service。

安装好后就可以创建Windows服务了:

1
mongrel_rails service::install -N rcms -c rcms -e development

其中-N指明service名称,-c指明Rails应用的目录,-e指启动模式。

在“控制面版” -> “管理工具” -> “服务”中可以发现多了名为rcms的服务。如果要取消该服务,可以使用以下命令:

1
mongrel_rails service::remove -N rcms

启动和关闭该服务的命令是:

1
2
mongrel_rails service::start -N rcms
mongrel_rails service::stop -N rcms

使用Win32备份Foxmail的邮件

Foxmail是一个非常不错的邮件客户端,可惜在备份邮箱内的邮件上做的不是很好,只能手工一封一封地导出,在邮件比较少的时候这样做还可以应付,但假如有成百上千封邮件时还要这样处理就显然相当郁闷。所以写了个小应用,可以自动将某个邮箱内所有邮件导出为单独的eml格式(该格式可以被OutLook邮件程序识别并打开)的邮件文件。

在Foxmail的安装目录下有一个mail文件夹,该文件夹中的每个子目录分别对应着一个邮件账号。进入某个帐号对应的文件夹,可以发现文件夹中有以in.BOX、out.BOX、send.BOX、spam.BOX和trash.BOX等文件,它们分别对应于收件箱、发件箱、已发送邮件箱、垃圾邮件箱和废件箱。根据VC++实现Foxmail邮件的批量导出一文可以知道每个邮件头以下面的16个字符开始:

1
2
3
0x10 0x10 0x10 0x10 0x10 0x10 0x10
0x11 0x11 0x11 0x11 0x11 0x11
0x53 0x0D 0x0A

知道邮箱所对应的存储文件的格式后,以下的事情就变的很简单。

  1. 获取要备份的文件夹路径;
  2. 遍历该文件夹,找出以BOX为后缀名的邮箱存储文件;
  3. 分析存储文件,将邮件导出为eml格式的邮件文本。

下面是需要用到的一些Win32 API函数:

创建非模态对话框

  • CreateDialog

获取目录信息

  • BROWSEINFO
  • LPITEMIDLIST
  • SHBrowseForFolder
  • SHGetPathFromIDList

遍历某个目录下的文件

  • SetCurrentDirectory
  • FindFileFirst
  • FindNextFile
  • FindClose

备份邮件

  • CreateDirectory
  • CreateFile
  • ReadFile
  • WriteFile
  • CloseHandle

还有,如果在对话框模版里用到了不一般的控件(比如说进度条),那么还需要

  1. 引用头文件commctrl.h;
  2. 并链接comctl32.lib;
  3. 在WinMain中创建对话框前调用InitCommonControls()方法。

代码下载

Windows Server 2008配置技巧

如何取消登录时要按Ctrl+Alt+Delete组合键

开始 -> 运行 -> gpedit.msc -> 计算机配置 -> Windows设置 -> 安全设置 -> 本地策略,点击“安全选项”,在右侧的框内找到“交互式登录:不要按CTRL+ALT+DEL” -> 启用。

如何取消关机时出现的关机理由选择项

开始 -> 运行 -> gpedit.msc -> 计算机配置 -> 管理模板,点击“系统”,在右侧的框内找到“显示关机事件跟踪” -> 禁用。

如何禁止远程用户的匿名访问

开始 -> 运行 -> regedit,定位到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa,修改右侧框内restrictanonymous的值为1。

如何在退出系统时清除最近打开的文档的历史

开始 -> 运行 -> gpedit.msc -> 用户配置 -> 管理模板 -> 开始菜单和任务栏 -> 在右侧的框内找到“退出系统时清除最近打开文档的历史” -> 启用。

如何取消必须输入密码登录系统

开始 -> 运行 -> rundll32 netplwiz.dll,UsersRunDll,选中要自动登陆的账户,取消“要使用本机,用户必须输入用户名密码”,输入该帐户的密码即可(前提是要关闭UAC)。

如何关闭UAC

控制面板 -> 用户帐户 -> 打开或关闭用户账户控制 -> 取消“使用用户账户控制(UAC)帮助保护您的计算机”。

如何关闭IE增强安全配置

控制面板 -> 管理工具 -> 服务器管理器 -> 在右侧的框内找到“配置 IE SEC”并打开 -> 禁用。

如何在Web应用的HTTP与HTTPS间的相互切换

对于HTTPS和HTTP的不同请求,Web容器会生成两个不同的session对象。因此,如果在同一个Web应用中只有部分页面使用SSL,要保证使用SSL的页面与不使用SSL的页面间的相互切换(也就是HTTPS请求与HTTP请求间的切换)会话保持连续,那么可以通过在访问的URL中传递sessionId来实现,也就是说在进入或退出HTTPS的URL上绑定一个sessionId。比如从HTTP切换到HTTPS时,URL为https://example.com/login.do;jsessionid=<%=session.getId()%>,从HTTPS切换到HTTP时为http://example.com/welcome.do;jsessionid=<%=session.getId()%>。这样Web容器会根据这个sessionId获取session对象,而不是生成新的session对象。就可以保证HTTP和HTTPS切换时会话不变。

由于在URL上绑定的sessionId容易被窃取,为了保证用户安全,会话认证时需要结合客户端IP地址。即当用户登录后,通过session.setAttribute("clientIp", request.getRemoteAddr())保存客户端的IP地址,在后继认证会话的合法性时判断客户端的IP是否是原先存储在session对象的clientIP属性的IP地址,如果不是则该会话是非法会话。

希望能在Java 7中看到的特性

1、支持@前缀的字符串,这样就不必再为到底是写几个反斜杠绞尽脑汁了。

2、支持String类型的switch。

3、支持静态equals方法的String类或扩展方法(Extension methods)。不明白为什么Java总是使劲地支持一些丑陋又复杂的语法。像比较两个字符串就偏偏要写成这种样式:

1
if (str1 != null && !str1.equals(str2))

难道就不能写成下面的格式吗?

1
if (str1 != str2)

1
if (!String.equals(str1, str2))

其中str1或str2中任何一个值为null时返回false。

4、对泛型进行一些适当的修正。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GenericsTest {

//    public void output(List<String> list) {
//        System.out.println("List<String>");
//    }

    public void output(List<Object> list) {
        System.out.println("List<Object>");
    }

    public static void main(String[] args) {
        GenericsTest gt = new GenericsTest();

        gt.output(new ArrayList<Object>());
        gt.output(new ArrayList<String>());    // 竟然不能通过编译,想不通!
    }
}

5、可以通过[]来访问List和Map。然后,下面的代码

1
2
3
4
List content = new LinkedList(10);
content.add(0, "Fred");
content.add(1, "Barney");
String name = content.get(0);

就可以写成

1
2
3
4
List content = new LinkedList(10);
content[0] = "Fred";
content[1] = "Barney";
String name = content[0];

还有可能的就是允许为列表使用数组初始化程序语法。例如:

1
LinkedList content = {"Fred", "Barney", "Wilma", "Betty"};

6、添加decimal或money关键字作为原生类型,或者使BigDecimal支持+,-,*,/等运算符。处理金融相关的数据时总是要使用到BigDecimal类型。可由于BigDecimal不支持运算符,在比较大小时就会很繁琐。例如,比较两个金额的大小:

1
2
3
4
BigDecimal zero = new BigDecimal(0);
if (amount.compareTo(zero) > 0) {
  return true;
}

7、支持ref/out关键字或支持返回Tuples。例如:

1
2
3
4
(int, int, int) transform(int x, int y, int z)

(r, g, b) = transform(x, y, z);
(l, a, b) = transform(transform(x, y, z));

当某个方法中有多个返回值或方法需要修改传递进来的String参数并返回修改后的值时,当前的Java语法处理起来就会比较麻烦。

8、重新整理简化I/O和Collection中的API,使之更易使用。

9、简单而强大的Date和Time实现,将java.util包中的Date和Calendar类合并成一个。

如何使文字与图标垂直居中对齐

在做网页的时候,经常会需要在某段文字前加上一个图标。然后就会发现增加的图标和文字的位置不齐,文字总是比图标低点。

smiley-smile微笑

解决的方法有两个:一个是设置图标的vertical-align为top;还有就是将margin-bottom设为-3px。

1
2
<img style="vertical-align: top" src="/uploads/smiley-smile.gif" border="0" />微笑
<img style="margin-bottom: -3px" src="/uploads/smiley-smile.gif" border="0" />微笑

实现自己的Java Annotation

Annotation类型的声明与一般的接口声明极其相似,区别只在于它在interface关键字前面使用“@”符号。下面就是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Event {
}

public class EventAnonationTest {
    @Event
    public void clicked() {
    }
}

@Target指定可以应用Annotation类型的程序元素,以防止在其它程序元素中误用Annotation类型:

1
2
3
4
5
6
7
8
9
10
public enum java.lang.annotation.ElementType {
    TYPE,               // Class, interface, or enum (but not annotation)
    FIELD,              // Field (including enumerated values)
    METHOD,             // Method (does not include constructors)
    PARAMETER,          // Method parameter
    CONSTRUCTOR,        // Constructor
    LOCAL_VARIABLE,     // Local variable or catch clause
    ANNOTATION_TYPE,    // Annotation Types (meta-annotations)
    PACKAGE             // Java package
}

@Retention设置Java编译器处理Annotation类型的方式:

1
2
3
4
5
public enum java.lang.annotation.RetentionPolicy {
    SOURCE,     // Annotation is discarded by the compiler
    CLASS,      // Annotation is stored in the class file, but ignored by the VM
    RUNTIME     // Annotation is stored in the class file and read by the VM
}

@Documented指明需要在Javadoc中包含Annotation(缺省是不包含的)。使用@Documented的一个技巧就是指定Retention为RetentionPolicy.RUNTIME。这样,Annotation就会保留在编译后的类文件中并且由虚拟机加载,然后Javadoc就可以抽取出Annotation并添加到类的HTML文档中。

@Inherited定义了Annotation类型的修饰是否可以由被修饰类的子类继承。

wxWidgets Wizard for Visual Studio 2005/2008

花了点时间为VS2005/VS2008写了一个简单的wxWidgets Wizard。本来想写的更完善一些再发布出来的,只是实在没有多少心思再继续地写下去了。而且该向导程序已经基本可以满足我自己的要求了。不过,如果以后有时间的话还是会继续完善它的。以下是我原本打算要实现的一些功能:

  1. 支持创建对话框程序;
  2. 可以直接安装wxWidgets Wizard到VS2005中的安装程序;
  3. 集成wxWidgets Help文档到VS2005中;
  4. 实现代码智能提示功能;
  5. 可以在VS2005中直接编辑和编译XRC资源;
  6. 在向导过程中可以设置一些wxWidgets的高级特性(比如Menu Bar,Status Bar等)。

截图:

wxwizard-default

wxwizard-apptype

wxwizard-generated-classes

代码下载:https://github.com/dohkoos/wxwizard