顯示具有 碎碎唸 標籤的文章。 顯示所有文章
顯示具有 碎碎唸 標籤的文章。 顯示所有文章

21 1月 2013

那些我被放生的專案

由於最近被問了幾次,我不確定大家聽到什麼,所以整理一下先前的情形囉。

先請看一看我2012年六月的行事曆

Calendar
再看看我五月底到六月底兩個專案git的歷史資料

Pa

Pb

不清楚我要表達什麼?這很正常,讓我稍微整理一下
1.三義Yulon,Zyxel, MXIC這三個公司地點都不在台北,也分別代表三個案子
2.我Git的commit log自5月24日起到6月22日,只有5月26日,5月30日,6月1日這三天沒有Commit log,其他都都有喔,而且這三天裡,只有5月26日是假日,其他都還是工作日
3.再看看commit的時間,在晚上八點之後到隔日八點之間的至少有15天,凌晨2~4的日子也不少
這代表我在這個月內,獨自面對兩個看來是高強度的遠距離案子,當然強度如何可能要看一下Commit的程式多寡與難度,所以只好說〞看來是〞。

那這些又能代表什麼?嗯,當然只能看下去囉
我先說明這三個案子
1.三義是個維護案,就是原來系統的功能再修修改改,量不多,壓力也不大
2.Zyxel則是某個廠商案子的第二階段,這案子自3月開始接觸,預計開發時程為4、5兩個月 。以下簡稱A案。
3.MXIC則是個導入案,我們只負責其中資料交換的部份。類似ETL,但沒有邏輯的部份。4月中談下來的,預計時間大約是5月~7月初要結束。以下簡稱B案。

再來我用時間的順序來看一下狀況
三月:
A案自3月起開始與客戶討論,初步大約是九個功能,中間廠商會做SA與PM的角色,討論時僅能說個大概,詳細部份要等4月份才能提供;所以我評估這個案子可能需要2個人,時間2個月應該風險不高,最後的報價大概支付2個月帳面薪水還能有一半的收益。
不過因為預計要有兩個人,所以我當然會去要人,我希望不要是新手,而且至少在4月前能有1~2個星期先磨合,不要到了專案開始才給我人,這樣風險會很高,我得到的回答是要人沒有問題,到時候一定找得到人,不過再提了2次後,我想應該不會有人了,所以我只好提,這個案子開始後,我應該沒有時間去做別的東西,面試或其他的工做大概都幫不上忙。
四月:
3號左右收到了文件,稍微放下了心,功能的要求比我想像中要來得容易,很多我認為要做後台管理的東西都可以免了,我跟廠商的PM談了一下各階段的東西,預計是2星期左右做Prototype,再來就是開發的階段,中間客戶提了項技術性的要求,我必需去改某OpenSource的Source Code加入AJAX的支援以便配合我慣用的頁面模式,多要求了3天做Prototype,不過最後還是在2星期內提給客戶一個全部頁面都能點擊操作的系統,只是內容資料都是假的;
再來後的的就比較輕鬆,當時認為如果客戶該提供的東西都沒有Delay的話,還可能會提早1~2個星期完成。所以當提出要去談另一個案子,我覺得還可以,由其客戶預計是五月才開始,七月初要上線,那依規劃的情形下,我六月份可以有很多的時間來處理,應該完全不用擔心。
五月:
中間持續有些討論,廠商的權限設計很有趣....,不過在5月18日我也把東西給A案廠商測,測試大約一個星期左右也都符合他們內部份的規格後,我開始在看B案要怎麼做比較好。
我當時認為,A案廠商都驗收過了,再來應該不會有什麼大問題.....
六月:
A案廠商交給客戶看了,提了很多問題,不過裡面有3/4我認為那是規格上沒有註明也沒有說明的部份,這情形當然要算CR,廠商的PM也承認很多不是我們的問題;第一個假日,我將他們提的東西改完後分為2部份,一部份是我該修的,一部份是我不該修的,各自Package一個版本,交給公司內部的人去運用;另一方面,B案的PM也與我討論什麼時間要到客戶那,初步訂了6月18日要到客戶那進行我那部份的Demo;


A案的客戶在修正之後,又持續提了很多東西,幾乎佔用了我大多的時間,特別又一直要我待在新竹,但如果在新竹,我B案無法做任何事,再來後面提的東西基本都是CR,我不認為我們需要為這些負責,CR本來就該另估時程並計價,在沒完成計價估時的情形下,開發人員不該擅自處理。


不過只要我沒去新竹,A案廠商就會一直打電話來,說我一定要去新竹不然客戶不高興等等,我堅持的是如果是我們的BUG,那當然我們負責到底,但是後面提的都不在規格裡,為什麼我們一定要配合他們給客戶看,當然沒提的是,我還在忙另一個案子。

中間雖然公司的人被電話煩到一度曾氣說案子不做了,但最後還是讓執行的人討論,所以我還是做了下去。


就這樣撐著撐到6月12日,當時給的9個功能在客戶那也都過了,不過另一個晴天霹靂出現,A案的客戶提出兩個全新的功能,一個是較複雜的查詢,另一個是要與舊系統整合,13號我到新竹看了實際的需求,初步的評估可能需要4~6個工作日。
這下慘了,我很需要後面幾個完整的工作日來進行我B案的部份,要不B案會沒辦法如期交付,而且還訂好時間要到客戶那....

6月14日,我上午先到三義瞭解客戶的需求,下午我還是進台北辦公室跟公司的人談A案與B案的事情,我認為A案是CR,而不知道還有多少洞要補,AB兩個案子要救應該要先救B案,至少A案在當時給的東西是完全不在當時簽約的規格內。
不過公司的人接電話接到煩了,A案廠商會晚上很晚還打電話罵為什麼我不去新竹,所以看來公司的人看來是不知道怎麼擋了,也不說話了,
〞那公司有沒有其他人可以先幫助B案?〞,我想有人先頂一下吧?不過沒有回應。
我最後想想,〞那是不是幫忙打電話給B案的客戶,說明因其他專案的問題所以可能延後交付?〞
公司的人還是沈默沒回應,我笑了笑,〞算了,我自己處理〞,我很明白,我完全被放生了。

