收藏到: Del.icio.us Google书签 Digg Live Bookmark Technorati Furl Yahoo书签 Facebook 百度搜藏 新浪ViVi 365Key网摘 天极网摘 和讯网摘 博拉网 POCO网摘 添加到饭否 QQ书签 Digbuzz我挖网 QQ书签 更多 Bookmark and Share

2009年4月29日星期三

性能监控 以及 开发自动化 -- Java

前一阵在找性能监控的工具,自己对Java比较关注点,因此在java上找到一些工具,比较著名的就是JProfiler,在Java1.5以后,在JDK中也带有一些性能监控的工具,如jconsole等,还有一些开源的产品,但是JProfiler分析的程度比较深,因此使用的效果相对好点。
(十个最好的Java性能故障排除工具 : http://www.kuqin.com/developtool/20080721/11869.html

在进行性能监控工具的查找中,发现问题的根源很多时候还是出在开发上面,在开发时对性能不重视,以为Java有内存自动回收(gc)就万无一失了,对Java的内存分配方式、回收方式都不去了解它,造成开发出来的程序问题一大堆。

网上看到IBM有一个开发自动化的专题(以前看到过,但是没有这次这么有感触),要仔细的揣摩一下

  • CheckStyle 报告与项目预定的编码标准的偏离度。
  • CPD 报告代码重复。
  • JavaNCSS 可以帮助团队专注于更高级的代码复杂性领域。



2009年4月27日星期一

Tomcat遇到了“java.lang.OutOfMemoryError: PermGen space”

Tomcat中新增了一些功能后,原来正常的程序,在运行一段后,发生了一下错误:
Exception in thread "TP-Processor171" java.lang.OutOfMemoryError: PermGen space
java.lang.OutOfMemoryError: PermGen space

PermGen内存溢出。根据网上查了一下资料,发现这是sun JDK分代管理的一个问题 --- 当然,程序使用上也有讲究,但是程序上只能尽量避开这个问题,说解决的方式更换JDK,换成BEA JRokit。

Permanent Generation space,是指内存的永久保存区域,用于存放ClassMeta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放InstanceHeap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APPLOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。 如果你的WEB APP下都用了大量的第三方jar, 其大小 超过了jvm默认的大小(4M-- 这个值各个地方写的不一样,没有搞懂,要确切,到JDK提供的官方去查)那么就会产生此错误信息了。 


以下是网上的一些资料,
---------------------------------------------------
问题已经查明,这个和程序编写有关系。

当初不知道谁写的代码,当hibernate发生错误的时候,就重构一个hibernate的factory,每发生一次错误,就重构一次。这样每次重构的时候,都会造成PermGen的增长。把这个问题解决了,PermGen Size也没有溢出过。





性能调试、测试工具

性能对版本是否能给客户带来比较好的感受、是否能降低整个系统的投资,占据着相当重要的地位。

有一个版本即将发布,在讨论时,对于程序的性能提了一下,在这里顺便做一下总结,对公司目前的所有产品,都考虑做一下性能的测试,以提高产品的服务能力。

从公司开发的角度、测试的角度来看,大致需要以下一些调试、测试工具:

  1. Windows C++的
  2. Linux C++的
  3. Java的
  4. C#的
  5. 数据库的(Oracle & MySQL)
  6. Web性能的
    Web性能还包括安全方面的测试工具
  7. 操作系统的(以Linux为主)
  8. 网络方面的
估计就是上面一些(可能会有遗漏),对以上的每一项,都需要多一下单独的讨论--- 自己比较关心Java性能工具、Web性能工具、数据库性能工具以及操作系统的性能工具(这个应该每个人都会)

性能调试、测试工具,不仅需要了解机器的整体的性能,对于每个开发人员,必须要了解自己开发的程序的性能,单个进程的性能(目前还没有多进程程序)


2009年4月22日星期三

BugFree 2.0.2 指派人员支持多个邮件发送

测试小组,每人负责某几个产品,开发人员要知道什么产品谁负责也是比较讨厌的。一个开发人员同时参与几个产品的开发(不同产品有不同的人员负责测试),而且开发人员更换的有时也比较快,又要重新培训也比较麻烦。因此一开始规定,将所有的测试人员邮件编辑成一个小组,开发人员版本提交的时候,发送到这个测试小组。测试人员收到邮件,如果是自己负责测试的产品就进行测试。


一段时间用下来,平安无事。有一次客户又提出相同的bug,这个问题早已经解决过了,怎么客户还提这个问题。一路检查下来,发现公司的邮件不正常。

原来开发人员发送邮件,发送给测试小组,测试小组相关人员收到邮件后会进行处理。但是现在负责该产品的测试人员没有收到这个邮件,造成了整个环节上的脱钩,无法联系起来。因此发送邮件通知版本发布还是有问题。现在内部的bug测试使用的是bugFree2.0.2,因此想使用bugfree进行版本提交,这样即使有邮件丢失,测试人员也可以通过检查bugfree工作记录重新拾起来。

原本考虑使用bugfree发送邮件,发送到一个邮箱,测试人员都通过IMAP方式接收这个邮箱的邮件,但是公司内部使用的邮件服务器不支持IMAP服务(shit),只能通过修改BugFree来进行了。

BugFree用的是Php开发的,从未接触过PHP,不过这些脚本式的语言应该可以依样画葫芦来进行处理。
需要修改的地方有:
1. 增加用户,可以输入多个邮件地址
2.修改用户: 可以输入多个邮件地址
3.可以往多个地址发送邮件

看了一下Bugfree的源码,发现一个比较有趣的地方(觉得有趣,估计bugfree的一些功能根本没有使用到):
1. 增加用户,检查邮件使用的是函数:sysCheckEmailFormat
2.修改用户:发现直接在修改用户的函数中设置了正则表达式进行验证:xAdminEditUser
3.发送邮件:在sysMail函数中,支持用逗号分割多个邮件地址。

既然找出来的这几个地方,就依样画葫芦进行修改(谁叫自己不懂PHP)
找到:sysCheckEmailFormat,将原来直接验证邮件的地方进行修改:
  原来: if(!eregi("^[ ......." , $EmailStr)
  修改成: $EmailList = explode(',', $EmailStr);
                   foreach($EmailList as $Singlemail)
                   {
                       if(!eregi("^[ ......." , $Singlemail) ....
                   }
      
修改用户信息的地方,将直接判断邮件的地方改成用sysCheckEmailFormat进行验证就可以了。

发送邮件的地方看了一下,在接收者的地方没有问题,Bugfree通过逗号进行了分割。但是抄送的地方没有进行判断,在CC的地方按照一样的方式,对每个人的邮件按照逗号进行分割,重新获取邮件地址。

----------------------------------------------------
学习到的知识:
1. PHP中的explode:将字符串变成数组,按照指定的字符进行分割
2. 函数array_diff/array_unique,数组的方法,进行数据判别
3. PHP的输出:在调试过程中,发生错误,但是不知道如何调试,找到的资料:
    通过syslog(LOG_NOTICE, 输出的内容)
    可以在/var/log/message中输出调试信息
4.jsAlert:直接在页面上显示信息出来,没有仔细看这是Bugfree自己写的方法还是php的方法


-----------------------------------
调试过程中,遇到的问题:
1. 原来以为通过分号分割用户的邮件地址,就可以向多个地方进行发送(类似于outlook等发送给多人的方式,实际使用下来不行,后来看到sysMail中用逗号分割,全部改成了逗号)
2. 获取调试信息:
    修改了代码,但是结果不对,无法看到调试信息,上网找了一些资料,自己当前情况最方便的就是用syslog方式输出调试信息

2009年4月18日星期六

Gear Factory

The Factory class is used to instantiate all other Gears objects. Using the create method, an application specifies the interface version it uses.

Code Example:
/ Check whether Gears is installed. if (window.google && google.gears) {   // Instantiate Gears objects   var db = google.gears.factory.create('beta.database');   db.open(); }

The supported class names are:

classNameGears class created
beta.databaseDatabase
beta.desktopDesktop
beta.geolocationGeolocation
beta.httprequestHttpRequest
beta.localserverLocalServer
beta.timerTimer
beta.workerpoolWorkerPool




Gear Database API 使用

Database API 提供铜鼓JavaScript使用的本地浏览器可以使用的数据。Gears使用SQLite数据库系统。

数据存储使用 same-origin security policy,意思是在这个domian之外的web应用无法访问到这个数据。Gears包括SQLite full-text-search extension fts2.

安全考虑:
  SQL语句通过execute()执行,并且应该用绑定参数的方式避免 SQL注入攻击(SQL injection attacks)。

许可:
  这些API要求用户许可。如果你要客户化缺省的dialog,你要显示调用google.gears.factory.getPermission()

Classes:
Database
ResultSet

修改本地SQLite库
SQLite通过Attache和Detach命令使用本地SQLite库。出于安全考虑,Gears禁止使用这些命令。将来在满足same-origin security policy情况下可以使用这些功能。

Pragma设置
  SQLite的Pragma命令允许在不同的平台下进行一些设置。出于安全考虑,目前禁止使用。
  Gears使用的Pragma设置:
  Pragma encoding=“UTF-8”
  Pragma auto_vacuum=1
  Pragma page_size=4096
  Pragma cache_size=2048
  Pragma synchronous=NORMAL
 
Full-Text Search
  Gears 包含 SQLite extension called fts2, for "Full-Text Search". fts2允许创建表,搜索Text数据。 fts2表创建:
 db.execute('CREATE VIRTUAL TABLE recipe USING fts2(dish, ingredients)');
 创建表recipe,字段dish、ingredients。所有fts2字段都是TEXT类型。表中的数据通过标准的SQL命令操作,如:INSERT/UPDATE/DELETE,和其他的表一样。fts2表有一个隐含的rowid字段,表现向唯一索引。

------------------------------------------------------------------
基本使用:

< type="text/javascript" src="gears_init.js">< /script>
< type="text/javascript">
var db = google.gears.factory.create('beta.database');
db.open('database-test');  // 创建数据库
db.execute('create table if not exists Test' +' (Phrase text, Timestamp int)');  // 创建表
db.execute('insert into Test values (?, ?)', ['Monkey!', new Date().getTime()]); // 插入数据
var rs = db.execute('select * from Test order by Timestamp desc');  // 查询

while (rs.isValidRow()) { // 是否到记录结束
  alert(rs.field(0) + '@' + rs.field(1));  // 获取字段值
  rs.next();  // 移动到下一条记录
}
rs.close();  // 关闭数据库
< /script>



Gear 体系结构

开发Gears程序,要按照离线程序体系结构开发做一些改变,

独立的数据层(Isloating the Data Layer)
普通的应用没有真正的数据层:



第一步,要独立数据层:

例如:如果Ajax也能够用通过JSON请求获取信息,要改成通过一个中间对象来获取数据。
这个对象决定是从服务器上、本地、或者同时从这2者中获取数据。

也可以考虑这个中间对象作为 data switch, 实现 Data Layer的接口
第一步通过这个data switch向Data Layer获取数据

第二步:如果增加了本地数据层,有Data switch进行切换:



Modality(特征?形式?)
最基本的离线使用的问题就是要确定Modality
  • Modal Application:相对于Online Modes,是很明确地离线使用,通过用户界面可以表现出来。用户很清楚地知道现在的使用模式,手工进行切换
  • Modeless Application:在online Mode和Offline Mode使用对用户是透明的,界面没有什么变化。用户不需要切换状态,是程序自动完成的。
Modal:
  当在线使用,和后台服务器进行通讯;当离线使用,和本地服务器进行通讯。数据必须在切换模式的时候进行同步。

Modeless:
 Modeless假设在离线状态下使用,或者随时都有可能离线。应用程序尽可能的使用本地数据(local store),并且不断地、小数据量通过后台服务和服务器进行数据同步。

数据同步(Data Synchronization)

有数种方式可以用在数据同步中,但是没有一种是适合任何情况的。最终选择的方案根据系统的不同而不同。

Manual Sync:
最简单的方式,有客户决定。可以通过将本地的所有数据进行上传覆盖服务器的数据,或者下载服务器上的数据覆盖本地的数据。

要求: 数据量不能大。

Background Sync:
   由应用程序在服务器和本地之间进行不断地同步。这个通过每隔一段和服务器进行通讯,让服务器push/stream数据到客户端(在Ajax术语中,叫做Comet)



Google Gear Manifest 文件

在Gear tutorial中的例子, Manifest包含以下内容:

{
  "betaManifestVersion": 1,
  "version": "my_version_string",
  "entries": [
      { "url": "go_offline.html"},
      { "url": "go_offline.js"},
      { "url": "../gears_init.js"}
    ]
}

其中:
version:定义一个版本的字符串,只要版本的内容不同,就会下载所有的enties中的内容 --- google文档上写这是一个已知的bug,目前无法做到增量更新

entries: 所有需要下载在本地的文件,可以进行离线浏览的文件,除了最后一个文件外,其他用comma进行分割
  文件的地址可以是完整的带域名的方式,也可以是相对Manifest文件的相对路径

要注意的:
1. 确信你的机器上已经安装了Gear (通过gear_init.js可以验证)
2.所有在Manifest中的文件必须URL正确,且存在可以获取到。Manifest文件和里面的URL在同一个Domain中
3.

2009年4月15日星期三

Google Gear 基本使用方法

Google Gear从例子上看,好像很容易使用(当然,仅仅是最基本的入门):

首先通过例子的gears_init.js生成一个Google Gears对象:google.gears
后面所有的操作通过这个对象进行。这个就直接抄gears_init.js实现,自己不用做任何处理了。

对google.gears对象的判断:
if (!window.google || !google.gears) {
    textOut("NOTE:  You must install Gears first."); --- 提示没有安装google gear,
  } else { ---- 安装了google gear,生成自己的localserver和store
    localServer = google.gears.factory.create("beta.localserver");
    store = localServer.createManagedStore(STORE_NAME);
    textOut("Yeay, Gears is already installed.");
  }

其中的:STORE_NAME就是一个自己随意取的名字;

MANIFEST的文件,在create store的时候使用到
  if (!window.google || !google.gears) { --- 判断是否安装了gear
    alert("You must install Gears first.");
    return;
  }

  store.manifestUrl = MANIFEST_FILENAME; --- 这个就是定义manifest文件名字的,而且mainfest的文件,要和当前的页面放在同一个目录中(如果没有设置目录的话)
  store.checkForUpdate();                                       --- 对store的创建更新

  var timerId = window.setInterval(function() {  --- 这是一个把更新的状态显示出来的脚本
    // When the currentVersion property has a value, all of the resources
    // listed in the manifest file for that version are captured. There is
    // an open bug to surface this state change as an event.
    if (store.currentVersion) {
      window.clearInterval(timerId);
      textOut("The documents are now available offline.\n" + 
              "With your browser offline, load the document at " +
              "its normal online URL to see the locally stored " +
       "version. The version stored is: " + 
              store.currentVersion);
    } else if (store.updateStatus == 3) {
      textOut("Error: " + store.lastErrorMessage);
    }
  }, 500);  


删除一个store也是相当容易的:
  if (!window.google || !google.gears) {
    alert("You must install Gears first.");
    return;
  }

  localServer.removeManagedStore(STORE_NAME); --- 删除,这个STORE_NAME就是上面创建时候的STORE NAME
  textOut("Done. The local store has been removed." +
          "You will now see online versions of the documents.");

2009年4月14日星期二

开发语言的选择

昨天讨论后续的开发工作,讨论讨论,就讨论到开发语言的选择上了。

现在的后台有一个系统使用Java+Jython做的,但是目前的技术负责是C++出身,一直认为Java的性能不行,对内存使用、内存管理有局限,一直排挤Java。领导呢对Python的某些功能比较青睐,又从网上找了一些的开源工具,可以节省开发难度,节省开发的工作量,因此想选择使用Python完成其中部分的工作 -- 对领导而言是比较重要的计算功能。

为了这个问题,大家争论不休,意见难以统一。

在这里也记录一下自己在会议上的意见:
自己的意见就是: 采用哪种语言开发应该不是目前最关心的,而且效率不是现在唯一要考虑的因素,需要从以下各个因素上综合考虑:
  1. C++语言的效率优势已经不那么明显了:
     现在机器的性能上去了,原来C++效率的优势没有原来这么大了。而且随着语言的不断进化,语言本身的效率也在不断的提高,虽说C++的效率还是比较高,但是各个语言间的效率已经越来越接近了;
  2. 需要考虑工作进度、公司的人员配备:
    虽说C++语言在效率上还残存一些优势,但是C++的开发周期、开发的工作量,都相对较大 --- 从实际情况来说,这个还是比较客气的说法。每次出产品,C++开发的服务器,都是时间最长最长的,开发人员需要的技术、开发过程中的成本也是最高的,和目前的快速维护、快速增加功能的原则背道而驰。关键的部分(如只能唯一的服务器)、最为核心的部分要用C++开发,达到稳定、高效的目的,这个无可厚非,但是系统中所有的地方都用C++,在开发周期、开发成本上就无法接受;
  3. 发挥每种语言的优势:
    每种语言的产生,必定有其出现的道理,有其存在的理由、优势。在开发过程中,要发挥每种语言的优势,进行合理的搭配,达到快速有效的开发,这样才是最合理的配置;
  4. 体系结构优于语言的选择:
    现在的开发,尤其是后台支撑系统的开发,已经是体系上的竞争,架构体系的优劣已经远远超越语言的竞争了。
    体系方面,譬如简单的增加一级缓存,带来系统性能的提升、服务能力的提升、吞吐量的提升可能是数个数量级上的提升;而语言的不同,带来的可能仅仅是同一级别、同一层次上的提升;何况通过多种语言的协作,发挥各种语言的优势,不仅能够充分使用到公司目前的资源,而且使用到语言本身的优势,快速推出新功能,尽快的响应市场的呼声。开发语言相争和架构体系上比较起来,相对而言只是一个很局部的问题(目前在架构体系上,可以优化、可以提升的地方太多了)。
最终的结果还是按照在原来的基础上,先完成功能,语言上的选择再议。

GTD --Getting Things Done

最近一阵狂热把工作搬到网上去,主要的原因还是有大量的工作需要在家里进行思考一下(周末不来公司,太远了,一来一回2个多小时,这些时间还不如在家里好好思考一下)。

在网上工作,主要用到的还是google的日历以及google的文档,包括现在在看的google的code源码保留:当然,在google code上只放一些测试代码,公司运行的商业代码不放上去。

在寻找网上任务管理的时候,看到有一个GTD的大家都挺推崇的(以前工作只能在局域网中工作,对现在的这些在线工具都孤陋寡闻了),GTD不知道是什么,在维基百科中查了一下:


相关资源:
http://www.chedong.com/blog/archives/000790.html 车东的blog,我比较喜欢的一个
http://www.rememberthemilk.com 网上的DTD网站,评价很高


维基百科中的注释:
原来不打算抄的,后来看一下还是抄录过来比较好,以下是维基百科上的内容:

GTD是英文Getting Things Done的缩写,是一种行为管理的方法,也是David Allen写的一本书的书名。

GTD的主要原则在于一个人需要通过记录的方式把头脑中的各种任务移出来。通过这样的方式,头脑可以不用塞满各种需要完成的事情,而集中精力在正在完成的事情。

目录

 [隐藏]

[编辑]GTD是关于什么的

和其他时间管理专家不同的是,Allen并不把重点放在设置任务的优先级。他提出制定出在各种环境下的任务列表,例如,制定一个需要打电话的列表,或者在市区才能完成的事情的列表。他也建议任何两分钟之内就能完成的任务应该马上做。

GTD在心理上的好处在于使你需要完成的事情相关的信息易于保存,跟踪和获取。Allen认为导致很多我们在做事的时候碰到的脑力上的障碍的原因是前期的计划不足(举个例子,对任何项目我们需要弄清楚要达到什么目标,还有什么措施需要完成)。

Allen认为我们的脑力上的“提醒系统”相当的低效,很少能够在恰当的时间和地点提醒我们需要做的事情。因此,把“下一步行动”根据场景分类存放在“可信的系统”当中,是一个能使我们在正确的时间得到正确的提醒的手段。在“GTD”中有很多个人的管理小技巧在实行Allen描述的工作流程中很有用的。

一个很概括的对于Allen的书的内容的描述是对于任何事情都准备好:

“把所有事情都从你的脑袋里弄出来。在事情出现,而不是在事情爆发的时候,就做好相关行动的一系列决定。以合适的类别组织好你的项目的各种提醒以及下一步的行动。保持你的系统更新和完整,充分地检查,使你在任何时候都能信任你的对于你正在做(或者不做)的事情直觉的选择。”

[编辑]原则

GTD的核心原则如下:

[编辑]搜集

把任何你需要跟踪或者记住或者做的事情记在Allen称之为‘水桶’的地方:一个收件箱,电子邮箱,磁带,笔记本,PDA,或者它们的组合。把你脑子里的任何东西都拿出来放到你的搜集设备里,准备好做下一步的处理。每星期所有的水桶都应该被至少清空一次。

[编辑]处理

处理你的收件箱要遵循一个严格的工作流程:

  • 从最上面开始。
  • 一次处理一项。
  • 不把任何东西放回收件箱。
  • 如果任何一项需要做:
  • 做(如果花的时间少于两分钟)
  • 委托别人完成,或者
  • 把它延期。
  • 否则
  • 把它存档以便查询,
  • 把它扔掉,或者
  • 使它成熟以便下一步的处理

两分钟原则:任何事情如果花的时间少于两分钟,那么马上就去做。两分钟是一个分水岭,这样的时间和正式地推迟一个动作所花的时间差不多。

[编辑]组织

Allen描述了一个建议的列表集合,你可以用来跟踪需要关注的项目:

  • 下一步行动(Next actions) - 对于每个需要你关注的事项,定好什么是你可以实际采取的下一步行动。例如,如果事项为“写项目报告”,下一步行动可能会是“给Fred发邮件开个简短会议”,或者“给Jim打电话问报告的要求”,或者类似的事情。虽然要完成这个事项,可能会有很多的步骤和行动,但是其中一定会有你需要首先去做的事情,这样的事情就应该被记录在“下一步行动”列表上。较好的做法是把这些事项根据能够被完成的“环境”整理分类,例如“在办公室”,“用电话”,“在商场”.
  • 项目(Projects) - 每个需要多于一个实际的行动才能达到的生活或者工作中的“开放式回路”就是一个“项目”.使用跟踪以及周期性的回顾来确保每个项目都有一个下一步的行动进行下去。
  • 等待(Waiting for) - 当你已经指派了一个事项给其他人或者在项目进行下去之前需要等待外部的事件,就应当在你的系统当中跟踪以及定期检查是否已经可以采取行动或者需要发出一个提醒。
  • 将来/可能(Someday/Maybe) - 这些事情你需要在某个点去做,但是不是马上。例如:“学习中文”,或者“进行一个潜水假期”.

对于跟踪你的预约和委托,一个日历也是重要的;另外,Allen特别推荐日历应该被用在他所谓的“硬工程”上:必须在某个特定的期限之前完成的事情,或者在约定的时间和地点完成的会议和约会.“待办”事项应该用在下一步行动列表当中。

GTD的最后一个关键组织模块是归档系统.“Getting Things Done”书里说如果要用一个归档系统,那它必须得是简单易用和有趣。即使是一张纸,如果你需要用来记录参考信息,如果不属于你已经有的一个目录,也要有自己的文件组织方式。Allen的建议是你可以维护一个按照字母顺序组织的归档系统,这样可以比较容易快速的存储和提取你所想要的信息。

Google的Gmail的用户可以用创建标签的方式来创建“待办事项”和“项目”,这种方式在Bryan Murdaugh的 “Getting Things Done with Gmail” [1]白皮书中有清楚的描述。它保留了很多GTD的相同概念,但是是在在线的电子邮件系统中实施。

[编辑]检查

如果你不至少每天或者只要你有时间就回顾检查,那么你的行动和提醒的列表将会变的毫无用处。以你当时拥有的精力,资源和时间,决定什么是对你来说最重要的事情,然后做。如果你倾向于拖延,你可能会老是做最容易的事情,避免那些难的。为了解决这个问题,你可以一个接一个地做列表上的事情,按照它们的顺序,就象你处理你的收件箱一样。

至少以星期为周期,GTD要求你回顾所有你比较主要的“行动”,“项目”和“等待”的事项,确保所有的新任务或者即将到来的事件都进入你的系统,而且所有的事情都更新到符合最新的情况。Allen建议制作一个难题档案来帮助你更新你关于主要行动的记忆。

[编辑]

如果你把你的时间都花在组织工作、而不是做它们,那么这样的系统是不好的!David Allen的观点是,如果你可以把必须做的事情,让它变得简单、容易、有趣的话,那你就比较不会拖延、或者被太多的“开放性回路”所压倒。

[编辑]工具和技巧

A slice of '43 Folders'

一个Allen推荐的工具是难题文件夹,用来组织你的GTD的文字工作(也被称为‘43文件夹’).12个文件夹用来表示每一个月,另外的31个文件夹用来表示每一天。这些文件夹用来帮助提醒你当天的活动。每天你打开表达当天的文件夹。你把所有的事项都拿出文件夹,然后把空文件夹放进下一个月里。这种处理允许你为自己保存提醒的硬拷贝。例如,如果你在这个月的12号有一个音乐会,你可以把票放在第12个文件夹当中。当12号到的时候,它就在那里等着你。

[编辑]DIY Planner Hipster PDA

这是一种用来执行GTD的纸本DIY范本,对于习惯用实体纸本计划的人来说,可作为另一种优质选择。[2]

2009年4月13日星期一

Python解析中文xml问题

用Python解析带中文的xml文档,发生问题:

xml头:


Python在解析的时候报:

>>> xmldoc=minidom.parse('e:\\cc.xml')
Traceback (most recent call last):
File "", line 1, in
File "D:\Python26\lib\xml\dom\minidom.py", line 1918, in parse
return expatbuilder.parse(file)
File "D:\Python26\lib\xml\dom\expatbuilder.py", line 924, in parse
result = builder.parseFile(fp)
File "D:\Python26\lib\xml\dom\expatbuilder.py", line 207, in parseFile
parser.Parse(buffer, 0)
xml.parsers.expat.ExpatError: unknown encoding: line 1, column 30

将encoding去掉或者修改成UTF-8,这一步可以过了

------------------------------------
再次解析,又报新的错误:
在节点的属性中,有中文内容,
通过java的native2ascii,将整个文档转换成utf-8的编码方式,可以通过

------------------------------------
其实用编辑器(Notepad++,其他的一样)的 "转为UTF-8编码格式”重新保存一下即可。

刚才搞错了 “以UTF-8格式编码”和“转为UTF-8编码格式”之间差别

2009-04-13 记录女儿诗2首

1. 2009年春节前,到南京汤山温泉去了一次,这是泡好温泉当晚,女儿在旅馆里写的:
今日泡温泉,
身心脾恢复,
池池都有yi(益),
小鱼吃死皮,
瀑布从天降;
水喷托起人;
桑拿蒸热人,
中温 使人舒,
高温令人热;
再入冷水池,
锻炼身体zhi(质),
入池出未冷,
药池把病治,
真是好温泉。

2. 第二天去了天目湖,也写了几句:

今去天目湖,
水色如此好,
碧绿似feicui(翡翠)


3. 小草
参加2009“七彩风”小学生童诗童话创作大赛

春天,草变绿了,
春天,地不黄了。
你是那么蓬勃,
那么美丽
你长出了一棵棵
碧绿碧绿的草,
你开出了一朵朵
艳丽艳丽的花。
看着你,让我欢快,
看着你,让我没了烦恼,
看着你,让我进入梦幻,
看着你,让我像天使般快乐。
愿你天天永远蓬勃,
永远美丽!

2009年4月10日星期五

配置EmForge遇到的问题 -- 用户注册

安装了EmForge,无法注册新用户。页面报cannot register user

使用的是windows下的msi安装包,看了一下,使用的是Jetty服务器,在log中有报错,是smtp服务器配置错误。

将smtp服务器配置正确,依然不能注册客户,这次报的错误和上次不一样了,这次应用程序的exception打到了页面上:



[EmForge]: [2009-04-11 09:25:01,203] ERROR org.ajax4jsf.webapp.BaseXMLFilter - Exception in the filter chain
javax.servlet.ServletException: viewId:/register.faces - View /register.faces could not be restored.
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:270)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1124)
at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:390)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:517)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:164)
at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141)
at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:417)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.emforge.web.upload.UploadMultipartFilter.doFilter(UploadMultipartFilter.java:25)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.emforge.security.web.EditProfileFilter.doFilterHttp(EditProfileFilter.java:55)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:109)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:83)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.emforge.web.LocaleFilter.doFilterInternal(LocaleFilter.java:60)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:534)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:879)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:741)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:213)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:403)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:451)
Caused by: javax.faces.application.ViewExpiredException: viewId:/register.faces - View /register.faces could not be restored.
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:185)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:103)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
... 74 more

