乐者为王

Do one thing, and do it well.

Struts 2登录实例

新的Eclipse已经包含Maven插件了,不过还需要到Eclipse Marketplace中去安装一个m2-wtp(Maven Integeration for Eclipse WTP)插件,不然启动Tomcat时会报错。

安装好后创建新Maven项目,填写groupId和artifactId。

在pom.xml中加入Struts 2的依赖配置:

1
2
3
4
5
6
7
<dependencies>
    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-core</artifactId>
        <version>2.3.4</version>
    </dependency>
</dependencies>

在web.xml文件中添加Struts 2的Filter:

1
2
3
4
5
6
7
8
9
10
11
<filter>
    <filter-name>struts2</filter-name>
    <filter-class>
        org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    </filter-class>
</filter>

<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

配置resources/struts.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
<struts>
    <package name="default" extends="struts-default">
        <action name="login" class="com.codemany.account.action.LoginAction">
            <result name="success">/index.jsp</result>
            <result name="input">/index.jsp</result>
        </action>

        <action name="logout" class="com.codemany.account.action.LogoutAction">
            <result name="success" type="redirect">/index.jsp</result>
        </action>
    </package>
</struts>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.codemany.account.action;

import com.codemany.account.model.User;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {
    private static final long serialVersionUID = -389833745243649130L;

    private String username;
    private String password;

    public String execute() throws Exception {
        if (username == null || username.length() != 0
                || password == null || password.length() != 0) {
            return INPUT;
        }

        if ("test".equals(username) && "test".equals(password)) {
            ActionContext.getContext().getSession().put("logined", true);
            return SUCCESS;
        }

        return INPUT;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.codemany.account.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class LogoutAction extends ActionSupport {
    private static final long serialVersionUID = -518996422060716751L;

    public String execute() throws Exception {
        ActionContext.getContext().getSession().put("logined", false);
        return SUCCESS;
    }
}

index.jsp内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>

<%@ taglib uri="/struts-tags" prefix="s" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Struts2 Login Sample</title>
</head>

<body>
    <s:if test="#session.logined">
        Welcome, you have logined. <a href="<%= request.getContextPath() %>/logout.action">Logout</a>
    </s:if>
    <s:else>
        <p>User: test/test</p>
        <s:form action="login">
            <s:textfield key="username" label="Username" />
            <s:password key="password" label="Password" />
            <s:submit value="Login" />
        </s:form>
    </s:else>
</body>
</html>

在项目上点击右键 -> Run As -> Maven build运行项目。在Goals栏中填写tomcat:run,Maven会自动下载Tomcat到项目的target目录下后启动服务器。

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

使用Ruby批量修改繁体文件名为简体

首先是遍历目录所有文件,使用了 http://beike.iteye.com/blog/361108 的代码,稍微做了下修改。主要是在Windows下处理系统目录时会停止遍历,加上了异常处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def traverse(path)
  begin
    if File.directory?(path)
      Dir.foreach(path) do |file|
        if file != "." and file != ".."
          traverse(path + "/" + file) {|x| yield x}
        end
      end
    else
      yield path
    end
  rescue
    puts "Error: #{$!}"
  end
end

如果不输入文件夹路径,就使用当前目录作为根目录:

1
root = ARGV[0] || Dir.pwd

将繁体中文改成简体的代码:

1
2
3
4
5
6
7
mapping = {}
mapping["無"] = "无"
mapping["龍"] = "龙"

mapping.each do |key, value|
  file.gsub!(key.encode('gbk', 'utf-8'), value.encode('gbk', 'utf-8'))
end

文件重命名:

1
File.rename(old_file_name, new_file_name)

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

CSS命名规范

收集了网上的一些CSS命名规范,根据自己的理解和需求整理了一遍。

布局(layout)

  • container 就是将页面中的所有元素包在一起的部分
  • header 页面的头部区域,包含站点的logo和一些其它元素
  • navbar 横向导航栏,最典型的网页元素
  • breadcrumbs 即页面所处位置的导航提示
  • content 是站点的主要区域
  • sidebar
  • footer

头部(header)

  • logo
  • slogan

导航栏(navbar)

  • mainnav
  • subnav
  • topnav

侧边栏(sidebar)

  • menu 包含一般的链接。sidebar中可以包含多个menu

常用

  • userbar 登录注册条
  • copyright
  • banner
  • feature

我写的二分查找算法有bug!

看完 http://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/ 觉得很不服气,写个二分查找算法还不简单吗?分分钟搞定的事情。

按照它的要求,用Java写出以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int binarySearch(int[] array, int value) {
    int low = 0;
    int high = array.length - 1;
    int mid;
    while (low <= high) {
        mid = (low + high) / 2;
        if (array[mid] < value) {
            low = mid + 1;
        } else if (array[mid] > value) {
            high = mid - 1;
        } else {
            return mid;
        }
    }
    return -1;
}

把代码贴到评论区参加测试。

写代码和检查总共花费20分钟,75%的时间是用来在脑袋里模拟运算,防止数组索引溢出。然后编译2次,消除了两个语法错误(好久没写Java代码和使用Ruby的关系,竟然连个数组初始化都不会写了)。也想过数值溢出的问题,但是没想到点子上。结果恰恰倒在了这个地方。具体原因在 http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html 有描述。因为low + high的和会形成数值溢出,解决方法是采用:

1
int mid = low + ((high - low) / 2);

1
int mid = (low + high) >>> 1;

如何破解中兴ZXA10-F460 v3.0获取超级管理员密码

破解前的系统情况:

  • 硬件版本号 V3.0
  • 软件版本号 F460_IMS_V2.30.10P1T6_JS1111
  • BOOT版本号 V1.1.0P1T1
  • 版本日期 2012-02-29 16:29:07

中兴ZXA10-F460光猫(E8-C)在第一次安装的时候,超级管理员帐号是telecomadmin,密码为nE7jA%5m,这个是默认的,由安装人员设置好相关设置后,会自动下载配置,这时候这个超级管理员密码会被修改为telecomadminXXXXXXXX(8个X是8位数字,每个用户是不同的)这种格式。因为很多设置需要在超级管理员下才能设置,所以破解这个超级管理员密码是非常必要的。

其中V1.0和V2.0版本网上有破解方法,大家可以自己搜索。

在命令行下输入telnet 192.168.1.1,帐号和密码都是root。连接成功后,会出现“F460 login:”这样的提示。

注意:目前网上流传的查看/tmp目录下db_backup_cfg.xml文件内容的破解方法已经无效了。另外在/userconfig/cfg目录下有两个加密的配置文件db_backup_cfg.xml和db_user_cfg.xml,同目录下还有个db_default_cfg.xml是不加密的(该文件是出厂设置的配置文件),但是大家不要用这个文件去替换db_user_cfg.xml,否则会出现不可预知的后果。

实际上只要修改一下/home/httpd/login.gch这个文件,就能看到密码了。patch文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--- /home/httpd/login.gch 2012-07-03 14:05:16.000000000 +0800
+++ login.gch 2012-07-03 14:03:37.000000000 +0800
@@ -152,6 +152,17 @@
set_language("langcn.conf");
langclass = "login_title_centeren";
}
+var CK_HANDLE = create_paralist();
+var login_name = "telecomadmin";
+set_para(CK_HANDLE, "Username", login_name);
+qeury_list_bycond("OBJ_USERINFO_ID", "IGD", CK_HANDLE);
+destroy_paralist(CK_HANDLE);
+CK_HANDLE = create_paralist();
+var CK_IDENTITY = query_identity(0);
+get_inst(CK_HANDLE, "OBJ_USERINFO_ID", CK_IDENTITY);
+var now_pwd = get_para(CK_HANDLE, "Password");
+now_pwd = delMoreSlash(now_pwd);
+destroy_paralist(CK_HANDLE);
%>
<head>
<META HTTP-EQUIV="pragma" CONTENT="no-cache>
@@ -239,4 +250,5 @@
</script>
</head>
<body onload="onFocus();">
+<div><%=now_pwd;%></div>
<div id="container" class="content">

效果是浏览器登录192.168.1.1的时候,超级管理员密码会显示在首页界面的左上角。

到这儿有人想了,怎么把上面的patch更新到/home/httpd/login.gch中呢?总结了三种方法:

第一种方法是telent到F460上后,执行下面的命令即可:

1
2
3
4
5
6
7
8
9
10
11
12
sed -i '155 s/^/var CK_HANDLE = create_paralist();\n/' /home/httpd/login.gch
sed -i '156 s/^/var login_name = "telecomadmin";\n/' /home/httpd/login.gch
sed -i '157 s/^/set_para(CK_HANDLE, "Username", login_name);\n/' /home/httpd/login.gch
sed -i '158 s/^/qeury_list_bycond("OBJ_USERINFO_ID", "IGD", CK_HANDLE);\n/' /home/httpd/login.gch
sed -i '159 s/^/destroy_paralist(CK_HANDLE);\n/' /home/httpd/login.gch
sed -i '160 s/^/CK_HANDLE = create_paralist();\n/' /home/httpd/login.gch
sed -i '161 s/^/var CK_IDENTITY = query_identity(0);\n/' /home/httpd/login.gch
sed -i '162 s/^/get_inst(CK_HANDLE, "OBJ_USERINFO_ID", CK_IDENTITY);\n/' /home/httpd/login.gch
sed -i '163 s/^/var now_pwd = get_para(CK_HANDLE, "Password");\n/' /home/httpd/login.gch
sed -i '164 s/^/now_pwd = delMoreSlash(now_pwd);\n/' /home/httpd/login.gch
sed -i '165 s/^/destroy_paralist(CK_HANDLE);\n/' /home/httpd/login.gch
sed -i '253 s/^/<div><%=now_pwd;%><\/div>\n/' /home/httpd/login.gch

第二种方法是telent到F460上后,执行cp /home/httpd/login.gch /home/httpd/login_gch.xml,通过浏览器把login_gch.xml保存到电脑上,然后再把patch更新进去。可以在电脑上架设ftp服务器,在F460上执行以下命令:

1
2
cd /home/httpd/
wget ftp://<YOUR_COMPUTER_IP>/login.gch  # 把<YOUR_COMPUTER_IP>替换成你自己的

第三种方法是telent到F460上后,执行df命令,查看是否有usb1_1或usb2_1,如果有就可以进行下一步操作。输入cp /home/httpd/login.gch /mnt/usb1_1/login.gch把文件拷贝到优盘上,修改后使用cp /mnt/usb1_1/login.gch /home/httpd/login.gch把文件更新到猫上就可以。

2013/6/16更新

其实还有种更简便的方法,中兴的光猫有个sendcmd命令,下面是该命令的简单用法:

  1. 读取全部表名 sendcmd 1 DB all;
  2. 读取指定表信息 sendcmd 1 DB p 表名。比如sendcmd 1 DB p UserInfo,查看路由器帐号和密码,超级管理员密码也在这里查看;
  3. 查看当前系统运行的服务状态 sendcmd -pc show;
  4. 修改某个表的某个字段的值 sendcmd 1 DB set 表名 行数 字段名 字段值。比如要修改超级管理员密码,可以使用sendcmd 1 DB set UserInfo 0 Password 123456;
  5. 保存对配置文件的修改 sendcmd 1 DB save。

当然还有其它的命令,但是就修改光猫配置来说,这几条命令足够了。

SQL连接总结

连接(join)可以在SELECT语句的FROM子句或WHERE子句中建立,在FROM子句中指出连接时有助于将连接操作与WHERE子句中的搜索条件区分开来。连接可以对同一个表操作,也可以对多个表操作,对同一个表操作的连接称为自连接(self join)。

连接类型(join type)分为三种:内连接、外连接和交叉连接。

  • 内连接(inner join):使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。
  • 外连接(outer join):与内连接不同的是外连接不只列出与连接条件相匹配的行,而且列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合条件的数据行。
  • 交叉连接(cross join):返回连接表中所有数据行的笛卡尔积。

内/外链接可以分为等值连接和不等连接:

  • 等值连接(equijoin):在连接条件中使用等于(=)运算符比较被连接列的列值,其查询结果中列出被连接表中的所有列,包括其中的重复列。
  • 不等连接(non-equijoin):在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。

注意:自然连接(natural join)是一种特殊的等值连接。它使用选择列表指出查询结果中所包括的列,由此删除连接表中的重复列。

books数据表:

books-table

publishers数据表:

publishers-table

自连接查询:

1
SELECT * FROM books b1 [INNER] JOIN books b2 ON b1.price = b2.price

self-join

内连接(简称连接)查询:

1
SELECT * FROM books b [INNER] JOIN publishers p ON b.publisher_id = p.publisher_id

inner-join

自然连接查询:

1
SELECT b.*, p.name FROM books b [INNER] JOIN publishers p ON b.publisher_id = p.publisher_id

natural-join

左外连接(简称左连接)查询:

1
SELECT * FROM books b LEFT [OUTER] JOIN publishers p ON b.publisher_id = p.publisher_id

left-outer-join

右外连接(简称右连接)查询:

1
SELECT * FROM books b RIGHT [OUTER] JOIN publishers p ON b.publisher_id = p.publisher_id

right-outer-join

全外连接(简称全连接)查询:

1
SELECT * FROM books b FULL [OUTER] JOIN publishers p ON b.publisher_id = p.publisher_id

full-outer-join

交叉连接查询:

1
SELECT * FROM books CROSS JOIN publishers

cross-join

下图是由Moffatt在2008年制作的连接集合表示:

sql-joins

在创业和生活中间你需要计划A、B和Z

英文原文:https://techcrunch.com/2012/02/14/in-startups-and-life-you-need-plan-a-b-and-z/

创业者总会从真正聪明的、有经验的人士那里得到很多矛盾的建议。例如,你可能被告知既要坚持又要灵活;要有一个你不懈地追求的清晰愿景,但是也要随着市场的变化而改变你的愿景。简单,对吧?

同样的矛盾在职业咨询当中盛行。有些人会告诉你,要想清楚十年后你希望在哪里,然后通过逆向推导,建立一个实现你的雄心的长期职业规划。其他人告诉你,坚定的计划就像一种桎梏:它们会让你错失意想不到的突破机会。他们说,最好是保持随机应变。

谁是对的呢?两者不仅都是对的,而且至关重要。创业者需要灵活性地坚持。我曾经共事过的优秀创业者忙于重要的规划与战略,但他们不会设置固定的计划。在我和Ben Casnocha的新书《The Start-Up of You: Adapt to the Future, Invest in Yourself, and Transform Your Career》中,我们展示了任何专业人士如何将创业技巧应用于他们的职业,即使你从未计划去求职于一家公司。

我参与过一些在它们初创时你不会认可的公司。尤其是作为很好例子的两个,因为它们都有着巨大的影响力:Flickr和PayPal。Flickr开始时是一个多玩家在线游戏,之后渐变为最广泛使用的照片托管和分享网站之一。PayPal开始时是移动电话的加密平台,然后成为Palm Pilot之间的转钱服务,最终成为领先的在线支付公司。两者都坚持它们最初的愿景,以求学习和成长:Flickr作为一种社交体验和PayPal是安全的现金。然而,正是因为有着极大的弹性和锲而不舍,两家公司都彻底地改变了产品的类型并最终获得用户的青睐。

在创业或职业生涯中,你是如何做到灵活地坚持的?这里是我使用的一个框架:ABZ计划。在事业和生活中,你应该有3个计划:A计划、B计划和Z计划。

A计划是你当前的计划,你目前的重点是怎样赢得市场。对于Flickr的联合创始人Caterina FakeStewart Butterfield而言,最初的A计划是Game Neverending,一个多玩家在线游戏。不同于当时的大多数游戏只能通过固定的体验在几个对手之间进行,他们希望他们的游戏能有数以百计的用户同时玩,并且永远地在游戏中创造新的事物。为了吸引住用户,他们构建了像群组、即时通讯这样的社交功能,最关键的功能是允许玩家彼此之间分享照片。

我最初的职业A计划是致力于学术界,因为我认为传播什么是造就一个良好社会的思想是对这个世界造成影响的最好方式。当我在牛津学习时,我学到很多关于人们如何聚在一起,在群组间互动和与社会产生关联。但是我也了解到,学术界的职业成功往往意味着写出只有50多人读过的专业著作。

B计划是当你意识到一个新机会比你所从事的事情拥有更多潜力时转向的计划。有时候你转变为B计划是因为A计划并不奏效,这是大多数人听到“B计划”时所想的。但有时候A计划还能正常工作,然而B计划似乎拥有更多潜力。不管转移的原因是什么,最好的B计划是虽有不同,但却和你现在正在做的事情有关。这样,你可以把你已经学到的经验和教训用于新计划。

对照到Flickr,出乎意料地,照片分享功能受欢迎的程度超越了游戏本身。Caterina和Stewart面临一个抉择:他们应该坚持他们的A计划还是暂时中止游戏(和2万狂热用户)以便完全专注于照片分享功能?最终他们转移到B计划。当然,Caterina和Stewart仍然遵循最初的想法来构建一个在线的社交空间——他们只是看到照片分享比游戏有更大的潜力。

在我的职业生涯中,我对学术界的认识导致我转向B计划,找到具有更广泛影响力的职业路径。我的B计划是创建新的软件。软件行业的成功也意味着“影响力”——但是比学术界有更广泛的规模。在某些情况下,它意味着构建一种每天改善数百万人生活的产品。为追求这条替代路线,我首先通过在苹果和富士通的在线部门工作来专注于建立相关技能和关系。其次,我与可以共同创建一家我自己的公司的人联系。然后,当我开始我的第一家公司时,我招募了许多聪明的顾问和参与者,以便快速地学习和调整。而且,就公司规划方面来说,由于我的第一家公司(SocialNet)不成功,PayPal和LinkedIn都是我自己转向的新的B计划。

请记住,你应该很少写下一个明确的B计划,但是你应该总是在你执行你的A计划时注意你的动态参数。你应该考虑“临近可能性”。你的可转换的技能。在视野内的其它机会。

Z计划有两个关键的部分。首先,确认如何衡量你正在朝着最坏的情况发展,其次,它是告诉你最坏的情况发生时应该做什么。也许当你的信用卡债务膨胀到一定数量时,你会提前支取你的401k养老金,或者在星巴克找份工作。Z计划最后担保的确定性就是让你在职业生涯中承担各种不确定性和风险。当我开始我的第一家公司SocialNet时,我的父母在他们的住宅为我提供了一间屋子,以防事情没有成功。住在那里然后找其它的工作是我的Z计划。它让我有信心全身心地投入到事业中,因为我知道就算一切都没有了,我也不至于沦落街头。为了可以重新开始,你需要在失败中存活。

TechCrunch读者每天都会看到一些成功公司的适应性路径。然而,很少有人会将这种适应性手册应用于自己的生活。像ABZ规划这样的框架可以帮助你掌控你的职业生涯。我们应该记住的是,无论我们开始创业,在初创公司工作,或者在大公司工作:最终的创业者都是你。

如何避免点击链接下载文件时浏览器自动打开文件

HTTP Response Header有个Content-Disposition参数,该参数的目的是为了在客户端另存文件时提供一个建议的文件名。将它的值设置为attachment后,浏览器就会老老实实地显示另存为对话框,如果这个值设成inline,则无论怎样浏览器都会自动尝试用已知关联的程序打开文件。

注意:在Firefox中需要把filename用双引号包起来才能得到想要的文件名,不然如果含有空格就会丢掉空格后面的部分,而IE会把空格转为下划线。

Java设置header代码:

1
res.addHeader("Content-Disposition", "attachment; filename=" + res.encodeURL(filename));

Ruby设置header代码:

1
headers['Content-Disposition'] = "attachment; filename=#{filename}"

如何避免项目总是完成90%

程序员经常会说“功能已经基本完成,接下来只需要花点时间进行扫尾工作即可,但这些基本都是些小事情,我已经完成了90%。”但实际情况是项目进度总是延迟。这里最最根本的问题是什么?这个程序员并没有一个清单,把自己要做的所有事情都列出来。这就意味着,即使他坚信自己已经完成90%的工作,他还是不知道这个项目什么时候可以做完,他的时间表是没有真实依据的。

一个好的软件项目经理的任务就是,鼓励并且强制要求程序员创建一张他们所要做的全部事情的列表。然后再为其中的每一项列出子项,并且尽可能把所有的子项都加进来。一旦你拥有了这么一个包含所有事项的列表,你就可以开始估算这个任务需要花费多少时间了。

以下是避免“总是完成90%”问题的发生的几点建议:

  1. 把项目中所有要做的事情全部罗列出来做成列表。
  2. 估算这个列表中每一项所需要花费的时间。这种最初的估算可以帮助你知道整个项目大致要花费多少时间。
  3. 如果列表中有一项的时间花费超过1天,则把这项拆分成若干小项。这种将大任务拆分成小任务的方式是解决“总是完成90%”问题的关键步骤。
  4. 使用一种呈现任务状态的方式,以便那些感兴趣的人可以了解项目进度。
  5. 既然你已经拥有很多小的任务,并且这些任务所消耗的时间都在1天以内,你就可以每天追踪自己的任务完成情况。

改写Rails 2.x generator到Rails 3.x时的一些记录

因为有个基于Rails 2.x的项目用到了feedback插件,在把项目迁移到Rails 3.x版本后,需要让feedback也支持Rails 3.x。但是原作者早已不再更新该插件,所以只能自己动手。花了两天时间查找资料,修改代码,终于完成。

Rails 3.x中生成generator可用下面的命令:

1
2
3
4
5
rails g generator feedback_form
      create  lib/generators/feedback_form
      create  lib/generators/feedback_form/feedback_form_generator.rb
      create  lib/generators/feedback_form/USAGE
      create  lib/generators/feedback_form/templates
1
2
3
class InitializerGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("../templates", __FILE__)
end

生成基类也从Rails::Generators::Base变成了Rails::Generators::NamedBase。它们的区别是Rails::Generators::NamedBase期望至少一个参数。

USAGE文件里的内容是输入-h选项时显示的文档:

1
2
3
rails generate feedback_form --help
Usage:
  rails generate feedback_form NAME [options]

Rails 2.x中的manifest可以删除了,因为在3.x的generator中每个public方法在生成过程中会被自动调用。