所以接下來就一連串的加班工作,我把B案完全放下,利用假日加上三個左右的工作天完全在加班趕A案的東西,還好B案他們自己延到20號,不過那天是颱風天,所以又延到22號,我在沒有出天窗的情形下過關了。。。。

總結一下
1。我在專案裡要不到人,急的時候完全沒有支援。
2。CR的部份,公司的人完全不會處理,我不只一次說,不在規格內的東西,時程應該是可以討論的,但這是我最失敗的地方,應該所有東西都要抓回來讓我自己談,我把公司的人想得太好了。

對了,我完全沒有加班費,開車去新竹也沒有交通補助,大家說我是不是佛心來的呢?

31 5月 2011

A500 首發

用blogger-droid寫blog大概就只能寫出plurk的內容吧
Published with Blogger-droid v1.6.9

14 3月 2011

偶有所感

方才看了 綿羊的譯心譯意 的新博文“大師出手,高下立見“,見到下面這段文字

“I am not bound to win, but I am bound to be true. I am not bound to succeed but I am bound to live up to what light I have. I must stand with anybody that stands right, stand with him while he is right and part with him when he goes wrong.“

我覺得失敗也要敗得有格調,但我現在配合的人似乎不在意格調的問題....
突然有點感慨我現在做的工作,又要找下一步了嗎?

 

21 1月 2011

第五個季節

"你知道我最喜歡哪個季節嗎?",妳一如往常無厘頭地問起問題;
我手邊正翻著雜誌,有一搭沒一搭地回應反問:“哪一個?“
“第五個季節啊!“,妳瞪大眼睛,一副不可思議地表情看著我。

“第五個季節?哪是什麼樣的季節?“,我趕緊放下雜誌回應,我瞭解如果我再一副漫不經心的回應,妳雖然不會像對其他人一樣往我胸口捶,但蹭一下腳擰一下腿恐怕是免不了的。
“我還沒有想清楚耶,你也幫我想一想吧。“,妳低頭又想了一下,“對了,那你幫我的第五個季節寫首詩吧?“,“呿,那是什麼我都不知道,還寫什麼詩?有機會再聯絡。“,我又開始翻起我的雜誌,“等妳想清楚了,我再考慮。“,沒多久我腳上就多了兩個瘀青

“那妳知道我最喜歡哪個季節嗎?“,我只好反問妳轉移你的注意力,開始揉著我剛剛得到的兩個瘀青。
妳轉頭想了想,“誰在乎啊?“,踢了下我椅子又轉身去鬧別人了。

當時我們都不知道,這是我們一起渡過的最後一個季節。
我永遠都不知道妳的第五個季節是什麼樣的季節,但我想,妳知道我最喜歡的季節。
是啊,每個有妳陪的季節,就是我最喜歡的季節。

19 1月 2011

Web Application Security

Web Application Security要做的通常不過就是確認目前使用者是誰,能不能做目前要做的動作,就算提到到SSO、OpenID這些東西,也不過是在確認使用者上多些手續而已。

近來面談的人,跟兩三年前不同,多半對Spring都有些瞭解,但再深入問些應用卻又讓我有點失望,Sercurity就是很常讓我不滿意的地方。
有些人還停留在Submit button的enable or disable,這種情形就比較糟糕,有些完全沒有意識到這樣做會有問題的地方,所以開個Firebug,將disabled拿掉,這些人才覺得這是個問題...再有些人認為用method="GET" or "POST"就能阻止這些問題,但要送一個http post request又有何難?

有些人會很快回覆說利用Filter控制,我個人也認為基本上沒有問題,但我不認為全部都在Filter裡處理完是個好方法,所以我通常會再問如何在Spring的AOP裡得知現在的使用者為何,部份的人會說要修改API,將使用者當做參數之一,或是說傳入HttpSession,但沒有人跟我說過:因為是Web Application,所以可以利用ThreadLocal,如果是其他類型的Applicaton,利用InheritableThreadLocal也可以達成。

將使用者資料透過Filter,自Session中取得放入ThreadLocal中,這做法已經用了好幾年,我想這不是一個很具獨特性的做法,因為像是Spring Security已經到了3.X版,而它最重要的存放使用者的方式就是在放ThreadLocal中;能夠取到使用者的資料,那要在Filter、Service Layer或AOP中要進行控管或記錄都不會是問題,端看設計者的需要。

寫到這又有些擔心,擔心自己是不是自我意識過於良好,不過管他的! 我就是覺得目前看來,不把使用者資料放在ThreadLocal中就是一種設計上的缺陷!

29 11月 2010

碎碎唸

個人覺得,工作可以是一種信仰、一種宗教,而一間公司可能同時存在有多種信仰;

信仰間是會有衝突的,有些信仰無法容下其他的信仰而有衝突,發生衝突時也難免會因較為激進的派系而交火;在人數極多時,我想這無可避免。

但如果一間人數極少而每個人竟都有不同的信仰,這個組合應該是一件蠢事。

這蠢事居然就在我眼前。

蠢!


12 7月 2010

If all you have is a hammer, everything looks like a nail

最近這兩個月頗忙,因為要換工作的關係,手上的東西要快點結掉,其中一個案子,在12個工作天左右,寫了大約近8千行的程式,雖然先前對主要流程所訂的lifecycle算是很符合這個案子需要,所在大方向上並沒有什麼問題,但在部份小地方的設計就稍嫌趕了一點。很多東西就是當下想到個大概就動手了,所以有不少程式再回頭看時就覺得有些不當。只是可能沒什麼機會改了.....

對於標題,另外有個想法
You may have a hammer, but not everything is a nail.


23 2月 2010

State Pattern