原来以为是不是EmForge的安全属性没有设置,文档看了半天觉得不应该是,缺省的方式可以,再看一遍文档,发现没有设置: email.defaultFromAddress
这个可能是一个问题,如果不设置正确的defaultFromAddress,邮件服务器会将邮件拒绝的,配置好这个参数,重启一下服务,用户可以注册,在信箱中收到验证邮件。

点击验证邮件中的连接,用户注册成功

----------------------------------
问题继续: 刚才注册的是 onine.sh.cn信箱,注册成功。在自己的机器上测试工作流,需要几个账号进行工作分配,注册一个gmail.com信箱,又报“Cannot register user”


[EmForge]: [2009-04-11 10:28:21,156] ERROR org.emforge.security.web.bean.RegisterController - Cannot register user
org.emforge.EmForgeException: 不能够发送注册通知Email给XXXXXX@gmail.com
at org.emforge.security.SecurityServiceImpl.registerNewUser(SecurityServiceImpl.java:304)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy9.registerNewUser(Unknown Source)
at org.emforge.security.web.bean.RegisterController.submit(RegisterController.java:152)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.el.parser.AstValue.invoke(AstValue.java:152)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:68)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:387)
at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:321)
at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:296)
at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:253)
at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:466)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1124)
at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:390)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:517)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:164)
at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141)
at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:417)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.emforge.web.upload.UploadMultipartFilter.doFilter(UploadMultipartFilter.java:25)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.emforge.security.web.EditProfileFilter.doFilterHttp(EditProfileFilter.java:55)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:109)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:83)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.emforge.web.LocaleFilter.doFilterInternal(LocaleFilter.java:60)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:534)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:879)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:741)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:213)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:403)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:451)
Caused by: org.emforge.email.EmailerException: Failed messages: javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 550 relaying mail to gmail.com is not allowed