State Pattern這個入門的Design Pattern,我想大多數人都不陌生,只是我工作這麼久,幾乎沒有看別人使用過,多半都是用if {} else {}, switch case來處理,這是我比較不能理解的情形。
一個程式有多種State,而且需要依目前不同State做出不同的反應,幾乎是每個系統一定會有的,
像電信業的使用者多半有prepaid, postpaid, unsubscribe,np等多種State;
還有所謂的Task,可能有Init, Scheduled, Triggered, Success, Fail等State,我看到的程式幾乎都是將State開放交給在外部程式利用setter來改變。
這樣也不是說不好,如果控制得當,系統當然也不會有什麼大問題,但是在每次系統要做事之前,一定要經過一大堆的State判斷,可能是if else,也可能是switch,才能決定能不能做,要做些什麼。
開發時間一長或是人員替換,再來看這個程式,就會有點傷腦筋,到底有多少種State,在什麼情形下會改變,又是誰改了這個State,很多問號就一直浮出來。

先說說我自己的常遇到的情形好了。

所謂的State多半有三種,Initial State, Alive State, Final State。
Initial State就是最開始的State(廢話..),而且幾乎只有一種,一開始都是處於這個State之下,再來會進入Alive或Final State,只要離開了Initial State就幾乎不會再回到Initial State了。
Alive State則是運作中的State,像是Scheduled, Triggered這種State,Alive State間可能會互相轉換,最終應該要進入Final State。
Final State是程式最後運行的終點,進入Final State之後就不應該再有任何改變,像是Success, Fail, Cancel這種詞意所代表的通常就是Final State。
也很有可能沒有Final State,像是User這種類型的Class,可能就僅在Active, Inactive, Suspended之類的State間轉換。
State間的轉換不應該是依靠setter來處理,而應該是一個Action(Method),這樣才能確保State是依照我們的控制在運作,而不會被天外飛來之物所改變。

這裡舉個簡單的例子,用常見的Task來看看吧。先利用enum列出可能的State。

public enum TaskStateEnum {
 Init, //Initial State
 Scheduled, //Alive State
 Triggered, //Alive State
 Retry, //Alive State
 Success, //Final State
 Fail, //Final State
 Canceled //Final State
}

這時候如果有個State Diagram的話就會很容易理解各個State間轉換的情形,但原讓諒小弟懶人一個....
一般寫出來的Task可能會像下面這樣

public class Task {
 private String oid;
 private Date scheduleTime;
 private TaskStateEnum taskStateEnum = TaskStateEnum.Init;
 
 public Task(String oid) {
  this.oid = oid;
 }

 public void schedule(Date scheduleTime) {
  if (null == scheduleTime) {
   throw new RuntimeException("scheduleTime can't be null value.");
  }
  //檢查State,僅有Init跟Scheduled的Task才能改變scheduleTime
  if (!(TaskStateEnum.Init == taskStateEnum || TaskStateEnum.Scheduled == taskStateEnum)) {
   System.out.println("Task["+oid+"] can't schedule from "+taskStateEnum);
   return;
  }
  this.taskStateEnum = TaskStateEnum.Scheduled;
  this.scheduleTime = scheduleTime;
 }

 public void execute() throws Exception {
  //檢查State,僅有Scheduled的Task才能進行execute
  if (!(TaskStateEnum.Scheduled == taskStateEnum)) {
   System.out.println("Task["+oid+"] can't execute from "+taskStateEnum);
   return;
  }
  //改變State為Trigger
  this.taskStateEnum = TaskStateEnum.Triggered;
  
  //oid為SuccessTask就直接進入Success,其餘則進入Retry。丟出Exception告訴TaskRunner執行有問題。
  if ("SuccessTask".equals(this.oid)) {
   this.taskStateEnum = TaskStateEnum.Success;
   System.out.println("Task["+this.oid+"] success at ["+this.scheduleTime+"].");
  } else {
   this.taskStateEnum = TaskStateEnum.Retry;
   throw new Exception("Task["+this.oid+"] fail.");
  }
 }

 public void retry() {
  //檢查State,僅有Retry的Task才需要再次處理,這使用switch並非必要,僅是為了demo麻煩的switch
  switch(taskStateEnum) {
  case Retry:
   //oid為RetryTask就直接進入Success,其餘則進入Fail。
   //這小Demo僅retry一次,若再失敗就直接Fail不再處理。
   if ("RetryTask".equals(this.oid)) {
    this.taskStateEnum = TaskStateEnum.Success;
    System.out.println("Task["+this.oid+"] success at ["+this.scheduleTime+"].");
   } else {
    this.taskStateEnum = TaskStateEnum.Fail;
    System.out.println("Task["+this.oid+"] fail at ["+this.scheduleTime+"].");
   }
   break;
  case Init:
  case Scheduled:
  case Triggered:
  case Success:
  case Fail:
  case Canceled:
  default:
   System.out.println("Task["+oid+"] can't retry from "+taskStateEnum);
  }
 }

 public void cancel() {
  //檢查State,僅有Init跟Scheduled等尚未被trigger的Task才能cancel。
  if (!(TaskStateEnum.Init == taskStateEnum || TaskStateEnum.Scheduled == taskStateEnum)) {
   System.out.println("Task["+oid+"] can't cancel from "+taskStateEnum);
   return;
  }
  taskStateEnum = TaskStateEnum.Canceled;
 }
 
 //盡量不要將setTaskStateEnum設為public,
 //否則將來出了問題就要找整個系統,但是如果用JDBC DAO之類的程式可能無法避免。
 protected void setTaskStateEnum(TaskStateEnum taskStateEnum) {
  this.taskStateEnum = taskStateEnum;
 }
 
 public String getOid() {
  return oid;
 }
 
 public Date getScheduleTime() {
  return scheduleTime;
 }

 public TaskStateEnum getTaskStateEnum() {
  return taskStateEnum;
 }
 
 protected void setScheduleTime(Date scheduleTime) {
  this.scheduleTime = scheduleTime;
 }
 
}