at org.emforge.email.EmailSenderImpl.sendMessage(EmailSenderImpl.java:267)
at org.emforge.security.SecurityServiceImpl.registerNewUser(SecurityServiceImpl.java:301)
... 104 more
Caused by: org.springframework.mail.MailSendException; nested exceptions (1) are:
Failed message 1: javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 550 relaying mail to gmail.com is not allowed

at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:422)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:342)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:338)
at org.emforge.email.EmailSenderImpl.send(EmailSenderImpl.java:146)
at org.emforge.email.EmailSenderImpl.sendMessage(EmailSenderImpl.java:253)
... 105 more



2009年4月9日星期四

2009-04-10 女儿的眼睛好了一点

晚上睡觉的时候,女儿告诉我,爸爸,黑板上投影的字能看清楚一点了。

当时特别高兴,可以用喜出望外来形容。仔细的问了女儿:原来黑板上的字看不清楚?

女儿:老师在黑板上写的小字看不清楚,大字没有问题,现在老师也基本不写小字了

我:那你刚才说黑板上的字是什么?

女儿:黑板上面那块布放下来放的投影,老师有时会写字的,以前看不清,现在有点看清了。

女儿视力有所进步,心里特别兴奋,和女儿做起了报告:
    看来我们最近的努力起到了效果,1. 我们每天滴眼药水(治疗假性近视的药水),2.你现在注意写字姿势了,这样眼睛就没有这么累了,3.我们每天晚上转眼珠,一定要坚持,4.每天喝的补充营养的水,也发挥了作用。这些一定要坚持。我们开始转眼珠吧。

女儿也很兴奋,“嗯”的一声,开始转起了眼珠。太晚了,转完眼珠,女儿就没有声音了,“是不是想睡觉了”,女儿又“嗯”了一声,把敲背给省下来了。
-------------------------------

视力这东邪,一旦近视了,极难恢复到正常水平。希望女儿的视力能够得到改善,要求不高,能够0.8,1.0往上恢复,不用带眼镜就可以了。自己就是带眼镜的,知道带眼镜的痛苦,但愿女儿这是假性近视,通过自己的努力,能够得到提升吧!


2009年4月7日星期二

《互联网时代的软件革命--SaaS架构设计》读后感

拿到这本书,就很快的读完了,从技术角度来讲,这本书没有什么特别的难度,从设计、架构的角度来讲,这本书应该讲真的不错,介绍的相当全面。

Saas架构设计将原来自己了解的一些设计、优化的点,通过一条完整的线串联起来,而且为什么这么做,这么做解决什么问题,如何解决问题的都进行了说明。结合一个例子的发展,充分的说明了SaaS架构的几个阶段的架构调整、优化方法,贴近实际,比较有说服力。

看这本书,真的需要有很多方面的知识,才能了解为什么这么做,这么做带来的提升。
如果想要直接从这本书中得到怎么做,估计要是失望了。这本书中的每一个步骤,都可以单独出一本书,因此这本书只是一本纲领性的书。通过了解这本书提到的每种方式,再去学习每个步骤采用的做法,采用的方法可以不用这本书上讲到的方式,随着时间的推移,可以采用的技术也越来越多,但是一定要明确你遇到了什么问题,要解决什么问题,这个才是最重要的、最主要的,也是这本书主要谈到的问题,这些问题才是架构解决的主要问题。