其中的schedule、execute、retry、cancel等就是所謂的Action,Action在執行時會造成TaskStateEnum的轉換,這裡使用最常見的if else 與 switch方式來檢查TaskStateEnum,然後直接使用this.taskStateEnum = XXX來改變TaskStateEnum。
要想將這個if else 跟 switch去除,就要靠State Pattern囉。

第一步先將這些Action抽出來,訂成一個Interface,就命名為State吧
public interface State {

 void schedule(Date scheduleTime);
 void execute() throws Exception;
 void retry();
 void cancel();
 
 //讓外界明白目前是什麼State
 TaskStateEnum getStateEnum();
}
第二步再訂一個基本的實做AbstractState,利用這個AbstractState將所有的Action預設為無作用,可以簡化後面的開發。
public abstract class AbstractState implements State {
 protected NewTask task;
 protected TaskStateEnum stateEnum;
 
 public AbstractState(NewTask task, TaskStateEnum stateEnum) {
  this.task = task;
  this.stateEnum = stateEnum;
 }
 
 @Override
 public TaskStateEnum getStateEnum() {
  return stateEnum;
 }
 
 @Override
 public void cancel() {
  System.out.println("Task["+task.getOid()+"] can't cancel from "+stateEnum);
 }

 @Override
 public void execute() throws Exception {
  System.out.println("Task["+task.getOid()+"] can't execute from "+stateEnum);
 }

 @Override
 public void retry() {
  System.out.println("Task["+task.getOid()+"] can't retry from "+stateEnum);
 }

 @Override
 public void schedule(Date scheduleTime) {
  System.out.println("Task["+task.getOid()+"] can't schedule from "+stateEnum);
 }

}
第三步再依據TaskStateEnum中的Initial、Alive、Final State建立Class,通常Alive State會自行擁有一個Class,而Final State可以共用一個Class,各個State implementation間會互相認識,並且利用task 的switchState()來要求task轉換State,
public class InitState extends AbstractState {

 public InitState(NewTask task) {
  super(task, TaskStateEnum.Init);
 }

 @Override
 public void schedule(Date scheduleTime) {
  this.task.switchState(new ScheduledState(task));
  this.task.setScheduleTime(scheduleTime);
 }
 
}

//因為Final State什麼事都不能做,所以幾乎等於AbstractState
public class FinalState extends AbstractState {

 public FinalState(NewTask task, TaskStateEnum stateEnum) {
  super(task, stateEnum);
 }

}

public class ScheduledState extends AbstractState {

 public ScheduledState(NewTask task) {
  super(task, TaskStateEnum.Scheduled);
 }
 
 @Override
 public void schedule(Date scheduleTime) {
  this.task.setScheduleTime(scheduleTime);
 }

 @Override
 public void execute() throws Exception {
  this.task.switchState(new TriggeredState(task));
  
  if ("SuccessTask".equals(this.task.getOid())) {
   this.task.switchState(new FinalState(task, TaskStateEnum.Success));
   System.out.println("Task["+this.task.getOid()+"] success at ["+this.task.getScheduleTime()+"].");
  } else {
   this.task.switchState(new RetryState(task));
   throw new Exception("Task["+this.task.getOid()+"] fail.");
  }
 }
 
}

public class TriggeredState extends AbstractState {

 public TriggeredState(NewTask task) {
  super(task, TaskStateEnum.Triggered);
 }

}

public class RetryState extends AbstractState {

 public RetryState(NewTask task) {
  super(task, TaskStateEnum.Retry);
 }
 
 @Override
 public void retry() {
  if ("RetryTask".equals(this.task.getOid())) {
   this.task.switchState(new FinalState(task, TaskStateEnum.Success));
   System.out.println("Task["+this.task.getOid()+"] success at ["+this.task.getScheduleTime()+"].");
  } else {
   this.task.switchState(new FinalState(task, TaskStateEnum.Fail));
   System.out.println("Task["+this.task.getOid()+"] fail at ["+this.task.getScheduleTime()+"].");
  }
 }
}
最後就修改Task,將TaskStateEnum轉換的機制改一下
 //加入State做為Action的delegater。
 private State state = null;
 
 public Task(String oid) {
  this.oid = oid;
  this.initState();
 }
 
 //無論是誰要改變taskStateEnum都必需提供State的實做,
 //以免Task與taskStateEnum的行為不一致
 public void switchState(State newState) {
  this.state = newState;
  this.taskStateEnum = newState.getStateEnum();
 }
 
 //利用目前的taskStateEnum來取得對應的State implementation
 public void initState() {
  switch(taskStateEnum) {
  case Scheduled:
   this.state = new ScheduledState(this);
   break;
  case Triggered:
   this.state = new TriggeredState(this);
   break;
  case Retry:
   this.state = new RetryState(this);
   break;
  case Success:
  case Fail:
  case Canceled:
   this.state = new FinalState(this, taskStateEnum);
   break;
  case Init:
  default:
   this.state = new InitState(this);
  }
 }
簡化後的Task就長得像下面這樣囉
public class Task {
 private String oid;
 private Date scheduleTime;
 private TaskStateEnum taskStateEnum = TaskStateEnum.Init;
 private State state = null;
 
 public Task(String oid) {
  this.oid = oid;
  this.initState();
 }

 public void schedule(Date scheduleTime) {
  this.state.schedule(scheduleTime);
 }

 public void execute() throws Exception {
  this.state.execute();
 }

 public void retry() {
  this.state.retry();
 }

 public void cancel() {
  this.state.cancel();
 }
 
 public String getOid() {
  return oid;
 }
 
 public Date getScheduleTime() {
  return scheduleTime;
 }

 public TaskStateEnum getTaskStateEnum() {
  return taskStateEnum;
 }
 
 protected void setScheduleTime(Date scheduleTime) {
  this.scheduleTime = scheduleTime;
 }

 protected void setTaskStateEnum(TaskStateEnum taskStateEnum) {
  this.taskStateEnum = taskStateEnum;
 }
 