总体来讲,对于架构设计师而言,尤其是互联网架构设计师,这本书值得一看。






2009年4月5日星期日

EmForge 配置

EmForge目前没有提供后台管理界面

EmForge使用Jetty作为缺省容器,通过msi安装的话,Emforge安装在/webapps/root中
2个主要的配置文件:
  • /WEB-INF/classes/config.properties : 一般配置文件
  • /WEB-INF/classes/META-INF/emforge-ds.xml: SpringFramework配置文件,设置数据源
/WEB-INF/classes/META-INF/spring目录中包含一些spring的配置,

Admin的登录:
  密码config.properties中的security.admin.password
  邮件地址在 security.admin.email

admin角色给其他用户:
   在数据库中emfrg_role表中找到ROLE_EMFORGEADMIN的roleid
   在emfrg_user表中,找到用户的id
   在emfrg_user_role表中,插入对应的记录

配置Emforge路径
   application.path=http://your-local/emforge-web/

配置EmForge使用另一个Repository
   缺省的,EmForge配置用  scm:svn:https://emforge.svn.sourceforge.net/svnroot/emforge   repository  作为在Source-Browser Tab中浏览代码。如果修改成自己的repository,只需要编辑:
repository.path=scm:svn:https://emforge.svn.sourceforge.net/svnroot/emforge
   在config.properties中,还需要配置一些你需要的SCM URL

配置EmForge使用另一个数据库:
   缺省的,EmForge使用内建的HSQL,这是一个简单的数据库,但是在生产环境中不是太好,但是使用HSQL不需要做特别的修改。

  如果要更换数据库,在config.properties中做如下步骤:
  • datasource.driver
    --- JDBC driver class-name。要使用这个,需要把相关的JDBC driver jar文件放在EmForge lib中(webapps/Emforge/WEB-INF/lib中)
  • datasource.url
    --- 包含JDBC-Url,指向数据库(包含用户名、口令)
  • datasource.dialect
    --- 包含Hibernaet dialect(多数情况下可以避免的,Hibernate会自动检测的)
    可以使用内建的自动检测dialet(com.liferay.portal.spring.hibernate.DynamicDialect)
在下一次使用EmForge的时候,会在数据库中重建所有必须得表和初始数据

配置EmForge使用DataSource
   也可以配置EmForge使用在J2EE容器中使用的数据源
  替换emforge-ds.xml文件以及config.properties的database.url。
  如在tomcat中,,替换成:
  datasource.url=java:comp/env/EmForgeDS
  这个时候,datasource.driver就无关紧要了。但是datasource.dialetct还是要设置的

Email配置
   EmForge在流程处理过程中会发出一些邮件。

SMTP Server 配置
  
  • email.host - host name of SMTP server (localhost by default)
  • email.port - port used for connection. Keep -1 to use default
  • email.username - username (in cases then authentication required)
  • email.password - password
  • email.useSsl - set to true, in case then connection done via SSL  

Configure email connection via JNDI

It is also possible to use mail Session, configured on application server level via JNDI. If you already have connection configures, just put it's name into email.sessionName and leave host, username and password empty, like:
email.sessionName=mail/Session 

Extra email settings
There are some other properties related to emailing you can change:
  • email.defaultFromAddress - email used for from-address by default (then for example registration notification
  • email sent email.testMailAccount - if this property is not empty, ALL emails will be sent to this email (instead of real recipient). May be helpful for testing
其他安全设置
    在内网使用,暂且不管安全问题了
WiKi配置:    
   看单独的wiki配置说明
其他i18N说明    
   在一些J2EE容器中(如JBossAS4.2.2+)遇到本地化的一些奇怪问题 --- 从browser过来的字符编码是latin1,不是UTF-8.解决这个问题,要修改配置:
    application.useUtf8Decoding=false
  但是只是在遇到本地化问题后再修改,其他时候修改这个配置可能会遇到其他问题。 
User-Interface setting   
    It is possible to tweak ui behavior of EmForge - some properties are responsible for it 
  • ui.onePageNewTask=false - switching this option to true will allow users to create new task not in step-by-step wizard (like emforge.org used) - but from one page, included ALL settings, required for task creation