 public void switchState(State newState) {
  this.state = newState;
  this.taskStateEnum = newState.getStateEnum();
 }
 
 public void initState() {
  switch(taskStateEnum) {
  case Scheduled:
   this.state = new ScheduledState(this);
   break;
  case Triggered:
   this.state = new TriggeredState(this);
   break;
  case Retry:
   this.state = new RetryState(this);
   break;
  case Success:
  case Fail:
  case Canceled:
   this.state = new FinalState(this, taskStateEnum);
   break;
  case Init:
  default:
   this.state = new InitState(this);
  }
 }
}

去除了惱人的if else 跟 switch,更容易聚焦在要修改的Action,State的轉換更是清楚(有State Diagram的話)。
只是多了很多Class....所以好不好也是見仁見智啦,有的人也就是不喜歡這麼多Class吧。
State Pattern是Design Pattern中很基礎的一種,小弟在這斗膽在這耍下小刀,高人見到莫笑啊....

什麼,看了這麼長的程式碼還想要看TestCase!?真是同道中人啊...
public class TaskTest {
 Task task = null;
 
 @Test
 public void testSuccessTask() {
  task = new Task("SuccessTask");
  task.schedule(new Date());
  try {
   task.execute();
  } catch (Exception e) {
   Assert.fail();
  }
  Assert.assertEquals(TaskStateEnum.Success, task.getTaskStateEnum());
 }
 
 @Test
 public void testRetryTask() {
  task = new Task("RetryTask");
  task.schedule(new Date());
  try {
   task.execute();
   Assert.fail(); //task must throw exception
  } catch (Exception e) {
   task.retry();
  }
  Assert.assertEquals(TaskStateEnum.Success, task.getTaskStateEnum());
 }
 
 @Test
 public void testFailTask() {
  task = new Task("FailTask");
  task.schedule(new Date());
  try {
   task.execute();
   Assert.fail(); //task must throw exception
  } catch (Exception e) {
   task.cancel(); //Triggered Task can't be canceled.
   Assert.assertEquals(TaskStateEnum.Retry, task.getTaskStateEnum());
   task.retry();
  }
  Assert.assertEquals(TaskStateEnum.Fail, task.getTaskStateEnum());
 }
}

27 10月 2009

emma-maven-plugin always executes test cases twice

這是碎碎唸
如果同時要出Emma Code Coverage跟Surefire Test report的話,一定要跑2次 Unit Test,聽起來很合理,但其實根本就不用。
只要在Surefire執行前先將test classes做instrument,這様測出來的就是具有coverage的unit test.
<plugin>
 <groupId>org.sonatype.maven.plugin</groupId>
 <artifactId>emma-maven-plugin</artifactId>
 <version>1.1</version>
 <inherited>true</inherited>
 <executions>
  <execution>
   <id>instrument</id>
   <phase>process-classes</phase>
   <goals>
    <goal>instrument</goal>
   </goals>
  </execution>
 </executions>
</plugin>
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <version>2.4.3</version>
 <inherited>true</inherited>
 <configuration>
  <forkMode>once</forkMode>
        <reportFormat>xml</reportFormat>
  <classesDirectory>${project.build.directory}/generated-classes/emma/classes</classesDirectory>
 </configuration>
</plugin>
這様跑完就㑹產生coverage.em, coverage.ec等產生Code Coverage Report所需要的東西。


而jira上也提出這個問題很久了…就是他X的沒人解決,
Allow generating reports outside the emma:emma goal


現在只好偷偷用emma4it-maven-plugin,單獨來跑report,只是不能出現在project reports裡
<plugin>
 <groupId>org.sonatype.maven.plugin</groupId>
 <artifactId>emma4it-maven-plugin</artifactId>
 <version>1.3</version>
 <executions>
  <execution>
   <id>report</id>
   <phase>post-integration-test</phase>
   <goals>
    <goal>report</goal>
   </goals>
   <configuration>
    <sourceSets>
     <sourceSet>
      <directory>${project.build.sourceDirectory}</directory>
     </sourceSet>
    </sourceSets>
   </configuration>
  </execution>
 </executions>
</plugin>

18 9月 2009

碎碎唸(8)

一直來就想做件事,租個虛擬主機,掛個Apache + Subversion,這樣我在外頭也可以改改自High的demo,回家後也不用開MBP來sync程式到我的Desktop,但一方面是不確定是否能正常運作,一方面也不想多這筆開銷(在家用ADSL自己架?我可不想天天擔心家裡電器走火…),所以一直沒有付諸實行,但最近因為玩Ruby的關係,接觸到了GIT,發現有免錢的github,真是一整個高興!Eclipse上的pluging “egit”雖然有點不成材,但用git protocol來接github倒沒出什麼事,只可惜了我的iPhone 3GS,JB後裝的git 跟subversion 就沒什麼用了…

06 8月 2009

為什麼VCS要能透過HTTP存取對我很重要

有朋友問著,Subversion用svn protocol,Git就用git protocol,設定起來不是比較容易嗎?

當然,在Intranet裡,通常你要怎麼用都沒問題,問題是當你離開辦公室後,或是特別的因素開發人員必需分隔兩地時,能不能透過HTTP來存取VCS就很重要了。

從公司內部來看,很少MIS會願意特別幫我在防火牆上去開svn, git要用的port,要不然也要經過相當麻煩的申請手續,除非我就是那個網路管理者。

從身在他地的人來看,如果是在自己家中還好,不會特別去關svn, git用到的port,但是如果是身在其他公司內會有網路使用限制的人,恐怕連pop3/smtp, ftp都不能連了,更不用提這些svn, git所用的port。

所以能透過最不容易被擋的HTTP存取VCS的重要性就在這囉。

29 7月 2009

其實,我想知道你聽到的是什麼

以下這對話經常出現在我高中下課後

我:香草霜淇淋一個

店員:先生,請問你要什麼口味

我:…

我真的很想知道,你們聽到的是什麼

17 7月 2009

不要舉報我

西斯版為什麼好笑?請見範例,現在還是看一遍笑一遍啊

-----------------------------------

作者: ohyeahhh (古大熙) 看板: sex

標題: [心得] 近來西斯版的文章
時間: Tue Jun 9 21:18:37 2009
現在我依最近西斯版回文較多的文章題目,來後以各板可能的推文來加以分析
如有不同意或不正確之處,還請各位板友不吝嗇指正


[討論] 如果強暴罪最重可求處死刑...

Joke →:笑點勒 幹你娘 我走錯板了嗎
Boy-Girl →:愛不是這樣的,怎麼會有人在沒有愛的情況下還可以發生這種事
法西斯 →:那些賤男人,最好通通都給老娘我去死一死
西斯板 →:改判肛刑,讓牠們知道被捅的滋味,五樓你說好嗎
→:五樓都跟士官長肛肛好了,他沒再怕
→:電梯向下


[認真]女生在床上的時候,該叫些什麼

Joke →:笑點勒 幹你娘 是不會去問版喔
Boy-Girl →:心靈上的契合,即使沒有言語上的交流,也可以很完美唷
法西斯 →:叫甚麼不一定啦,但我男朋友就超喜歡我們在做那檔事的時候叫我稱讚他
→:推1F,適度地給男朋友鼓勵一下,真的能增加情趣
西斯板 →:你是我這輩子見過最大的男生
→:我聽了~~~我聽了~~~
→:邱若男,我要幹死你
→:歐歐歐歐 你是我的花朵
→:洽卡洽卡洽卡洽卡洽卡洽卡洽卡洽卡....咻咻~~~~
→:推文超好笑的啦XDDDDDDDDDDDDDDDDDD借轉就可
[m [32m※ [1m***** [;32;40m:轉錄至看板 joke [m


[心得] puma的特徵

Joke →:笑點勒 幹你娘 你媽知道你在這邊發廢文嗎
Boy-Girl →:說穿了,她們只是在愛情道路上迷途的羔羊,祝她們早日找到自己的真愛
法西斯 →:甚麼pu不pu,都是一群死阿宅追不到我們就愛叫我們puma,也不照照鏡子
→:對阿,就是有男生愛創出這種侮蔑我們女性的暱稱,是不是男生阿
西斯板 →:在各百貨公司8F體育專櫃出沒
→:內衣除了白色跟阿婆米色系列,其他顏色都可以在她們的衣櫃裡都找的到
→:在夜店看到歪國人就勃起的濃妝妹
→:推3F XDDDDDDDDDDDDDDD


[認真] 電愛習慣

Joke →:笑點勒 幹你娘 你捏捏自己的LP 這哪裡好笑
Boy-Girl →:我相信只要有心,雙方都肯花心思來經營,電愛也可以得到幸福
法西斯 →:很不切實際,我寧可努力上班工作或好好充實自己
西斯板 →:沒錢交馬子就算了,我連電愛的錢都沒有,越想越悶,去論壇繞一圈好了
→:樓上有股......


[討論] 女友信基督不給督

Joke →:笑點勒 幹你娘 有種不要刪
Boy-Girl →:可以雙方彼此先好好溝通,如果你是真心愛你女朋友,我相信你能體諒她
法西斯 →:當然不要給他們阿,他們有第一次就有第二次,我們要有堅持,感謝主
西斯板 →:基督教其實很Nice的,其中一定有甚麼誤會
→:主耶穌基督在你後面,他現在非常火
→:你可以跟她說其實你是耶穌轉世,射完記得說,阿阿阿阿阿阿阿阿阿門


[認真] 又見瑤瑤

Joke →:有乳必推 科科
Boy-Girl →:不懂你的問題點在哪,但還是祝你早日找到自己的真愛
法西斯 →:......
西斯板 →:阿宅,醒了沒
→:阿宅,醒了沒
→:阿宅,醒了沒
→:阿宅,醒了沒
→:阿宅,醒了沒
→:阿宅,醒了沒
→:========================反詐騙=========================


[認真] 男朋友最近都不碰我

Joke →:笑點勒 幹你娘 不要以為是女生就不噓你
Boy-Girl →:妳應該找個時間好好跟他談一談,看看妳們之間的問題點到底是甚麼
→:可能他是最近工作比較累,不要想太多啦,原PO加油
法西斯 →:一定是在外面有了,賤男人,通通去死死
西斯板 →:閃開!讓專業的來!
→:《私人信箱》有新進信件還沒看 鄉民衝了?
→:去找誠誠,車資他會出放心
→:妳應該趁現在好好試試鄉民的30cm
→:原PO是正妹


[認真] 我每次看到補習班的櫃台小姐就會勃起 這樣正常嗎

Joke →:笑點勒 幹你娘 阿阿阿阿阿阿阿阿阿阿阿阿
Boy-Girl →:你們年紀會差很多嗎,其實年齡也不是問題啦,只要肯用心經營
→:其實不太建議差太多歲的愛情啦,但事情也沒有說一定,原PO加油
法西斯 →:噁心死了,要問這種低俗的問題不會去西斯板問喔
西斯板 →:不正常,請砍掉重練
→:幹,圖哩
→:發文不附圖,此風不可長


[閒聊] 趁家人不在家 推砲~~

Joke →:笑點哩 幹你娘 趕閃我 噓死你
Boy-Girl →:你們真幸福 哈哈 要一直幸福下去喔
法西斯 →:記得一定要叫男朋友帶套喔
西斯板 →:你跟士官長都休假啦?
→:他老爸在門外打槍,他非常火
→:      還 蠻 屌 的
------

Jizz魂

--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 123.240.224.19

碎碎唸(7)

以往下了班就是隨便買點東西,開了Windows桌機就等WOW的登入畫面(我的WOW是放在Windows的啟動....羞)。
現在呢,下了班也是隨便買點東西,開了一台Ubuntu的桌機、一台Ubuntu的筆電跟一台Mac mini,然後就是LPIC跟Ruby交替看著,Mac mini就乖乖當著動物機跑。那這些有什麼好碎碎唸?

Ubuntu 9.04支援ext4,實在是頗快啊!而且常用到的Apache2, Tomcat6, Subversion 1.5.4,要設定的東西實在愈來愈少,東西做這麼好,我以後離不開怎麼辦,公司裡那台Red Hat Server實在想丟掉啊。

從開始用Ubuntu當Server,不再單純只當Coding的開發機,發現自己有很多的錯誤觀念,沒事自己在自己home folder建個java 目錄幹什麼,tomcat跟maven不用Ubuntu綁定的話就下載後丟到/opt下啊,沒事在.profile裡加PATH幹什麼,直接用ln -s加到/usr/bin下啊。Ubuntu要求一定要用sudo來做這些事其實很不錯,可以讓自己想清楚權限的事,Red Hat搞到最後幾乎都用root登入,實在很糟糕啊(是我糟糕不是Red Hat)。

其他要唸的像是:

Valen Hsu的新專輯有點給他失望,雖說不是第一次失望,但之前那首單曲“好聽“實在不錯啊,總覺得後面2張專輯詞曲都不是很優。真想聽Valen唱唱郭靜的“心牆“…倒底最近還有什麼新專輯可以聽的?

Springframework 3停在M3很久了,說好的RC呢?已經延了一個多月了耶,是不是要喝下每朝啊?不然M這麼久也該看醫生吧。

西斯不西斯很久了,快點開版啊!鄉民快不行了吧!

2台Ubuntu,1台Mac mini,那…我那台比前面3台加起來總價還貴的MBP呢?有了iPod Touch,我會沒事開它來聽音樂嗎(誤)?

最後
Elliot@iPhone coming soon!

22 6月 2009

碎碎唸(6)

離上一次登入WOW已經整整一個月了,雖然偶爾會連上Armory看看之前同團成員的資料,從成就裡還是可以瞭解些他們現在的情形,說不上是關心,也許只是淡淡的懷念吧,昨天發現有幾個人已經接近一個星期沒登入過,而奧杜亞成就也只停在威斯札,優格還沒倒,忽然有些難過....

玩WOW讓我現實世界中大部份的事物都停滯不前,唯獨體重緩慢增加,雖說現在的公司沒有健身房,家裡附近沒有合適的場所可以運動也是個因素,但我想我花了太多時間在WOW上面還是主因,從2007年3月到今年年初,大約增加了14、15公斤。年初的健檢的確讓我嚇一跳,77公斤對於一個身高只有168、169的人來說是太重了些...控制飲食是有些效果,減到68、69左右卻又停了下來,突然覺得目標60公斤是好遠好遠的距離...想找出問題還是需要些記錄吧,從App Store 下載了Health Cubby,準備開始仔細地記記,也有個工具可以提醒自己....

找Health Cubby時也想說順便找下記帳用的軟體,雖說我好像沒有透支的問題,上家公司薪水戶頭裡的錢買完高雄的房子都還有剩,依我花錢的方式來看,可能還能用個2年,只是想試試看記段時間,也許有什麼特別的發現;找了幾個軟體,覺得Balance太簡單,而其他又幾乎是要收費的,雖說USD $5、6不是很貴,但心理莫明有個聲音:暗、我也是寫軟體的....

13 6月 2009

碎碎唸(5)

最近常常突然就進入放空狀態...慘。

不過也不是完全沒收獲,發現了幾首放空時聽了卻完全不會干擾到放空狀態的歌。

青山黛瑪 - そばにいるね

 

梁靜茹-可惜不是你

 

剛剛在PTT看到一篇評魔羯座的,其中有一句

"如果他愛你,你就什麼都不用做,因為他都會幫你做;如果他不愛你.你也什麼都不用做,因為做了也沒用"

我笑了起來

繼續放空....

11 6月 2009

碎碎唸(4)

睡不著,拿起了村上春樹的"國境之南、太陽之西"複習,這本小說是我第一次接觸村上成為村上迷的開始。手上這本是1998年2月第16刷,也就是這本書大約在我身邊已經過了10年了,所以我記憶應該沒錯,是我在新化受預官訓時放假等車回家,頗為無聊時在書店翻起,在村上之前我所知道的日本作家,大概就是夏木漱石、大江健三郎、川端康成這些作家,對村上的印象頗淺;依我的習慣,通常不太熟的作家我都是先看略薄一點的著作,如果看完有興趣,就會將該作家的書全部掃完(這真的應該算病態吧...)。

那時挑這本書沒什麼原因,只是因為它比較薄,封面截錄的" '萬物都在那裡生長',你說,'然而真正存活的只有沙漠本身' ",其實引不起我的興趣....從台南回台北的路程很長,時間充足到可以讀這本書2遍,下了車,我沒有先回家,而是先到重慶南路再掃了幾本村上的書。

村上小說的主角,通常都是相當普通的人,就像是週遭就能見到的,而不是難以觸及的菁英份子或俊男美女,故事的內容會讓我產生莫名的認同感,似乎在說的不是別人而是自己的故事,就是這樣的認同感,讓我讀著一本又一本,一遍又一遍。

"我常常讀書、聽音樂。....一旦開始讀起來,中途都欲罷不能。那對我來說好像是麻藥一樣的東西。...只要一有時間就窩在房間裡聽爵士唱片。...但是幾乎沒有欲望把我那種讀書和聽音樂的體驗和別人談論。我就是我自己,不是其他任何人,這反而使我感到安逸、滿足。在這層意義上,我是一個極端孤獨而傲慢的少年。",這是描寫主角始在高中時的心境,怎麼看都覺得跟年輕的自己好像。

如果是島本的話,或者是泉的話,我就可以比較正確地表達的心情。....我想如果真的能這樣的,不知道該有多好。可是我並沒有做任何努力去實現這想法。結果她們只是已經從我的人生之中失去的存在。時鐘是不能逆轉的。”,“島本,最大的問題是我欠缺了什麼。我這樣一個人,我的人生,空空的缺少了什麼,失去了什麼,而那部份一直飢餓著,乾渴著。....這個世界上只有妳一個人能夠做到這個。跟妳在一起,我才感覺到那個部份滿足了。....我再也沒辦法回到那樣的世界去了。”。

其實不是沒有話要說的,只是自己的故事,沒有必要讓每一個人都知道,只要有那個人聽我說就好,除了那個人之外的其他人我都不在乎,這樣的想法,已經不知道在我心裡浮現多少次。如果那個人還在,我應該與現在有很大不同吧,每次拿起這本小說,總是抱著可以捨棄一切只求能再見她一面的想法,但畢竟是不可能的事了....

非常遺憾的是,某些事物是不能往後退的。那一旦往前走之後,不管怎麼努力,都回不去了。如果那時候有什麼絲毫差錯的話,就會以錯誤的樣子凝固下來。”

我在這裡,唯一能做的,就只是回憶而已。

08 6月 2009

碎碎唸(3)--說是心得太沉重

又是碎碎唸?因為說是心得太沉重...

白石一文在台有翻譯出版的小說應該都看完了吧?一瞬之光、我心中尚未崩壞的部分、愛有多少、永遠在身邊、心中鑲著龍。白石一文的小說果然口味比較重,沒一本看完開心的...

不能分割的生與死,中間的日子要如何渡過?將每一刻都當做最後一刻來過,致力將其成為最閃耀的極致時光,生氣蓬勃地活著走向死亡,在其他生命的幸福裡認可自己的生命?孕育一個生命就是將他推向死亡,人無法選擇自己的出生與否,甚至也無法選擇自己的死亡方式,這樣的生命,如果只是受著看不見的命運與不斷浮現的慾望控制而活動,又怎麼能明確定出"自我"的存在與意義?也許"一瞬之光"跟"我心中尚未崩壞的部分"要合在一起看。

眼裡所見的,終究只是事實其中一部份;"所謂的理解,通常只不過是誤解的總合"。愛有多少這本書裡提到的四個故事,都在說明"肉眼不可見的確定性",無論是岬與安西一開始的誤解,市川家與里見家複雜的親子關係、知佳與英一的外遇、正平與晶交往的問題,都受著當事人看不見的事物所影響,只是...當眼前所見都不能完全相信時,還能相信些什麼呢?

有沒有年過30淚腺比較不容易控制的八掛?為什麼我看完岬寫的"致廿年後的我"居然流淚到眼紅而看不下書?而岬所想的,尋找心愛的人與尋找可以成為妻子的人其中的區別,是其無法判斷能否發自內心愛對方的底線,我也只能如搗蒜般地點頭,"所見即所思",等等...我很確定我是男人...

至於心中鑲著龍,實在很難從書名想像內容,看到封面就更糟糕了...看完了的感想是完全屬於黑暗面的,人際關係陰沉面的回憶成為我腦海裡揮之不去的困擾,基於散播憂傷是不道德的行為,當我沒提過...

沒有生命的事物才能永遠在一起,就如同故事裡兩個主角小時候所立下刻著名字的石磚,人生的際遇終究無法預測,你所得到的並不一定是你所追求的,更多的是不請自來的,津田敦最後在醫院裡對青野精一郎發洩的他對於人生的憤怒與不甘願,的確說出了生命的無奈與悲哀,但是精一郎的回覆也有意思,"大家都很努力活著,這樣就足夠了",不需要對發生在別人身上的遭遇覺得有必要負起責任,每個人都有追求幸福的權利,但為了別人幸福而犠牲自己則大可不必。也許,簡單的說,不要把別人的人生扛在自己的肩上,只要大家都努力地活著,這樣,就好。(我也不確定我在寫什麼,也許該解構一下....),我難以完全體會表達的原因,也許是因為像是十多歲時聽李宗盛的凡人歌,少了點閱歷吧。

07 6月 2009

碎碎唸(2)

原諒我自己將以非常零碎而雜亂的方法記事,不然可能悶很久也沒什麼東西出來...

工作是簡單的:

  • 發現問題,分析目前可見已發生的現象,推測可能發生的原因,找出可能的解決方式,將其解構為執行的步驟,驗証問題是否消失,於是所有的可能都變成確定。
  • 提出構想,分析手上現有的資源,推演是否能完成,再加以分項執行,找合適的人,建立確認點,再想下一個構想。

之所以稱為簡單,是因為已經有可見的結果,所以該如何走,是否已經走到都可以被確認,也許路不一定好走,但總體上來說還是比較簡單的,至少是可以理解而不會困惑的。就像迷宮是簡單的,只要從出口倒回去走,就能在花費最短時間的情形下找到正確的路。

現實中,大部份的問題並沒有明確的答案。選擇會決定事情的結果,但當下並不能看到那個結果。可以有所期望,但是失望的機率永遠存在。這是因為我們沒辦法看得到所有的現象,所以完整無誤的執行步驟是難以被建立的。而即使我們找到可能的方式,也因為問題難以再次完整重現,而不能確定所做的方式是正確的。

正確性與絕對性,在大部份的事實中,並不存在。

我們所看到的"事實",通常只是"它"其中的一面,還有很多是我們看不到的。人也是一樣,你以為你明白某個人,其實只是知道其中一面,還有很多的"他"是你沒見過的,即使見過,也不一定能明白。

當有人對我說:"我明白你的感受"、"我瞭解你這個人",我可能會非常刻意地漏出嗤嗤的笑(鼻)聲,我常常都覺得不明白我自己了,為什麼有人可以用這種具有"絕對性"語意地方式來發表"正確性"的意見?

"可能、也許、大概是",我的對答裡通常使用了大量的不確定語氣,不是因為想逃避或是不誠懇,我儘量對我的言語負責,但我實在難以違背我對絕對性的理解。

如果我用了絕對性地語氣說話,可以不接受,但是請不要懷疑。

例如:

我 愛 你